# -*- coding: utf-8 -*-
from rest_framework import generics
from rest_framework.response import Response
from rest_framework.views import APIView
from .models import Product, Lesson, LessonViewed
from .serializers import LessonSerializer, ProductStatDetailSerializer
from django.contrib.auth.models import User
from . import models
from django.db.models import Sum

class UserLessonsListView(generics.ListAPIView):
    serializer_class = LessonSerializer

    def get_queryset(self):
        user_id = self.request.user.id
        return Lesson.objects.filter(products__owner=user_id)

class LessonByProductView(generics.ListAPIView):
    serializer_class = LessonSerializer

    def get_queryset(self):
        user_id = self.request.user.id
        product_id = self.kwargs['pk']
        print(f"user_id: {user_id}, product_id: {product_id}")
        queryset = Lesson.objects.filter(products__owner=user_id, products__id=product_id)
        print(f"Queryset: {queryset}")
        return queryset

    # def get_queryset(self):
    #     user_id = self.request.user.id
    #     product_id = self.kwargs['pk']
        # return Lesson.objects.filter(products__owner=user_id, products__id=product_id)
class ProductStatDetailView(APIView):
    def get(self, request):
        products = Product.objects.all()
        stat_data = []

        for product in products:
            total_lessons_viewed = LessonViewed.objects.filter(lesson__products=product).count()
            total_time_spent = LessonViewed.objects.filter(lesson__products=product).aggregate(total_time_spent=Sum('viewed_time_seconds'))['total_time_spent']
            total_students = LessonViewed.objects.filter(lesson__products=product).values('user').distinct().count()
            total_users = User.objects.all().count()
            acquisition_percentage = (total_students / total_users) * 100

            stat_data.append({
                'product': {
                    'id': product.id,
                    'name': product.name
                },
                'total_lessons_viewed': total_lessons_viewed,
                'total_time_spent': total_time_spent,
                'total_students': total_students,
                'acquisition_percentage': acquisition_percentage
            })

        serializer = ProductStatDetailSerializer(stat_data, many=True)
        return Response(serializer.data)


#%%

import numpy as np
import pandas as pd
import time, urllib, os.path

from astroquery.jplhorizons import Horizons
# #%%
# obj = Horizons(id='199', location='399',
# 			   epochs={'start':'2010-01-01', 'stop':'2010-03-01',
# 					   'step':'1d'})
# eph = obj.vectors()
# print(eph.columns)
# TODO: Define origin in a static point. (if that's possible)

exit()
def get_link(objid, start_time, stop_time, stepsize, center):
	link = "https://ssd.jpl.nasa.gov/horizons_batch.cgi?batch=1"
	link += "&COMMAND='"+ str(objid) +"'"
	link += "&MAKE_EPHEM='YES'"
	link += "&TABLE_TYPE='VECTORS'"
	link += "&START_TIME='"+ start_time +"'"
	link += "&STOP_TIME='"+ stop_time +"'"
	link += "&STEP_SIZE='"+ stepsize + "'"
	link += "&OUT_UNITS='KM-S'"
	link += "&REF_PLANE='ECLIPTIC'"
	link += "&REF_SYSTEM='J2000'"
	link += "&VEC_LABELS='YES'"
	link += "&VEC_DELTA_T='NO'"
	link += "&OBJ_DATA='YES'"
	link += "&VEC_TABLE='1'"
	link += "&VECT_CORR='NONE'"
	link += "&CSV_FORMAT='YES'"
	link += "&CENTER='"+ center + "'"
	return(link)

def get_data(link, savename, objid):
	# values = {'name':'Armand Comas', 'location':'Boston MA USA', 'language':'Python 3.9'}
	# headers = { 'User-Agent':"Atlas_of_Space_bot Armand Comas" }
	values = {'name':'Eleanor Lutz', 'location':'Seattle WA USA', 'language':'Python 3.7.1'}
	headers = { 'User-Agent':"Atlas_of_Space_bot Eleanor Lutz contact with issues at tabletopwhale@outlook.com" }
	data = urllib.parse.urlencode(values)

	# if (values.get("name") == "Eleanor Lutz") or ("Eleanor Lutz" in headers.get("User-Agent")):
	# 	raise ValueError("Please edit the scraper with your own contact information")

	if not os.path.isfile(savename):
		try:
			output = urllib.request.urlopen(link, data, headers)
			output = [x.decode('UTF-8') for x in output]
			output = [x.strip() for x in output]

			if '$$SOE' in output:
				header = output[output.index('$$SOE')-2].split(',')
				header = [x.lstrip() for x in header]

				content = output[output.index('$$SOE')+1 : output.index('$$EOE')-1]
				content = [x.split(',') for x in content]

				df = pd.DataFrame(content, columns=header)
				print(savename)
				input('Hi')
				df.to_csv(savename, index=False)
			else:
				print(objid, 'request successful but output not expected format')
				print(link)
		except:
			print(objid, 'request unsuccessful')
			print(link)

def query_horizons(readname, savename_head, stepsize='1d', center='@sun'):
	if not os.path.isfile(savename_head[:-1]+".zip"):
		print("---\nNow analyzing", readname)
		df = pd.read_csv(readname, low_memory=False)

		# Check for duplicated names before running script
		dupl = df.duplicated('horizons')
		print(sum(dupl), 'names in the series are duplicated, of', len(df), 'total')
		if sum(dupl) > 1:
			print(df[dupl == True])

		# Check to see if any names are NaN values
		print(df['horizons'].astype(str).replace(' ', '').isnull().sum(),
			  'null values in horizons query list')

		for index, row in df.iterrows():
			if (index % 500 == 0) and (index != 0):
				print(index, 'items analyzed!')
			start_time = str(row['begin_time']).replace(' ', '')
			stop_time = str(row['end_time']).replace(' ', '')
			objid = str(row['horizons']).replace(' ', '')
			savename = savename_head + objid + '.csv'
			if not os.path.isfile(savename):
				link = get_link(objid, start_time, stop_time, stepsize, center)
				get_data(link, savename, objid)

				# sleep to be polite to HORIZONS servers
				sleeptime = min(20, max(1, np.random.normal(loc=10, scale=5)))
				time.sleep(sleeptime)

				if index == 0:
					# Save parameters of 1st item to txt file for later checking
					txtname = savename_head + 'PARAMETERS.txt'
					with open(txtname, "w") as f:
						f.write(link)
	else:
		raise ValueError("Please unzip the data files that already exist")
	print('ALL ITEMS ANALYZED!')

#%%

'''
Get orbital coordinates from HORIZONS server
'''
center = '@earth'
stepsize = '1d'

# readname = './data/moons.csv'
# savename_head = './data/moons/'
# query_horizons(readname, savename_head, stepsize=stepsize, center=center)

readname = './data/planets.csv'
savename_head = './data/planets/'
query_horizons(readname, savename_head, stepsize=stepsize, center=center)

# readname = './data/large_asteroids.csv'
# savename_head = './data/large_asteroids/'
# query_horizons(readname, savename_head, stepsize=stepsize, center=center)
#
# readname = './data/large_comets.csv'
# savename_head = './data/large_comets/'
# query_horizons(readname, savename_head, stepsize=stepsize, center=center)
#
# readname = './data/small_asteroids.csv'
# savename_head = './data/small_asteroids/'
# query_horizons(readname, savename_head, stepsize=stepsize, center=center)
#
# readname = './data/any_outer_asteroids.csv'
# savename_head = './data/any_outer_asteroids/'
# query_horizons(readname, savename_head, stepsize=stepsize, center=center)
#
# readname = './data/any_inner_asteroids.csv'
# savename_head = './data/any_inner_asteroids/'
# query_horizons(readname, savename_head, stepsize=stepsize, center=center)

#%%


import re
import sys

from typing import Generator, List, Optional, Tuple

from bento_variant_service.beacon.datasets import BeaconDataset
from bento_variant_service.constants import SERVICE_NAME
from bento_variant_service.tables.base import VariantTable
from bento_variant_service.tables.vcf.file import VCFFile
from bento_variant_service.variants.models import Allele, Variant, Call

MAX_SIGNED_INT_32 = 2 ** 31 - 1

REGEX_GENOTYPE_SPLIT = re.compile(r"[|/]")
VCF_GENOTYPE = "GT"
VCF_READ_DEPTH = "DP"
VCF_PHASE_SET = "PS"

class VCFVariantTable(VariantTable):
    def __init__(
        self,
        table_id: str,
        name: Optional[str],
        metadata: dict,
        files: Tuple[VCFFile, ...] = (),
    ):
        self._files: Tuple[VCFFile, ...] = ()
        super().__init__(table_id, name, metadata)
        self.update_with_files(name, metadata, files)

    def update_with_files(self, name: Optional[str], metadata: dict, files: Tuple[VCFFile]):
        # Check passed files for well-formattedness, skip otherwise
        good_files: List[VCFFile] = []
        for file in files:
            fg = next(file.fetch(), ())
            if len(fg) >= 9:  # Need 9th column of VCF to deal with genotypes, samples, etc.
                good_files.append(file)

        self.update(name, metadata, tuple(vf.assembly_id for vf in good_files))
        self._files: Tuple[VCFFile] = tuple(good_files)

    @property
    def beacon_datasets(self):
        return tuple(
            BeaconDataset(
                table_id=self.table_id,
                table_name=self.name,
                table_metadata=self.metadata,
                assembly_id=a,
                files=tuple(vf for vf in self._files if vf.assembly_id == a)
            ) for a in sorted(self._assembly_ids)
        )

    @staticmethod
    def _int_or_none_from_vcf(val):
        return None if val == "." else int(val)

    @staticmethod
    def _int_or_missing_from_vcf(val):
        return val if val in (".", "*") else int(val)

    @staticmethod
    def _variant_calls(variant: Variant, sample_ids: tuple, row: tuple, only_interesting: bool = False):
        for sample_id, row_data in zip(sample_ids, row[9:]):
            row_info = {k: v for k, v in zip(row[8].split(":"), row_data.split(":"))}

            if VCF_GENOTYPE not in row_info:
                # Only include samples which have genotypes
                continue

            gt = row_info[VCF_GENOTYPE]
            call = Call(
                variant=variant,
                genotype=tuple(map(VCFVariantTable._int_or_missing_from_vcf, re.split(REGEX_GENOTYPE_SPLIT, gt))),
                phased="/" in gt,
                phase_set=VCFVariantTable._int_or_none_from_vcf(row_info.get(VCF_PHASE_SET, ".")),
                sample_id=sample_id,
                read_depth=VCFVariantTable._int_or_none_from_vcf(row_info.get(VCF_READ_DEPTH, ".")),
            )

            if only_interesting and not call.is_interesting:
                # Uninteresting, not present on sample
                continue

            yield call

    @property
    def files(self) -> Tuple[VCFFile]:
        return self._files

    @property
    def n_of_variants(self) -> int:
        return sum(vf.n_of_variants for vf in self._files)

    @property
    def n_of_samples(self) -> int:
        sample_set = set()
        for vcf in self._files:
            sample_set.update(vcf.sample_ids)
        return len(sample_set)

    def _variants(
        self,
        assembly_id: Optional[str] = None,
        chromosome: Optional[str] = None,
        start_min: Optional[int] = None,
        start_max: Optional[int] = None,
        offset: Optional[int] = None,
        count: Optional[int] = None,
        only_interesting: bool = False,
    ) -> Generator[Variant, None, None]:
        # If offset isn't specified, set it to 0 (the very start)
        offset: int = 0 if offset is None else offset

        variants_passed = 0
        variants_seen = 0

        # TODO: Optimize offset/count
        #  e.g. by skipping entire VCFs if we know their row counts a-priori

        for vcf in filter(lambda vf: assembly_id is None or vf.assembly_id == assembly_id, self._files):
            if (
                chromosome is None and  # No filters (otherwise we wouldn't be able to assume we're skipping the VCF)
                start_min is None and  # "
                start_max is None and  # "
                not only_interesting and  # "
                vcf.n_of_variants <= offset - variants_seen
            ):
                # If the entire file is covered by the remaining offset, skip it. This saves time crawling through an
                # entire VCF if we cannot use any of them.
                print(f"[{SERVICE_NAME}] [DEBUG] Skipping VCF due to offset: {vcf}", flush=True)
                variants_seen += vcf.n_of_variants
                continue

            try:
                # TODO: Security of passing this? Verify values in non-Beacon searches
                # TODO: What if the VCF includes telomeres (off the end)?]

                # TODO: pysam uses 0-based indexing, double-check

                query = ()
                if chromosome is not None:
                    query = (
                        chromosome,
                        start_min - 1 if start_min is not None else 0,
                        start_max - 1 if start_max is not None else MAX_SIGNED_INT_32,
                    )

                for row in vcf.fetch(*query):
                    variants_passed += 1

                    if variants_passed <= offset:
                        continue

                    if count is not None and variants_seen >= count:
                        return

                    if chromosome is None:
                        # Didn't index in, so check start_min / start_max by hand
                        if start_min is not None and int(row[1]) < start_min:
                            continue
                        elif start_max is not None and int(row[1]) >= start_max:
                            continue

                    alt_alleles = tuple(Allele(Allele.class_from_vcf(a), a) for a in row[4].split(","))
                    variant = Variant(
                        assembly_id=vcf.assembly_id,
                        chromosome=row[0],
                        start_pos=int(row[1]),
                        ref_bases=row[3],
                        alt_alleles=alt_alleles,
                        qual=float(row[5]) if row[5] != "." else None,
                        file_uri=vcf.original_index_uri,
                    )

                    variant.calls = tuple(VCFVariantTable._variant_calls(variant, vcf.sample_ids, row,
                                                                         only_interesting=only_interesting))

                    if only_interesting and len(variant.calls) == 0:
                        # Uninteresting; no calls of note on the variant
                        continue

                    yield variant

                    variants_seen += 1

            except ValueError as e:
                # Sometimes this can occur if a region not found in Tabix file, so continue searching but log it
                print(f"[{SERVICE_NAME}] [ERROR] Encountered ValueError: {e}", file=sys.stderr, flush=True)
                print(f"[{SERVICE_NAME}] [ERROR]     In VCF: {repr(vcf)}", file=sys.stderr, flush=True)
                continue


def dbus_to_string(dbus_bytes):
    string = ''
    for byte in dbus_bytes:
        raw = hex(byte)
        # print("raw: |{}|".format(raw))
        raw = raw.replace("\\x", "")
        raw += " "
        string += raw
    return string

def int_to_byte(byte, int1, int2):
    arr = [byte, (int1 >> 24 & 0xFF), (int1 >> 16 & 0xFF), (int1 >> 8 & 0xFF), (int1 & 0xFF), (int2 >> 24 & 0xFF),
           (int2 >> 16 & 0xFF), (int2 >> 8 & 0xFF), (int2 & 0xFF)]
    arr.append(checksum(arr))
    byte_arr = []
    # for val in arr:
    #    byte_arr.append(byte(val))
    return arr

def checksum(arr):
    sum = 0
    for i in range(0, 9):
        sum += arr[i]
    sum &= 0xFF
    return sum

def empty_params(size=10):
    params = []
    for i in range(0, size):
        params.append(0)
    return params


import lit.formats
import shutil
import os

config.name = "Test suite"
config.test_format = lit.formats.ShTest(True)

config.suffixes = ['.cs', '.test', '.il']

config.test_source_root = os.path.dirname(__file__)
config.test_build_root = os.path.join(config.my_obj_root, 'integration-test')

config.substitutions.append(('%mono', config.mono_bin))
config.substitutions.append(('%mcs', config.mcs_bin))
config.substitutions.append(('%ilasm', config.ilasm_bin))
config.substitutions.append(('%FileCheck', os.path.join(config.llvm_bin_dir, "FileCheck")))

# This is required for testing the nuget package, which is generated in the CI.
# If the directory is not found, fallback to the output of "dotnet build"
nuget_tool_installation = os.path.join(config.sharp_src_dir, "local-installation/sharp-cli")
if os.path.isfile(nuget_tool_installation):
    sharp_cli_path = nuget_tool_installation
else:
    sharp_cli_path = os.path.join(config.sharp_bin_dir, "sharp-cli")

config.substitutions.append(('%sharp-cli', sharp_cli_path))

# This is useful if a custom dotnet installation is used.
if os.environ.get('DOTNET_ROOT') is not None:
    config.environment['DOTNET_ROOT'] = os.environ.get('DOTNET_ROOT')

def _clean_test_directory(directory):
    for entry in os.scandir(directory):
        basename = os.path.basename(entry.path)
        if basename == "lit.site.cfg.py":
            continue
        if entry.is_dir():
            shutil.rmtree(entry.path)
        else:
            os.remove(entry.path)

_clean_test_directory(config.test_build_root)


from nltk.corpus import stopwords
from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras import Sequential, Input
from tensorflow.keras.layers import Dense, Dropout

from string import punctuation
from os import listdir
from collections import Counter
from numpy import array, half

def load_doc(filename):
    f = open(filename, 'r')
    doc  = f.read()
    f.close()
    return doc

def clean_doc(doc):
    tokens = doc.split()
    table = str.maketrans('', '', punctuation)
    tokens = [w.translate(table) for w in tokens]
    tokens = [w for w in tokens if w.isalpha()]
    stop_words = set(stopwords.words('english'))
    tokens = [w for w in tokens if not w in stop_words]
    tokens = [w for w in tokens if len(w) > 1]
    return tokens

def add_doc_to_vocab(filename, vocab):
    doc = load_doc(filename)
    tokens = clean_doc(doc)
    vocab.update(tokens)
    return ''

def process_docs(directory, vocab, callback, is_train=True):
    lines = list()
    for filename in listdir(directory):
        if is_train and filename.startswith('cv9'):
            continue
        if not is_train and not filename.startswith('cv9'):
            continue
        path = directory + '/' + filename
        line = callback(path, vocab)
        lines.append(line)
    return lines

def make_tokens(vocab, min_occur):
    return [token for token, count in vocab.items() if count >= min_occur]

def save_list(lines, filename):
    data = '\n'.join(lines)
    file = open(filename, 'w')
    file.write(data)
    file.close()

def make_doc_to_line(filename, vocab):
    doc = load_doc(filename)
    tokens = clean_doc(doc)
    tokens = [w for w in tokens if w in vocab]
    return ' '.join(tokens)

def make_vocab_file(data_dir, pos, neg, is_train):
    vocab = Counter()
    _ = process_docs(data_dir+pos, vocab, add_doc_to_vocab, is_train)
    _ = process_docs(data_dir+neg, vocab, add_doc_to_vocab, is_train)
    print(f'vocabulary length: {len(vocab)}')
    # print(vocab.most_common(50))

    min_occur = 2
    tokens = make_tokens(vocab, min_occur)
    print(f'token length: {len(tokens)}')

    save_list(tokens, 'vocab.txt')

def load_vocab_file(filename, data_dir, pos, neg, is_train):
    vocab = load_doc(filename)
    vocab = vocab.split()
    vocab = set(vocab)

    pos_lines = process_docs(data_dir+pos, vocab, make_doc_to_line, is_train)
    neg_lines = process_docs(data_dir+neg, vocab, make_doc_to_line, is_train)

    print(f'positive lines length: {len(pos_lines)}')
    print(f'negative lines length: {len(neg_lines)}')

    return neg_lines + pos_lines

def make_vector(tokenizer, docs):
    return tokenizer.texts_to_matrix(docs, mode='freq')

def make_y_vector(size):
    half_size = int(size/2)
    return array([0 for _ in range(half_size)] + [1 for _ in range(half_size)])

def make_dataset(filename, data_dir, pos, neg, train_size, test_size):
    docs_train = load_vocab_file(filename, data_dir, pos, neg, is_train=True)
    docs_test = load_vocab_file(filename,data_dir, pos, neg, is_train=False)

    tokenizer = Tokenizer()
    tokenizer.fit_on_texts(docs_train)

    X_train = make_vector(tokenizer, docs_train)
    y_train = make_y_vector(train_size)

    X_test = make_vector(tokenizer, docs_test)
    y_test = make_y_vector(test_size)

    return (X_train, y_train), (X_test, y_test)

def make_model(n_words):
    model = Sequential()
    model.add(Input(shape=(n_words,)))
    model.add(Dense(50, activation='relu'))
    model.add(Dense(1, activation='sigmoid'))
    model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
    return model


import torch
from .engine_utils import get_init_pose_train, get_normed_kps, get_init_scale_train
from .engine_utils import aug_3d_bbox, aug_RT
from .batch_test import batch_data_test, batch_updater_test
from lib.vis_utils.image import grid_show, heatmap
from lib.pysixd.misc import transform_pts_batch, transform_normed_pts_batch

def batch_data(cfg, data, device="cuda", phase="train", dtype=torch.float32):
    if phase == "test":
        return batch_data_test(cfg, data, device=device, dtype=dtype)

    input_cfg = cfg.INPUT
    # batch training data and flatten
    tensor_kwargs = {"dtype": dtype, "device": device}
    to_float_args = {"dtype": dtype, "device": device, "non_blocking": True}
    to_long_args = {"dtype": torch.long, "device": device, "non_blocking": True}

    batch = {}
    num_imgs = len(data)

    # construct by flattening instance data =============================
    batch["pcl"] = torch.cat([d["instances"].pcl for d in data], dim=0).to(**to_float_args)

    batch["obj_cls"] = torch.cat([d["instances"].obj_classes for d in data], dim=0).to(**to_long_args)
    batch["obj_pose"] = torch.cat([d["instances"].obj_poses.tensor for d in data], dim=0).to(**to_float_args)
    batch["obj_scale"] = torch.cat([d["instances"].obj_scales for d in data], dim=0).to(**to_float_args)

    # batch["obj_visib_mask"] = torch.cat([d["instances"].obj_visib_masks.tensor for d in data], dim=0).to(
    #     **to_float_args
    # )
    # batch["obj_trunc_mask"] = torch.cat([d["instances"].obj_trunc_masks.tensor for d in data], dim=0).to(
    #     **to_float_args
    # )

    batch["obj_mean_points"] = torch.cat([d["instances"].obj_mean_points for d in data], dim=0).to(**to_float_args)
    batch["obj_mean_scales"] = torch.cat([d["instances"].obj_mean_scales for d in data], dim=0).to(**to_float_args)

    if "last_frame" in cfg.INPUT.INIT_POSE_TYPE_TRAIN:
        batch["last_frame_poses"] = torch.cat([d["instances"].last_frame_poses for d in data], dim=0).to(
            **to_float_args
        )

    if cfg.INPUT.KPS_TYPE.lower() == "fps":
        batch["obj_fps_points"] = torch.cat([d["instances"].obj_fps_points for d in data], dim=0).to(**to_float_args)

    num_insts_per_im = [len(d["instances"]) for d in data]
    num_insts_all = len(batch["obj_cls"])
    K_list = []
    sym_infos_list = []
    im_ids = []
    inst_ids = []  # the idx in the current img, should be used with im_ids
    for i_im in range(num_imgs):
        sym_infos_list.extend(data[i_im]["instances"].obj_sym_infos)
        for i_inst in range(num_insts_per_im[i_im]):
            im_ids.append(i_im)
            inst_ids.append(i_inst)
            K_list.append(data[i_im]["cam"].clone())

    batch["im_id"] = torch.tensor(im_ids, **tensor_kwargs)
    batch["inst_id"] = torch.tensor(inst_ids, **tensor_kwargs)
    batch["K"] = torch.stack(K_list, dim=0).to(**to_float_args)
    batch["sym_info"] = sym_infos_list

    # keep max_objs ----------------------------
    n_obj = min(cfg.DATALOADER.MAX_OBJS_TRAIN, num_insts_all)
    for _k in batch:
        if len(batch[_k]) == num_insts_all:
            batch[_k] = batch[_k][:n_obj]

    if input_cfg.WITH_IMG:
        batch["img"] = torch.stack([d["image"] for d in data]).to(**to_float_args)

    if input_cfg.WITH_DEPTH:
        batch["depth_obs"] = torch.stack([d["depth"] for d in data], dim=0).to(**to_float_args)

    # do some augmentation here --------------------------------------------
    if torch.rand(1) < input_cfg.BBOX3D_AUG_PROB:
        aug_3d_bbox(batch)

    if torch.rand(1) < input_cfg.RT_AUG_PROB:
        aug_RT(batch)

    return batch

def batch_updater(
    cfg,
    batch,
    cur_iter=1,
    poses_est=None,
    scales_est=None,
    device="cuda",
    dtype=torch.float32,
    phase="train",
):
    if phase == "test":
        return batch_updater_test(
            cfg,
            batch,
            poses_est=poses_est,
            scales_est=scales_est,
            device=device,
            dtype=dtype,
        )
    # batch updater for train =========================================
    tensor_kwargs = {"dtype": dtype, "device": device}
    to_float_args = {"dtype": dtype, "device": device, "non_blocking": True}

    n_obj = batch["obj_pose"].shape[0]
    if poses_est is None:
        # init pose --------------------------------------------------------------------------
        get_init_pose_train(cfg, batch, **tensor_kwargs)  # obj_pose_est
    else:
        batch["obj_pose_est"] = poses_est

    if scales_est is None:
        if cfg.MODEL.REFINE_SCLAE:
            get_init_scale_train(cfg, batch, **tensor_kwargs)  # obj_scale_est
        else:
            batch["obj_scale_est"] = batch["obj_scale"].detach().clone()
    else:
        batch["obj_scale_est"] = scales_est

    if "obj_kps" not in batch:
        get_normed_kps(cfg, batch, **to_float_args)  # obj_kps

    r_est = batch["obj_pose_est"][:, :3, :3]
    t_est = batch["obj_pose_est"][:, :3, 3:4]
    s_est = batch["obj_scale_est"]

    tfd_kps = transform_normed_pts_batch(
        batch["obj_kps"],
        r_est,
        t=None if cfg.INPUT.ZERO_CENTER_INPUT else t_est,
        scale=s_est,
    )

    batch["tfd_kps"] = tfd_kps.permute(0, 2, 1)  # [bs, 3, num_k]

    if cfg.INPUT.ZERO_CENTER_INPUT:
        batch["x"] = batch["pcl"].permute(0, 2, 1) - t_est.view(n_obj, 3, 1)  #  [bs, 3, num_k] - [bs, 3, 1]
    else:
        batch["x"] = batch["pcl"].permute(0, 2, 1)

    # done batch update train------------------------------------------


# encoding:utf-8

# 测试 pywin32 库的一些功能
# 使用 pywin32 刷 KF2 等级

import time
import win32gui, win32api, win32con

# KF2 客户端窗口的类名和标题
class_name = 'LaunchUnrealUWindowsClient'
title_name = 'Killing Floor 2 (64-bit, DX11) v1075'

hwnd = win32gui.FindWindow(class_name, title_name)
if hwnd == 0:
    print('KF2 Window not found.')
    exit(1)
print('KF2 hwnd:', hwnd)

left, top, right, bottom = win32gui.GetWindowRect(hwnd)
print('KF2 rect:', left, top, right, bottom)

win32gui.SetForegroundWindow(hwnd)
time.sleep(2)

count = 1
while True:
    curr_hwnd = win32gui.GetForegroundWindow()
    if  curr_hwnd == hwnd:
        win32api.keybd_event(71, 0, 0, 0)
        win32api.keybd_event(71, 0, win32con.KEYEVENTF_KEYUP, 0)
        time.sleep(0.3)

        if count == 6:
            win32api.keybd_event(65, 0, 0, 0)
            time.sleep(0.2)
            win32api.keybd_event(65, 0, win32con.KEYEVENTF_KEYUP, 0)
            time.sleep(0.1)

            win32api.keybd_event(68, 0, 0, 0)
            time.sleep(0.22)
            win32api.keybd_event(68, 0, win32con.KEYEVENTF_KEYUP, 0)

            count = 0

        count += 1
    else:
        time.sleep(2)


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# (c) B.Kerler 2019-2023 under GPLv3 license
# If you use my code, make sure you refer to my name
#
# !!!!! If you use this code in commercial products, your product is automatically
# GPLv3 and has to be open sourced under GPLv3 as well. !!!!!
import copy
import sys
import argparse
import time
import serial.tools.list_ports
from Exscript.protocols.telnetlib import Telnet
from binascii import hexlify, unhexlify
import logging
import logging.config
import logging.handlers
import colorama

class ColorFormatter(logging.Formatter):
    LOG_COLORS = {
        logging.ERROR: colorama.Fore.RED,
        logging.DEBUG: colorama.Fore.LIGHTMAGENTA_EX,
        logging.WARNING: colorama.Fore.YELLOW,
    }

    def format(self, record, *args, **kwargs):
        # if the corresponding logger has children, they may receive modified
        # record, so we want to keep it intact
        new_record = copy.copy(record)
        if new_record.levelno in self.LOG_COLORS:
            pad = ""
            if new_record.name != "root":
                print(new_record.name)
                pad = "[LIB]: "
            # we want levelname to be in different color, so let's modify it
            new_record.msg = "{pad}{color_begin}{msg}{color_end}".format(
                pad=pad,
                msg=new_record.msg,
                color_begin=self.LOG_COLORS[new_record.levelno],
                color_end=colorama.Style.RESET_ALL,
            )
        # now we can let standart formatting take care of the rest
        return super(ColorFormatter, self).format(new_record, *args, **kwargs)

class LogBase(type):
    debuglevel = logging.root.level

    def __init__(cls, *args):
        super().__init__(*args)
        logger_attribute_name = '_' + cls.__name__ + '__logger'
        logger_debuglevel_name = '_' + cls.__name__ + '__debuglevel'
        logger_name = '.'.join([c.__name__ for c in cls.mro()[-2::-1]])
        LOG_CONFIG = {
            "version": 1,
            "disable_existing_loggers": False,
            "formatters": {
                "root": {
                    "()": ColorFormatter,
                    "format": "%(name)s - %(message)s",
                }
            },
            "handlers": {
                "root": {
                    # "level": cls.__logger.level,
                    "formatter": "root",
                    "class": "logging.StreamHandler",
                    "stream": "ext://sys.stdout",
                }
            },
            "loggers": {
                "": {
                    "handlers": ["root"],
                    # "level": cls.debuglevel,
                    "propagate": False
                }
            },
        }
        logging.config.dictConfig(LOG_CONFIG)
        logger = logging.getLogger(logger_name)

        setattr(cls, logger_attribute_name, logger)
        setattr(cls, logger_debuglevel_name, cls.debuglevel)

'''
C7 = 7 0		0	2	7		5 0
C6 = 3 1		7	0	3		0 1
C5 = 0 2		5	3	0		3 2
C8 = 1 3		3	1	5		7 3
C4 = 5 4		1	4	1		1 4
'''
prodtable = {
    "MDM8200": dict(openlock=0, openmep=1, opencnd=0, clen=8, init=[1, 3, 5, 7, 0],
                    run="resultbuffer[i]=self.SierraAlgo(challenge[i], 2, 4, 1, 3, 0, 3, 4, 0)"),
    # MC878XC_F1.2.3.15 verified, key may be stored in nvitem 0x4e21;MC8700 M3.0.9.0 verified
    "MDM9200": dict(openlock=0, openmep=1, opencnd=0, clen=8, init=[7, 3, 0, 1, 5],
                    run="resultbuffer[i]=self.SierraAlgo(challenge[i], 4, 2, 1, 0, 3, 2, 0, 0)"),
    # AC881U, EM8805 SWI9X15C_05.05.58.00, at!openlock?6A1F1CEA298A14B0 => AT!OPENLOCK="3A9EA70D86FEE58C"
    "MDM9200_V1": dict(openlock=2, openmep=1, opencnd=0, clen=8, init=[7, 3, 0, 1, 5],
                       run="resultbuffer[i]=self.SierraAlgo(challenge[i], 4, 2, 1, 0, 3, 2, 0, 0)"),  # AC710
    "MDM9200_V2": dict(openlock=3, openmep=1, opencnd=0, clen=8, init=[7, 3, 0, 1, 5],
                       run="resultbuffer[i]=self.SierraAlgo(challenge[i], 4, 2, 1, 0, 3, 2, 0, 0)"),  # AC775
    "MDM9200_V3": dict(openlock=8, openmep=1, opencnd=8, clen=8, init=[7, 3, 0, 1, 5],
                       run="resultbuffer[i]=self.SierraAlgo(challenge[i], 4, 2, 1, 0, 3, 2, 0, 0)"),  # AC775
    "MDM9x15": dict(openlock=0, openmep=1, opencnd=0, clen=8, init=[7, 3, 0, 1, 5],
                    run="resultbuffer[i]=self.SierraAlgo(challenge[i], 4, 2, 1, 0, 3, 2, 0, 0)"),
    # 9x15C 06.03.32.02, AC340U 1.13.12.14 verified, #AT!CUSTOM=\"ADBENABLE\",1
    "MDM9x07": dict(openlock=9, openmep=10, opencnd=9, clen=8, init=[7, 3, 0, 1, 5],
                    run="resultbuffer[i]=self.SierraAlgo(challenge[i], 4, 2, 1, 0, 3, 2, 0, 0)"),
    # SWI9X07Y_02.25.02.01
    "MDM9x30": dict(openlock=5, openmep=4, opencnd=5, clen=8, init=[7, 3, 0, 1, 5],
                    run="resultbuffer[i]=self.SierraAlgo(challenge[i], 4, 2, 1, 0, 3, 2, 0, 0)"),
    # MC7455_2.30.01.01 #4
    "MDM9x30_V1": dict(openlock=17, openmep=15, opencnd=17, clen=8, init=[7, 3, 0, 1, 5],
                       run="resultbuffer[i]=self.SierraAlgo(challenge[i], 4, 2, 1, 0, 3, 2, 0, 0)"),
    # AC791L/AC790S NTG9X35C_02.08.29.00
    "MDM9x40": dict(openlock=11, openmep=12, opencnd=11, clen=8, init=[7, 3, 0, 1, 5],
                    run="resultbuffer[i]=self.SierraAlgo(challenge[i], 4, 2, 1, 0, 3, 2, 0, 0)"),  # AC815s
    "MDM9x50": dict(openlock=7, openmep=6, opencnd=7, clen=8, init=[7, 3, 0, 1, 5],
                    run="resultbuffer[i]=self.SierraAlgo(challenge[i], 4, 2, 1, 0, 3, 2, 0, 0)"),  # EM7565
    "MDM9x06": dict(openlock=20, openmep=19, opencnd=20, clen=8, init=[7, 3, 0, 1, 5],
                    run="resultbuffer[i]=self.SierraAlgo(challenge[i], 4, 2, 1, 0, 3, 2, 0, 0)"),  # WP77xx
    "SDX55": dict(openlock=22, openmep=21, opencnd=22, clen=8, init=[7, 3, 0, 1, 5],  # MR5100, MR6400 old fw
                  run="resultbuffer[i]=self.SierraAlgo(challenge[i], 4, 2, 1, 0, 3, 2, 0, 0)"),
    "MDM9x15A": dict(openlock=24, openmep=23, opencnd=24, clen=8, init=[7, 3, 0, 1, 5],  # AC779S
                     run="resultbuffer[i]=self.SierraAlgo(challenge[i], 4, 2, 1, 0, 3, 2, 0, 0)"),
    "SDX65": dict(openlock=25, openmep=21, opencnd=26, clen=8, init=[7, 3, 0, 1, 5],  # MR6400 new fw
                  run="resultbuffer[i]=self.SierraAlgo(challenge[i], 4, 2, 1, 0, 3, 2, 0, 0)")
}

infotable = {
    "MDM8200": ["M81A", "M81B", "AC880", "AC881", "MC8780", "MC8781", "AC880E", "AC881E", "EM8780", "EM8781",
                "MC8780V", "MC8781V", "MC8700", "AC308U"],
    "MDM9200": ["AC710", "MC8775", "MC8775V", "AC875", "MC8700", "AC313U", "MC8801", "MC7700", "MC7750", "MC7710",
                "EM7700", "770S", "781S"],
    "MDM9200_V1": ["AC710", "MC8775", "MC8775V", "AC875", "MC8700", "AC313U", "MC8801", "MC7700", "MC7750",
                   "MC7710", "EM7700"],
    "MDM9200_V2": ["AC775", "PC7200"],
    "MDM9200_V3": ["AC775"],
    "MDM9x07": ["SWI9X07Y", "WP76xx"],
    "MDM9x06": ["SWI9X06Y", "WP77xx"],
    "MDM9x15": ["SWI9X15C", "AR7550", "AR7552", "AR7554", "EM7355", "EM7655", "MC7354", "WP7100", "WP7102", "WP7104",
                "MC7305", "EM7305", "MC8805", "EM8805", "MC7350", "MC7350-L", "MC7802", "MC7304", "AR7556", "AR7558",
                "WP75xx", "WP85xx", "WP8548", "WP8548G", "AC340U"],
    "MDM9x15A": ["AC779S"],
    "MDM9x30": ["EM7455", "MC7455", "EM7430", "MC7430"],
    "MDM9x30_V1": ["Netgear AC790/MDM9230"],
    "MDM9x40": ["MR1100", "AC815s", "AC785s", "AC797S"],
    "MDM9x50": ["EM7565", "EM7565-9", "EM7511", "EM7411"],
    "SDX55": ["MR5100", "MR5200", "ac797-100eus", "MR6400"],
    "SDX65": ["MR6400", "MR6500", "MR6110", "MR6150", "MR6450", "MR6550"]
}

                      # 0 MC8775_H2.0.8.19 !OPENLOCK, !OPENCND .. MC8765V,MC8765,MC8755V,MC8775,MC8775V,MC8775,AC850,
                      #   AC860,AC875,AC881,AC881U,AC875, AC340U 1.13.12.14
keytable = bytearray([0xF0, 0x14, 0x55, 0x0D, 0x5E, 0xDA, 0x92, 0xB3, 0xA7, 0x6C, 0xCE, 0x84, 0x90, 0xBC, 0x7F, 0xED,
                      # 1 MC8775_H2.0.8.19 AC340U, OPENMEP default
                      0x61, 0x94, 0xCE, 0xA7, 0xB0, 0xEA, 0x4F, 0x0A, 0x73, 0xC5, 0xC3, 0xA6, 0x5E, 0xEC, 0x1C, 0xE2,
                      # 2 AC750,AC710,AC7XX,SB750A,SB750,PC7000,AC313u OPENMEP
                      0x39, 0xC6, 0x7B, 0x04, 0xCA, 0x50, 0x82, 0x1F, 0x19, 0x63, 0x36, 0xDE, 0x81, 0x49, 0xF0, 0xD7,
                      # 3 AC775,PC7200
                      0xDE, 0xA5, 0xAD, 0x2E, 0xBE, 0xE1, 0xC9, 0xEF, 0xCA, 0xF9, 0xFE, 0x1F, 0x17, 0xFE, 0xED, 0x3B,
                      # 4 MC7455_02.30.01.01 OPENMEP
                      0xFE, 0xD4, 0x40, 0x52, 0x2D, 0x4B, 0x12, 0x5C, 0xE7, 0x0D, 0xF8, 0x79, 0xF8, 0xC0, 0xDD, 0x37,
                      # 5 MC7455_02.30.01.01 OPENLOCK
                      0x3B, 0x18, 0x99, 0x6B, 0x57, 0x24, 0x0A, 0xD8, 0x94, 0x6F, 0x8E, 0xD9, 0x90, 0xBC, 0x67, 0x56,
                      # 6 SWI9x50 Openmep Key SWI9X50C_01.08.04.00
                      0x47, 0x4F, 0x4F, 0x44, 0x4A, 0x4F, 0x42, 0x44, 0x45, 0x43, 0x4F, 0x44, 0x49, 0x4E, 0x47, 0x2E,
                      # 7 SWI9x50 Openlock Key SWI9X50C_01.08.04.00
                      0x4F, 0x4D, 0x41, 0x52, 0x20, 0x44, 0x49, 0x44, 0x20, 0x54, 0x48, 0x49, 0x53, 0x2E, 0x2E, 0x2E,
                      # 8 MDM8200 Special
                      0x8F, 0xA5, 0x85, 0x05, 0x5E, 0xCF, 0x44, 0xA0, 0x98, 0x8B, 0x09, 0xE8, 0xBB, 0xC6, 0xF7, 0x65,
                      # 9 SWI9x07 Openlock Key 02.25.02.01
                      0x4D, 0x42, 0xD8, 0xC1, 0x25, 0x44, 0xD8, 0xA0, 0x1D, 0x80, 0xC4, 0x52, 0x8E, 0xEC, 0x8B, 0xE3,
                      # 10 SWI9x07 Openmep Key 02.25.02.01
                      0xED, 0xA9, 0xB7, 0x0A, 0xDB, 0x85, 0x3D, 0xC0, 0x92, 0x49, 0x7D, 0x41, 0x9A, 0x91, 0x09, 0xEE,
                      # 11 NTG9X40C_11.14.08.11 / mdm9x40r11_core AC815s / SWI9x50 MR1100 Openlock Key
                      0x8A, 0x56, 0x03, 0xF0, 0xBB, 0x9C, 0x13, 0xD2, 0x4E, 0xB2, 0x45, 0xAD, 0xC4, 0x0A, 0xE7, 0x52,
                      # 12 SWI9x50 MR1100 Openmep Key
                      0x2A, 0xEF, 0x07, 0x2B, 0x19, 0x60, 0xC9, 0x01, 0x8B, 0x87, 0xF2, 0x6E, 0xC1, 0x42, 0xA8, 0x3A,
                      # 13 SWI9x50/SWI9x65 Unknown key
                      0x28, 0x55, 0x48, 0x52, 0x24, 0x72, 0x63, 0x37, 0x14, 0x26, 0x37, 0x50, 0xBE, 0xFE, 0x00, 0x00,
                      # 14 SWI9x50,SWI9X06Y,SWI9x65 IMEI nv key
                      0x22, 0x63, 0x48, 0x02, 0x24, 0x72, 0x27, 0x37, 0x19, 0x26, 0x37, 0x50, 0xBE, 0xEF, 0xCA, 0xFE,
                      # 15 NTG9X35C_02.08.29.00 Openmep Key AC791L/AC790S Old
                      0x98, 0xE1, 0xC1, 0x93, 0xC3, 0xBF, 0xC3, 0x50, 0x8D, 0xA1, 0x35, 0xFE, 0x50, 0x47, 0xB3, 0xC4,
                      # 16 NTG9X35C_02.08.29.00 Openmep Key AC791/AC790S, NTGX55_10.25.15.02 MR5100 Alternative, NTG9X40C_30.00.12.00 Alternative
                      0x61, 0x94, 0xCE, 0xA7, 0xB0, 0xEA, 0x4F, 0x0A, 0x73, 0xC5, 0xC3, 0xA6, 0x5E, 0xEC, 0x1C, 0xE2,
                      # 17 NTG9X35C_02.08.29.00 Openlock Key AC791/AC790S Old
                      0xC5, 0x50, 0x40, 0xDA, 0x23, 0xE8, 0xF4, 0x4C, 0x29, 0xE9, 0x07, 0xDE, 0x24, 0xE5, 0x2C, 0x1D,
                      # 18 NTG9X35C_02.08.29.00 Openlock Key AC791/AC790S, NTGX55_10.25.15.02 MR5100 Alternative, NTG9X40C_30.00.12.00 Alternative
                      0xF0, 0x14, 0x55, 0x0D, 0x5E, 0xDA, 0x92, 0xB3, 0xA7, 0x6C, 0xCE, 0x84, 0x90, 0xBC, 0x7F, 0xED,
                      # 19 SWI9X06Y_02.14.04.00 Openmep Key WP77xx
                      0x78, 0x19, 0xC5, 0x6D, 0xC3, 0xD8, 0x25, 0x3E, 0x51, 0x60, 0x8C, 0xA7, 0x32, 0x83, 0x37, 0x9D,
                      # 20 SWI9X06Y_02.14.04.00 Openlock Key WP77xx
                      0x12, 0xF0, 0x79, 0x6B, 0x19, 0xC7, 0xF4, 0xEC, 0x50, 0xF3, 0x8C, 0x40, 0x02, 0xC9, 0x43, 0xC8,
                      # 21 NTGX55 Openmep Key, NTGX55_10.25.15.02 MR5100, NTG9X40C_30.00.12.00
                      0x49, 0x42, 0xFF, 0x76, 0x8A, 0x95, 0xCF, 0x7B, 0xA3, 0x47, 0x5F, 0xF5, 0x8F, 0xD8, 0x45, 0xE4,
                      # 22 NTGX55 Openlock Key, NTGX55_10.25.15.02 MR5100, NTG9X40C_30.00.12.00
                      0xF8, 0x1A, 0x3A, 0xCC, 0xAA, 0x2B, 0xA5, 0xE8, 0x8B, 0x53, 0x5A, 0x55, 0xB9, 0x65, 0x57, 0x98,
                      # 23 NTG9X15A Openlock Key, NTG9X15A_01.08.02.00
                      0x54, 0xC9, 0xC7, 0xA4, 0x02, 0x1C, 0xB0, 0x11, 0x05, 0x22, 0x39, 0xB7, 0x84, 0xEF, 0x16, 0xCA,
                      # 24 NTG9X15A Openlock Key, NTG9X15A_01.08.02.00
                      0xC7, 0xE6, 0x39, 0xFE, 0x0A, 0xC7, 0xCA, 0x4D, 0x49, 0x8F, 0xD8, 0x55, 0xEB, 0x1A, 0xCD, 0x8A,
                      # 25 NTGX65 Openlock Key, NTGX65_10.04.13.03
                      0xF2, 0x4A, 0x9A, 0x2C, 0xDA, 0x3D, 0xA5, 0xE2, 0x6B, 0x56, 0x9A, 0x45, 0x29, 0x25, 0x77, 0x9A,
                      # 26 NTGX65 Openadm Key, NTGX65_10.04.13.03
                      0x46, 0x30, 0x33, 0x43, 0x44, 0x36, 0x42, 0x34, 0x41, 0x32, 0x31, 0x32, 0x30, 0x35, 0x39, 0x37
                      ])

class SierraGenerator():
    tbl = bytearray()
    rtbl = bytearray()
    devicegeneration = None

    def __init__(self):
        for _ in range(0, 0x14):
            self.rtbl.append(0x0)
        for _ in range(0, 0x100):
            self.tbl.append(0x0)

    def run(self, devicegeneration, challenge, _type):
        challenge = bytearray(unhexlify(challenge))

        self.devicegeneration = devicegeneration
        if not devicegeneration in prodtable:
            print("Sorry, " + devicegeneration + " not supported.")
            exit(0)

        mepid = prodtable[devicegeneration]["openmep"]
        cndid = prodtable[devicegeneration]["opencnd"]
        lockid = prodtable[devicegeneration]["openlock"]
        clen = prodtable[devicegeneration]["clen"]
        if len(challenge) < clen:
            challenge = [0 for _ in range(0, clen - len(challenge))]

        challengelen = len(challenge)
        if _type == 0:  # lockkey
            idf = lockid
        elif _type == 1:  # mepkey
            idf = mepid
        elif _type == 2:  # cndkey
            idf = cndid

        key = keytable[idf * 16:(idf * 16) + 16]
        resp = self.SierraKeygen(challenge=challenge, key=key, challengelen=challengelen, keylen=16)[:challengelen]
        resp = hexlify(resp).decode('utf-8').upper()
        return resp

    def selftest(self):
        test_table = [
            {"challenge": "8101A18AB3C3E66A", "devicegeneration": "MDM9x15", "response": "D1E128FCA8A963ED"},
            {"challenge": "BE96CBBEE0829BCA", "devicegeneration": "MDM9x40", "response": "1033773720F6EE66"},
            {"challenge": "BE96CBBEE0829BCA", "devicegeneration": "MDM9x30", "response": "1E02CE6A98B7DD2A"},
            {"challenge": "BE96CBBEE0829BCA", "devicegeneration": "MDM9x50", "response": "32AB617DB4B1C205"},
            {"challenge": "BE96CBBEE0829BCA", "devicegeneration": "MDM9x06", "response": "28D718CCD669DEDE"},
            {"challenge": "BE96CBBEE0829BCA", "devicegeneration": "MDM9x07", "response": "F5A4C9A0D402E34E"},
            {"challenge": "BE96CBBEE0829BCA", "devicegeneration": "MDM8200", "response": "EE702212D9C12FAB"},
            {"challenge": "BE96CBBEE0829BCA", "devicegeneration": "MDM9200_V1", "response": "A9A4E76E2653F753"},
            {"challenge": "BE96CBBEE0829BCA", "devicegeneration": "MDM9200_V2", "response": "8B0FAB4B6F81B080"},
            {"challenge": "BE96CBBEE0829BCA", "devicegeneration": "MDM9200_V3", "response": "4A69AD8A69F390E0"},
            {"challenge": "BE96CBBEE0829BCA", "devicegeneration": "MDM9x30_V1", "response": "6A5E4C9CBCBDA7DC"},
            {"challenge": "BE96CBBEE0829BCA", "devicegeneration": "MDM9200", "response": "EEDBF8BFF8DAE346"},
            {"challenge": "20E253156762DACE", "devicegeneration": "SDX55", "response": "03940D7067145323"},
            {"challenge": "2387885E7D290FEE", "devicegeneration": "MDM9x15A", "response": "DC3E51897BAA9C1E"},
            {"challenge": "4B1FEF9FD43C6DAA", "devicegeneration": "SDX65", "response":"1253C1B1E447B697"}
        ]
        for test in test_table:
            challenge = test["challenge"]
            devicegeneration = test["devicegeneration"]
            response = test["response"]
            openlock = self.run(devicegeneration, challenge, 0)
            padding = " " * (16 - len(devicegeneration))
            if openlock != response:
                print(devicegeneration + padding + " FAILED!")
            else:
                print(devicegeneration + padding + " PASSED :)")

    def SierraPreInit(self, counter, key, keylen, challengelen, mcount):
        if counter != 0:
            tmp2 = 0
            i = 1
            while i < counter:
                i = 2 * i + 1
            while True:
                tmp = mcount
                mcount = tmp + 1
                challengelen = (key[tmp & 0xFF] + self.tbl[(challengelen & 0xFF)]) & 0xFF
                if mcount >= keylen:
                    mcount = 0
                    challengelen = ((challengelen & 0xFF) + keylen) & 0xFF
                tmp2 = tmp2 + 1
                tmp3 = ((challengelen & 0xFF) & i) & 0xFF
                if tmp2 >= 0xB:
                    tmp3 = counter % tmp3
                if tmp3 <= counter:
                    break
            counter = tmp3 & 0xFF
        return [counter, challengelen, mcount]

    def SierraInit(self, key, keylen):
        if keylen == 0 or keylen > 0x20:
            retval = [0, keylen]
        elif 1 <= keylen <= 0x20:
            self.tbl = [(i & 0xFF) for i in range(0, 0x100)]
            mcount = 0
            cl = keylen & 0xffffff00
            i = 0xFF
            while i > -1:
                t, cl, mcount = self.SierraPreInit(i, key, keylen, cl, mcount)
                m = self.tbl[i]
                self.tbl[i] = self.tbl[(t & 0xff)]
                i = i - 1
                self.tbl[(t & 0xFF)] = m
            self.rtbl[0] = self.tbl[prodtable[self.devicegeneration]["init"][0]] if \
                prodtable[self.devicegeneration]["init"][0] != 0 else self.tbl[(cl & 0xFF)]
            self.rtbl[1] = self.tbl[prodtable[self.devicegeneration]["init"][1]] if \
                prodtable[self.devicegeneration]["init"][1] != 0 else self.tbl[(cl & 0xFF)]
            self.rtbl[2] = self.tbl[prodtable[self.devicegeneration]["init"][2]] if \
                prodtable[self.devicegeneration]["init"][2] != 0 else self.tbl[(cl & 0xFF)]
            self.rtbl[3] = self.tbl[prodtable[self.devicegeneration]["init"][3]] if \
                prodtable[self.devicegeneration]["init"][3] != 0 else self.tbl[(cl & 0xFF)]
            self.rtbl[4] = self.tbl[prodtable[self.devicegeneration]["init"][4]] if \
                prodtable[self.devicegeneration]["init"][4] != 0 else self.tbl[(cl & 0xFF)]
            retval = [1, keylen]
        return retval

    def sierra_calc8F(self, challenge, a=0, b=1, c=2, d=3, e=4, ret=0, ret2=2):
        # MDM9200
        self.rtbl[b] = (self.rtbl[b] + self.tbl[(self.rtbl[d] & 0xFF)]) & 0xFF
        uVar2 = self.rtbl[c] & 0xFF
        bVar1 = self.tbl[uVar2]
        uVar4 = self.rtbl[b] & 0xFF
        self.tbl[uVar2] = self.tbl[uVar4]
        self.rtbl[d] = (self.rtbl[d] + 1) & 0xFF
        uVar5 = self.rtbl[a] & 0xFF
        self.tbl[uVar4] = self.tbl[uVar5]
        uVar3 = self.rtbl[d] & 0xFF
        self.tbl[uVar5] = self.tbl[uVar3]
        self.tbl[uVar3] = bVar1
        self.rtbl[ret] = challenge  # c
        self.rtbl[ret2] = self.tbl[self.tbl[(self.tbl[(self.rtbl[e] + self.tbl[bVar1]) & 0xFF] + (
                self.tbl[uVar5] & 0xFF) + (self.tbl[uVar2] & 0xFF) & 0xff) & 0xFF] & 0xFF] ^ self.tbl[
                              ((self.tbl[uVar4] & 0xFF) + (bVar1 & 0xff)) & 0xFF] ^ challenge  # a
        self.rtbl[e] = (self.rtbl[e] + self.tbl[bVar1]) & 0xFF
        return self.rtbl[ret2] & 0xFF  # a

    def SierraAlgo(self, challenge, a=0, b=1, c=2, d=3, e=4, ret=3, ret2=1, flag=1):  # M9x15
        v6 = self.rtbl[e]
        v0 = (v6 + 1) & 0xFF
        self.rtbl[e] = v0
        self.rtbl[c] = (self.tbl[v6 + flag & 0xFF] + self.rtbl[c]) & 0xFF
        v4 = self.rtbl[c] & 0xFF
        v2 = self.rtbl[b] & 0xFF
        v1 = self.tbl[(v2 & 0xFF)]
        self.tbl[(v2 & 0xFF)] = self.tbl[(v4 & 0xFF)]
        v5 = self.rtbl[d] & 0xFF
        self.tbl[(v4 & 0xFF)] = self.tbl[(v5 & 0xFF)]
        self.tbl[(v5 & 0xFF)] = self.tbl[(v0 & 0xFF)]
        self.tbl[v0] = v1 & 0xFF
        u = self.tbl[(self.tbl[(
                self.tbl[((self.rtbl[a] + self.tbl[(v1 & 0xFF)]) & 0xFF)] + self.tbl[(v5 & 0xFF)] + self.tbl[
            (v2 & 0xFF)] & 0xff)] & 0xFF)]
        v = self.tbl[((self.tbl[(v4 & 0xFF)] + v1) & 0xFF)]
        self.rtbl[ret] = u ^ v ^ challenge
        self.rtbl[a] = (self.tbl[(v1 & 0xFF)] + self.rtbl[a]) & 0xFF
        self.rtbl[ret2] = challenge & 0xFF
        return self.rtbl[ret] & 0xFF

    def SierraFinish(self):
        self.tbl = [0 for _ in range(0, 0x100)]
        self.rtbl[0] = 0
        self.rtbl[1] = 0
        self.rtbl[2] = 0
        self.rtbl[3] = 0
        self.rtbl[4] = 0
        return 1

    def SierraKeygen(self, challenge: bytearray, key: bytearray, challengelen: int, keylen: int):
        challenge = challenge
        resultbuffer = bytearray([0 for _ in range(0, 0x100 + 1)])
        ret, keylen = self.SierraInit(key, keylen)
        if ret:
            for i in range(0, challengelen):
                exec(prodtable[self.devicegeneration]["run"])  # uses challenge
            self.SierraFinish()
        return resultbuffer

class connection:
    def __init__(self, port="", ip="192.168.1.1"):
        self.serial = None
        self.tn = None
        self.connected = False
        if port == "":
            port = self.detect(port)
            if port == "":
                self.tn = Telnet(ip, 5510)
                if self.tn:
                    self.connected = True
                else:
                    self.connected = False
        if port != "":
            self.serial = serial.Serial(port=port, baudrate=115200, bytesize=8, parity='N', stopbits=1, timeout=1)
            self.connected = self.serial.is_open

    def detect(self, port):
        if port == "":
            for port in serial.tools.list_ports.comports():
                if port.vid == 0x1199:
                    portid = port.location[-1:]
                    if int(portid) == 3:
                        print("Detected Sierra Wireless device at: " + port.device)
                        return port.device
                elif port.vid == 0x8046:
                    portid = port.location[-1:]
                    if int(portid) == 3:
                        print("Detected Netgear device at: " + port.device)
                        return port.device

        return ""

    def readreply(self):
        info = []
        if self.serial is not None:
            while (True):
                tmp = self.serial.readline().decode('utf-8').replace('\r', '').replace('\n', '')
                if "OK" in info:
                    return info
                elif ("ERROR" in info) or info == "":
                    return -1
                info.append(tmp)
        return info

    def send(self, cmd):
        if self.tn is not None:
            self.tn.write(bytes(cmd + "\r", 'utf-8'))
            time.sleep(0.05)
            data = ""
            while True:
                tmp = self.tn.read_eager()
                if tmp != "":
                    data += tmp.strip()
                else:
                    break
            return data.split("\r\n")
        elif self.serial is not None:
            self.serial.write(bytes(cmd + "\r", 'utf-8'))
            time.sleep(0.05)
            return self.readreply()

    def close(self):
        if self.tn is not None:
            self.tn.close()
            self.connected = False
        if self.serial is not None:
            self.serial.close()
            self.connected = False

class SierraKeygen(metaclass=LogBase):
    def __init__(self, cn, devicegeneration=None):
        self.cn = cn
        self.keygen = SierraGenerator()
        if devicegeneration == None:
            self.detectdevicegeneration()
        else:
            self.devicegeneration = devicegeneration

    def run_selftest(self):
        print("Running self-test ...")
        self.keygen.selftest()

    def detectdevicegeneration(self):
        if self.cn.connected:
            info = self.cn.send("ATI")
            if info != -1:
                revision = ""
                _model = ""
                for line in info:
                    if "Revision" in line:
                        revision = line.split(":")[1].strip()
                    if "Model" in line:
                        _model = line.split(":")[1].strip()
                if revision != "":
                    if "9200" in revision:
                        devicegeneration = "MDM9200"  # AC762S NTG9200H2_03.05.14.12ap
                    if "9X07" in revision:
                        devicegeneration = "MDM9x07"
                    elif "9X25" in revision:
                        if "NTG9X25C" in revision:
                            devicegeneration = "MDM9200"  # AC781S NTG9X25C_01.00.57.00
                    elif "9X15" in revision:
                        if "NTG9X15A" in revision:
                            devicegeneration = "MDM9x15A"  # Aircard 779S
                        elif "NTG9X15C" in revision:
                            devicegeneration = "MDM9200"  # AC770S NTG9X15C_01.18.02.00
                        elif "9X15A" in revision:
                            devicegeneration = "MDM9x15A"
                        else:
                            devicegeneration = "MDM9x15"
                    elif "9X30" in revision:
                        if "NTG9X35C" in revision:  # 790S NTG9X35C_11.11.15.03
                            devicegeneration = "MDM9x30_V1"
                        else:
                            devicegeneration = "MDM9x30"
                    elif "9X40" in revision and not "9X40C" in revision:
                        devicegeneration = "MDM9x40"
                    elif "9X50" in revision:
                        if "NTG9X50" in revision:
                            devicegeneration = "MDM9x40"  # MR1100,AC797S NTG9X50C_12.06.03.00
                        else:
                            devicegeneration = "MDM9x50"
                    elif "9X06" in revision:
                        devicegeneration = "MDM9x06"
                    elif "X55" in revision or "9X40C" in revision:
                        if "NTGX55" in revision:  # MR5100 NTGX55_10.25.15.02, MR5200 NTGX55_12.04.12.00
                            devicegeneration = "SDX55"
                        devicegeneration = "SDX55"
                    elif "X65" in revision:
                        if "NTGX65" in revision:
                            version = revision[revision.find("_") + 1:revision.find(" ")].split(".")
                            maj = int(version[0]) * 1000000 + int(version[1]) * 10000 + int(version[2]) * 100 + int(
                                version[3])
                            if maj < 10041303:
                                devicegeneration = "SDX55"
                            else:  # MR6400 NTGX65_10.04.13.03
                                devicegeneration = "SDX65"
                                   # MR6550 NTGX65_12.01.31.00
                        devicegeneration = "SDX65"
                    else:
                        devicegeneration = ""
                    # Missing:
                    # SDX24 Sierra
                    # MR2100 NTGX24_10.17.03.00
                    # SDX55 Sierra
                    # AC810S NTG9X40C_11.14.08.16
                    # AC800S NTG9X40C_11.14.07.00
                    self.devicegeneration = devicegeneration
            else:
                print("Error on getting ATI modem response. Wrong port? Aborting.")
                self.cn.close()
                exit(0)

    def openlock(self):
        print("Device generation detected: " + self.devicegeneration)
        # print("Sending AT!ENTERCND=\"A710\" request.")
        # info = self.cn.send("AT!ENTERCND=\"A710\"")
        # if info == -1:
        #    print("Uhoh ... invalid entercnd password. Aborting ...")
        #    return
        print("Sending AT!OPENLOCK? request")
        info = self.cn.send("AT!OPENLOCK?")
        challenge = ""
        if info != -1:
            if len(info) > 2:
                challenge = info[1]
                print("Received challenge: " + info[1])
        else:
            print("Error on AT!OPENLOCK? request. Aborting.")
            return False
        if challenge == "":
            print("Error: Couldn't get challenge. Aborting.")
            return False
        resp = self.keygen.run(self.devicegeneration, challenge, 0)
        print("Sending AT!OPENLOCK=\"" + resp + "\" response.")
        info = self.cn.send("AT!OPENLOCK=\"" + resp + "\"")
        if info == -1:
            print("Damn. AT!OPENLOCK failed.")
        else:
            print("Success. Device is now engineer unlocked.")
            return True
        return False

def main(args):
    version = "1.5"
    info = 'Sierra Wireless Generator ' + version + ' (c) B. Kerler 2019-2021'
    parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter, description=info)

    parser.add_argument(
        '-openlock', '-l',
        help='AT!OPENLOCK? modem response',
        default="")

    parser.add_argument(
        '-openmep', '-m',
        help='AT!OPENMEP? modem response',
        default="")

    parser.add_argument(
        '-opencnd', '-c',
        help='AT!OPENCND? modem response',
        default="")

    parser.add_argument(
        '-devicegeneration', '-d',
        help='device generation',
        default="")

    parser.add_argument(
        '-port', '-p',
        help='use com port for auto unlock',
        default="")

    parser.add_argument(
        '-ip',
        help='ip port for unlock, usually 192.168.1.1 or 192.168.2.1',
        default="192.168.1.1")

    parser.add_argument(
        '-unlock', '-u',
        help='use com port for openlock',
        default=False, action='store_true')

    parser.add_argument(
        '-selftest', '-s',
        help='run selftest',
        default=False, action='store_true')

    args = parser.parse_args()

    openlock = args.openlock
    openmep = args.openmep
    opencnd = args.opencnd
    devicegeneration = args.devicegeneration

    if not args.selftest:
        if (devicegeneration == "" or (openlock == "" and openmep == "" and opencnd == "")) and not args.unlock:
            print(info)
            print("------------------------------------------------------------\n")
            print("Usage: ./sierrakeygen.py [-l,-m,-c] [challenge] -d [devicegeneration]")
            print("Example: ./sierrakeygen.py -l BE96CBBEE0829BCA -d MDM9200")
            print("or: ./sierrakeygen.py -u for auto unlock")
            print("or: ./sierrakeygen.py -u -p [portname] for auto unlock with given portname")
            print("or: ./sierrakeygen.py -s for self-test")
            print("Supported devicegenerations :")
            for key in infotable:
                info = f"\t{key}:\t\t"
                count = 0
                for item in infotable[key]:
                    count += 1
                    if count > 15:
                        info += "\n\t\t\t\t\t"
                        count = 0
                    info += item + ","

                info = info[:-1]
                print(info)
            exit(0)

        if devicegeneration == "" and not args.unlock:
            print("You need to specific a device generation as well. Option -d")
            exit(0)
    if devicegeneration == "":
        devicegeneration = None
    if args.selftest:
        kg = SierraKeygen(None, "selftest")
        kg.run_selftest()
    elif args.unlock:
        cn = connection(args.port, args.ip)
        if cn.connected:
            kg = SierraKeygen(cn, devicegeneration)
            if kg.devicegeneration == "":
                print("Unknown device generation. Please send me details :)")
            else:
                kg.openlock()
        cn.close()
    else:
        kg = SierraKeygen(None, devicegeneration)
        if openlock != "":
            resp = kg.keygen.run(devicegeneration, openlock, 0)
            print("AT!OPENLOCK=\"" + resp + "\"")
        elif openmep != "":
            resp = kg.keygen.run(devicegeneration, openmep, 1)
            print("AT!OPENMEP=\"" + resp + "\"")
        elif opencnd != "":
            resp = kg.keygen.run(devicegeneration, opencnd, 2)
            print("AT!OPENCND=\"" + resp + "\"")

if __name__ == '__main__':
    main(sys.argv)


import os
import sys
from flask import Flask, jsonify, request, abort, send_file
from dotenv import load_dotenv
from linebot import LineBotApi, WebhookParser
from linebot.exceptions import InvalidSignatureError
from linebot.models import MessageEvent, TextMessage, TextSendMessage
from fsm import TocMachine
from utils import send_text_message

import configparser

load_dotenv()

machine = TocMachine(
    states=["user", "help",
            "new", "hot", "search", "name",
            "taiwan", "usa", "week",
            "hot_drama", "type_drama", "hot_taiwan",
            "china", "hongkong", "japan", "korea", "america", "england",
            ],
    transitions=[
        {
            "trigger": "advance",
            "source": "user",
            "dest": "help",
            "conditions": "is_going_to_help",
        },
        {
            "trigger": "advance",
            "source": "user",
            "dest": "new",
            "conditions": "is_going_to_new",
        },
        {
            "trigger": "advance",
            "source": "user",
            "dest": "hot",
            "conditions": "is_going_to_hot",
        },
        {
            "trigger": "advance",
            "source": "user",
            "dest": "search",
            "conditions": "is_going_to_search",
        },

        {
            "trigger": "advance",
            "source": "search",
            "dest": "name",
            "conditions": "is_going_to_name",
        },

        {
            "trigger": "advance",
            "source": "user",
            "dest": "taiwan",
            "conditions": "is_going_to_taiwan",
        },
        {
            "trigger": "advance",
            "source": "user",
            "dest": "usa",
            "conditions": "is_going_to_usa",
        },
        {
            "trigger": "advance",
            "source": "user",
            "dest": "week",
            "conditions": "is_going_to_week",
        },

        {
            "trigger": "advance",
            "source": "user",
            "dest": "hot_taiwan",
            "conditions": "is_going_to_hot_taiwan",
        },
        {
            "trigger": "advance",
            "source": "user",
            "dest": "hot_drama",
            "conditions": "is_going_to_hot_drama",
        },
        {
            "trigger": "advance",
            "source": "user",
            "dest": "type_drama",
            "conditions": "is_going_to_type_drama",

        },
        {
            "trigger": "advance",
            "source": "type_drama",
            "dest": "china",
            "conditions": "is_going_to_china",
        },
        {
            "trigger": "advance",
            "source": "type_drama",
            "dest": "hongkong",
            "conditions": "is_going_to_hongkong",
        },
        {
            "trigger": "advance",
            "source": "type_drama",
            "dest": "japan",
            "conditions": "is_going_to_japan",
        },
        {
            "trigger": "advance",
            "source": "type_drama",
            "dest": "korea",
            "conditions": "is_going_to_korea",
        },
        {
            "trigger": "advance",
            "source": "type_drama",
            "dest": "america",
            "conditions": "is_going_to_america",
        },
        {
            "trigger": "advance",
            "source": "type_drama",
            "dest": "england",
            "conditions": "is_going_to_england",
        },

        {
            "trigger": "advance",
            "source": ["china", "hongkong", "japan", "korea", "america", "england"],
            "dest": "type_drama",
            "conditions": "is_going_to_back",
        },

        {
            "trigger": "advance",
            "source": "name",
            "dest": "search",
            "conditions": "is_going_to_back",
        },

        {
            "trigger": "advance",
            "source": ["china", "hongkong", "japan", "korea", "america", "england"
                        , "name"],
            "dest": "help",
            "conditions": "is_going_to_home",
        },

        {
            "trigger": "go_back",
            "source": [ "help",
                        "new", "hot",
                        # "movie", "tv", "search_movie",
                        "taiwan", "usa", "week",
                        "hot_drama", "hot_taiwan",
                        # "china", "hongkong", "japan", "korea", "america", "england"
                        ],
            "dest": "user"
        }
    ],
    initial="user",
    auto_transitions=False,
    show_conditions=True,
)

app = Flask(__name__, static_url_path="")

# read config
config = configparser.ConfigParser()
config.read("config.ini")

# get channel_secret and channel_access_token from your environment variable
# channel_secret = os.getenv("LINE_CHANNEL_SECRET", None)
# channel_access_token = os.getenv("LINE_CHANNEL_ACCESS_TOKEN", None)

# get channel_secret and channel_access_token from your environment variable
channel_access_token = config['line_bot']['Channel_Access_Token']
channel_secret = config['line_bot']['Channel_Secret']

# print(channel_access_token)
# print(channel_secret)

if channel_secret is None:
    print("Specify LINE_CHANNEL_SECRET as environment variable.")
    sys.exit(1)
if channel_access_token is None:
    print("Specify LINE_CHANNEL_ACCESS_TOKEN as environment variable.")
    sys.exit(1)

line_bot_api = LineBotApi(channel_access_token)
parser = WebhookParser(channel_secret)

# echo
@app.route("/callback", methods=["POST"])
def callback():
    signature = request.headers["X-Line-Signature"]
    # get request body as text
    body = request.get_data(as_text=True)
    app.logger.info("Request body: " + body)

    # parse webhook body
    try:
        events = parser.parse(body, signature)
    except InvalidSignatureError:
        abort(400)

    # if event is MessageEvent and message is TextMessage, then echo text
    for event in events:
        if not isinstance(event, MessageEvent):
            continue
        if not isinstance(event.message, TextMessage):
            continue

        line_bot_api.reply_message(
            event.reply_token, TextSendMessage(text=event.message.text)
        )

    return "OK"

# main function
@app.route("/webhook", methods=["POST"])
def webhook_handler():
    signature = request.headers["X-Line-Signature"]
    # get request body as text
    body = request.get_data(as_text=True)
    app.logger.info(f"Request body: {body}")

    # parse webhook body
    try:
        events = parser.parse(body, signature)
    except InvalidSignatureError:
        abort(400)

    # if event is MessageEvent and message is TextMessage, then echo text
    for event in events:
        if not isinstance(event, MessageEvent):
            continue
        if not isinstance(event.message, TextMessage):
            continue
        if not isinstance(event.message.text, str):
            continue
        print(f"\nFSM STATE: {machine.state}")
        print(f"REQUEST BODY: \n{body}")
        response = machine.advance(event)
        if response == False:
            send_text_message(event.reply_token, "Not Entering any State")

    return "OK"

# draw the fsm img
@app.route("/show-fsm", methods=["GET"])
def show_fsm():
    machine.get_graph().draw("fsm.png", prog="dot", format="png")
    return send_file("fsm.png", mimetype="image/png")

if __name__ == "__main__":
    port = os.environ.get("PORT", 5000)
    app.run(host="0.0.0.0", port=port, debug=True)


import signal

handlers = []

def handler(signal, frame):
	for h in handlers:
		h(signal, frame)

def sigint(fn):
	if 0 == len(handlers):
		signal.signal(signal.SIGINT, handler)
	handlers.append(fn)
	return fn


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

from geoedfframework.utils.GeoEDFError import GeoEDFError
from geoedfframework.GeoEDFPlugin import GeoEDFPlugin

import pandas as pd
import math

""" Module for implementing the DateTimeFilter. This supports a date time string pattern
    that specifies the kinds of values that will be returned from the filter. The user
    also provides a start date (in mm/dd/yyyy format) with optional timestamp as hh:mm:ss;
    and an optional end date. If an end date is provided, it is assumed that the user wants
    to generate all dates between the start and end subject to a period parameter. The period
    is a number followed by a character such as D,M,Y etc. for day, month and year. E.g. 2M is
    a period of 2 months. The DateTimeFilter is a filter that can be used to generate
    a string representation of all intervening dates. The parameter exact_dates is used only when
    the period is nD. By default, we will attempt to return every nth day of the year; if exact_dates
    is true, we will return every nth day beginning at the exact start date.
"""

class DateTimeFilter(GeoEDFPlugin):
    # has_time is Boolean, False by default
    # if end is provided, period also needs to be provided
    __optional_params = ['end','period','has_time','exact_dates']
    __required_params = ['pattern','start']

    # we use just kwargs since we need to be able to process the list of attributes
    # and their values to create the dependency graph in the GeoEDFConnectorPlugin super class
    def __init__(self, **kwargs):

        # list to hold all the parameter names; will be accessed in super to
        # construct dependency graph
        self.provided_params = self.__required_params + self.__optional_params

        # check that all required params have been provided
        for param in self.__required_params:
            if param not in kwargs:
                raise GeoEDFError('Required parameter %s for DateTimeFilter not provided' % param)

        # specific check for conditionally required params
        # if end is provided, also need a period
        if 'end' in kwargs:
            if 'period' not in kwargs:
                raise GeoEDFError('Period is required for DateTimeFilter when both start and end are provided.')

        # set all required parameters
        for key in self.__required_params:
            setattr(self,key,kwargs.get(key))

        # set optional parameters
        for key in self.__optional_params:
            # if key not provided in optional arguments, defaults value to None
            setattr(self,key,kwargs.get(key,None))
            # if has_time is not provided, set to False
            if key == 'has_time':
                if self.has_time is None:
                    self.has_time = False
            if key == 'exact_dates':
                if self.exact_dates is None:
                    self.exact_dates = False

        # initialize filter values array
        self.values = []

        # class super class init
        super().__init__()

    # each Filter plugin needs to implement this method
    # if error, raise exception; if not, set values attribute
    # assume this method is called only when all params have been fully instantiated
    def filter(self):

        # convert the start and end dates from strings to Pandas DateTime
        try:
            # check if time is present
            if self.has_time:
                start_date = pd.to_datetime(self.start,format='%m/%d/%Y %H:%M:%S')
            else:
                start_date = pd.to_datetime(self.start,format='%m/%d/%Y')
            if self.end is not None:
                if self.has_time:
                    end_date = pd.to_datetime(self.end,format='%m/%d/%Y %H:%M:%S')
                else:
                    end_date = pd.to_datetime(self.end,format='%m/%d/%Y')
        except ValueError as e:
            raise GeoEDFError('Invalid values provided for start or end date to DateTimeFilter : %s' % e)
        except:
            raise GeoEDFError('Invalid values provided for start or end date to DateTimeFilter')

        # use the period to generate all intervening dates
        try:
            # if exact_dates is used and the period is n days, process differently
            # essentially reset start date to align with period

            if (not self.exact_dates) and (self.period[-1:] == 'D'):

                start_year = start_date.strftime('%Y')
                start_day_of_year = int(start_date.strftime('%j'))
                period_num = int(self.period[:-1])
                # check if start day aligns with period
                if (start_day_of_year - 1)%period_num > 0:
                    new_start_day_of_year = math.ceil((start_day_of_year - 1)/period_num) * period_num + 1
                    start_date = pd.to_datetime('%d/%s' % (new_start_day_of_year,start_year),format='%j/%Y')

            if self.end is not None:
                all_dates = pd.date_range(start=start_date,end=end_date,freq=self.period)
            else:
                all_dates = [start_date]

            # convert back to string using the pattern
            for dt in all_dates:
                self.values.append(dt.strftime(self.pattern))

        except ValueError as e:
            raise GeoEDFError('Error applying DateTimeFilter : %s' % e)
        except:
            raise GeoEDFError('Unknown error applying DateTimeFilter')


#!/usr/bin/env python

# encoding: utf-8

# @Time    : 2019/10/28 15:18
# @Author  : yq
# @Site    :
# @File    : FTPManager.py
# @Software: PyCharm
import os
import socket
import configparser
import time
from ftplib import FTP

import codecs

# config_path = r'E:\projects\pycharm\NearGoos\background\byQY\config\Config.ini'
class FTPManager:
    def __init__(self, config_path, section):
        self.config_path = config_path
        self.ftp = FTP()
        self.section = section

        # 重新设置下编码方式
        self.ftp.encoding = 'UTF-8'

    def ftp_connect(self, host, username, password):
        """
        连接FTP
        :param host: 主机地址
        :param username: 用户名
        :param password: 密码
        :return:
        """
        # socket.setdefaulttimeout(3600)

        self.ftp.connect(host)
        self.ftp.login(username, password)

    def get_file_info_list(self, filepath):
        """
        以文本的方式获得文件信息列表
        :param filepath: 文件目录
        :return:
        """
        self.ftp.cwd(filepath)
        file_list = self.ftp.retrlines('MLSD')
        # file_list = self.ftp.nlst()
        # print(file_list)
        return file_list

    def get_config(self):
        """
        获取配置文件信息
        :return: 配置文件类
        """
        config = configparser.ConfigParser()
        config.read(self.config_path)
        return config

    def close_connect(self):
        """
        关闭FTP链接
        :return:
        """
        self.ftp.close()

    def get_filename(self, file_need, filepath):
        """
        返回指定文件名的文件目录
        :param file_need: 需被匹配的文件名
        :param filepath: 文件目录
        :return: 文件名数组
        """
        self.ftp.cwd(filepath)
        files = [filename for filename in self.ftp.nlst() if file_need in filename]
        return files

    def get_file_size(self, file_need, filepath):
        self.ftp.cwd(filepath)
        files = [filename for filename in self.ftp.nlst() if file_need in filename]
    # # 存储
    # def upload(self, target_path, old_path):
    #     fp = open(old_path, "rb")
    #     buf_size = 1024
    #     self.storbinary("STOR {}".format(target_path), fp, buf_size)
    #     fp.close()
    # def is_exist_file(self, local_file, remote_file):
    #     str_year = self.getCreateTime(self, remote_file)
    #

    def is_same_size(self, local_file, remote_file):
        """
        判断远程文件和本地文件大小是否一致
        :param local_file: 本地文件
        :param remote_file: 远程文件
        :return: 是否一致的布尔值
        """

        try:
            remote_file_size = self.ftp.size(remote_file)
        except Exception as err:
            # self.debug_print("is_same_size() 错误描述为：%s" % err)
            remote_file_size = -1
        try:
            local_file_size = os.path.getsize(local_file)
        except Exception as err:
            # self.debug_print("is_same_size() 错误描述为：%s" % err)
            local_file_size = -1
        # self.debug_print('local_file_size:%d  , remote_file_size:%d' % (local_file_size, remote_file_size))
        if remote_file_size == local_file_size:
            return 1
        else:
            return 0

    # [to-do] 此处可增加为批量下载
    def download_file(self, local_file, remote_file, local_file_dir):
        """
        下载远程文件至本地
        :param local_file:本地文件
        :param remote_file:远程文件
        :return:
        """
        if os.path.exists(local_file):
            # self.debug_print('%s 文件大小相同，无需下载' % local_file)
            print('文件已有无需下载')
            return 0
        else:
            if not os.path.exists(local_file_dir):
                os.makedirs(local_file_dir)
            # self.debug_print('>>>>>>>>>>>>下载文件 %s ... ...' % local_file)
            buf_size = 1024
            file_handler = open(local_file, 'wb')
            self.ftp.retrbinary('RETR %s' % remote_file, file_handler.write, buf_size)
            file_handler.close()
            print('下载数据成功')
            with open(local_file, 'rb+') as fp:
                content = fp.read()
                try:
                    #
                    content.decode("utf8")
                except:
                    content = content.decode("ANSI",'ignore').encode("utf8")
                    fp.seek(0)
                    fp.write(content)
                fp.close()
            print('转码成功')

            return 1

    def upload_file(self, local_file, remote_file):
        """
        从本地上传文件至FTP
        :param local_path:
        :param remote_path:
        :return:
        """
        if not os.path.isfile(local_file):
            # self.debug_print('%s 不存在' % local_file)
            print('不存在')
            return
        if self.is_same_size(local_file, remote_file):
            # self.debug_print('%s 文件大小相同，无需上传' % local_file)
            print('大小相同无需上传')
            return

        buff_size = 1024

        file_handler = open(local_file, 'rb')
        self.ftp.storbinary('STOR %s' % remote_file, file_handler, buff_size)
        file_handler.close()
        # self.debug_print('上传: %s' % local_file + "成功!")
        print('上传成功')

    # 获取年份
    def getCreateTime(self, remote_file):
       L = list(self.ftp.sendcmd('MDTM ' + remote_file))
       str = L[4]+L[5]+L[6]+L[7]
       return str


"""_summary_
"""
import logging
import pika
from pika.exceptions import AMQPError
from src.settings import QUEUE

class QueueRabbitmq():
    """_summary_
    """

    def __init__(self, queue_name: str):
        """_summary_
        """
        self.__connection = None
        self.__channel = None
        self.__queue_name = queue_name

    def __create_connection(self):
        """_summary_
        """
        self.__connection = pika.BlockingConnection(
            pika.ConnectionParameters(
                host=QUEUE["QUEUE_HOST"],
                port=QUEUE["QUEUE_PORT"]
            ))
        self.__channel = self.__connection.channel()
        self.__channel.queue_declare(queue=self.__queue_name)

    def send_message(self, message: str):
        """_summary_
        """
        try:
            self.__create_connection()
            self.__channel.basic_publish(
                exchange='', routing_key=self.__queue_name, body=message)
            self.__connection.close()
        except AMQPError as ex:
            logging.error(ex.args)

    def receive_message(self):
        """_summary_
        """
        self.__create_connection()
        method_frame, header_frame, body = self.__channel.basic_get(
            self.__queue_name
        )

        if method_frame:
            self.__channel.basic_ack(method_frame.delivery_tag)

        self.__connection.close()
        return method_frame, body

    def queue_purge(self):
        """_summary_
        """
        self.__create_connection()
        self.__channel.queue_purge(self.__queue_name)


from django.shortcuts import render
from .models import Article, Category, BlogComment, Tag
from .forms import BlogCommentForm
from django.shortcuts import get_object_or_404, redirect, get_list_or_404
from django.views.generic.list import ListView
from django.views.generic.detail import DetailView
# import markdown2
import re
import logging

# Create your views here.

logger = logging.getLogger(__name__)

class IndexView(ListView):
    template_name = 'blog/index.html'
    # 制定获取的model数据列表的名字
    context_object_name = "article_list"

    def get_queryset(self):
        """
        过滤数据，获取已发布文章列表，并转为html格式
        Returns:
        """
        article_list = Article.objects.filter(status='0')
        # for article in article_list:
        #     article.body = markdown2.markdown(article.body,)
        return article_list

    # 为上下文添加额外的变量，以便在模板中访问
    def get_context_data(self, **kwargs):
        category_list = Category.objects.all().order_by('name')
        for category in category_list:
            count = Article.objects.filter(category__name=category.name).count()
            category.count = count
        tag_list = Tag.objects.all().order_by('name')
        for tag in tag_list:
            count = Article.objects.filter(tags__name=tag.name).count()
            tag.count = count
        kwargs['category_list'] = category_list
        kwargs['tag_list'] = tag_list
        return super(IndexView, self).get_context_data(**kwargs)

class CategoryView(ListView):
    template_name = 'blog/index.html'
    context_object_name = "article_list"

    def get_queryset(self):
        article_list = Article.objects.filter(category=self.kwargs['cate_id'], status='0')
        # for article in article_list:
        #     article.body = markdown2.markdown(article.body,)
        return article_list

    def get_context_data(self, **kwargs):
        category_list = Category.objects.all().order_by('name')
        for category in category_list:
            count = Article.objects.filter(category__name=category.name).count()
            category.count = count
        tag_list = Tag.objects.all().order_by('name')
        for tag in tag_list:
            count = Article.objects.filter(tags__name=tag.name).count()
            tag.count = count
        kwargs['category_list'] = category_list
        kwargs['tag_list'] = tag_list
        name = get_object_or_404(Category, pk=self.kwargs['cate_id'])
        kwargs['cate_name'] = name

        return super(CategoryView, self).get_context_data(**kwargs)

class TagView(ListView):
    template_name = 'blog/index.html'
    context_object_name = 'article_list'

    def get_queryset(self):
        """
        根据指定的标签名获得该标签下的全部文章
        """
        article_list = Article.objects.filter(tags=self.kwargs['tag_id'], status='0')
        # for article in article_list:
        #     article.body = markdown2.markdown(article.body, extras=['fenced-code-blocks'], )
        return article_list

    def get_context_data(self, **kwargs):
        category_list = Category.objects.all().order_by('name')
        for category in category_list:
            count = Article.objects.filter(category__name=category.name).count()
            category.count = count
        tag_list = Tag.objects.all().order_by('name')
        for tag in tag_list:
            count = Article.objects.filter(tags__name=tag.name).count()
            tag.count = count
        kwargs['category_list'] = category_list
        kwargs['tag_list'] = tag_list
        name = get_object_or_404(Tag, pk=self.kwargs['tag_id'])
        kwargs['tag_name'] = name
        return super(TagView, self).get_context_data(**kwargs)

class ArticleDetailView(DetailView):
    """
    显示文章详情
    """
    model = Article
    template_name = 'blog/detail.html'
    context_object_name = "article"

    # pk_url_kwarg用于接受来自url中的参数作为主键
    pk_url_kwarg = 'article_id'

    # 从数据库中获取id为pk_url_kwargs的对象
    def get_object(self, queryset=None):
        obj = super(ArticleDetailView, self).get_object()
        # 点击一次阅读量增加一次
        obj.views += 1
        obj.save()
        # obj.body = markdown2.markdown(obj.body, extras=['fenced-code-blocks'],)
        return obj

    # 新增form到上下文
    def get_context_data(self, **kwargs):
        kwargs['comment_list'] = self.object.blogcomment_set.all()
        kwargs['form'] = BlogCommentForm()
        kwargs['category_list'] = Category.objects.all().order_by('name')
        kwargs['tag_list'] = Tag.objects.all().order_by('name')
        return super(ArticleDetailView, self).get_context_data(**kwargs)

def CommentView(request, article_id):
    if request.method == 'POST':
        form = BlogCommentForm(request.POST)
        if form.is_valid():
            name = form.cleaned_data['user_name']
            body = form.cleaned_data['body']

            article = get_object_or_404(Article, pk=article_id)
            new_record = BlogComment(user_name=name,
                                 body=body,
                                article=article)
            new_record.save()
            return redirect('app:detail', article_id=article_id)

def blog_search(request,):
    search_for = request.GET['search_for']
    if search_for:
        results = []
        article_list = get_list_or_404(Article)
        category_list = get_list_or_404(Category)
        for article in article_list:
            if re.findall(search_for, article.title):
                results.append(article)
        # for article in results:
        #     article.body = markdown2.markdown(article.body, )
        tag_list = Tag.objects.all().order_by('name')
        context = {
            'article_list': results,
            'category_list': category_list,
            'tag_list': tag_list,
        }

        return render(request, 'blog/index.html', context)
    else:
        return redirect('app:index')

def about(request,):
    return render(request, 'blog/about.html')


from flask import Flask, render_template, url_for, request, jsonify
from flask_sqlalchemy import SQLAlchemy
import config

app = Flask(__name__)
app.config.from_object(config)
db = SQLAlchemy(app)

class Student(db.Model):
    __tablename__ = 'students'
    id = db.Column(db.Integer, primary_key = True, autoincrement = True)
    phoneNum = db.Column(db.String(11), nullable = False, unique = True)
    userName = db.Column(db.String(16), nullable = False)
    course = db.Column(db.String(10), nullable = False)
    Number = db.Column(db.Integer, nullable = False)

@app.route('/yike')
def index():
    return render_template('index.html')

@app.route('/info', methods = ['POST'])
def addInfo():
    ntfInfo = {'result' : 'error'}
    if request.method == 'POST':
        data = request.get_json()
        phoneNum = data.get('phoneNum')
        userName = data.get('userName')
        course = data.get('course')
        Number = data.get('Number')

        if None != phoneNum and None != userName and None != course and None != Number:
            if Student.query.filter_by(phoneNum = phoneNum).first() == None:
                student = Student(phoneNum = phoneNum, userName = userName, course = course, Number = Number)
                db.session.add(student)
                db.session.commit()
                ntfInfo['result'] = 'success'
            else:
                ntfInfo['result'] = 'already_exist'

        return jsonify(ntfInfo)

if __name__ == '__main__':

    app.debug = True
    app.run()


from selenium import webdriver
from selenium.webdriver.common.by import By
import math

link = "http://suninjuly.github.io/redirect_accept.html"

browser = webdriver.Chrome()

try:
    print("Opening link...")
    browser.get(link)

    print("Clicking button to open a new tab...")
    button = browser.find_element(By.CSS_SELECTOR, "button.trollface")
    button.click()

    print("Switching to the new tab...")
    browser.switch_to.window(browser.window_handles[1])

    print("Finding the value of x...")
    x_element = browser.find_element(By.CSS_SELECTOR, "#input_value")
    x = int(x_element.text)
    print(f"Value of x is {x}.")

    y = str(math.log(abs(12*math.sin(x))))
    print(f"Entering the value of y ({y})...")
    input_field = browser.find_element(By.CSS_SELECTOR, "#answer")
    input_field.send_keys(y)

    print("Submitting form...")
    submit_button = browser.find_element(By.CSS_SELECTOR, "button[type='submit']")
    submit_button.click()
    print("Completed!")

finally:
    print("Closing the browser...")
    browser.quit()


import sqlalchemy as sa
import sqlalchemy.orm as orm
from mealie.core import root_logger
from mealie.db.models.model_base import SqlAlchemyBase
from slugify import slugify
from sqlalchemy.orm import validates

logger = root_logger.get_logger()

site_settings2categories = sa.Table(
    "site_settings2categories",
    SqlAlchemyBase.metadata,
    sa.Column("site_settings.id", sa.Integer, sa.ForeignKey("site_settings.id")),
    sa.Column("category_id", sa.Integer, sa.ForeignKey("categories.id")),
)

group2categories = sa.Table(
    "group2categories",
    SqlAlchemyBase.metadata,
    sa.Column("group_id", sa.Integer, sa.ForeignKey("groups.id")),
    sa.Column("category_id", sa.Integer, sa.ForeignKey("categories.id")),
)

recipes2categories = sa.Table(
    "recipes2categories",
    SqlAlchemyBase.metadata,
    sa.Column("recipe_id", sa.Integer, sa.ForeignKey("recipes.id")),
    sa.Column("category_id", sa.Integer, sa.ForeignKey("categories.id")),
)

custom_pages2categories = sa.Table(
    "custom_pages2categories",
    SqlAlchemyBase.metadata,
    sa.Column("custom_page_id", sa.Integer, sa.ForeignKey("custom_pages.id")),
    sa.Column("category_id", sa.Integer, sa.ForeignKey("categories.id")),
)

class Category(SqlAlchemyBase):
    __tablename__ = "categories"
    id = sa.Column(sa.Integer, primary_key=True)
    name = sa.Column(sa.String, index=True, nullable=False)
    slug = sa.Column(sa.String, index=True, unique=True, nullable=False)
    recipes = orm.relationship("RecipeModel", secondary=recipes2categories, back_populates="recipe_category")

    @validates("name")
    def validate_name(self, key, name):
        assert name != ""
        return name

    def __init__(self, name, session=None) -> None:
        self.name = name.strip()
        self.slug = slugify(name)

    def update(self, name, session=None) -> None:
        self.__init__(name, session)

    @staticmethod
    def get_ref(session, slug: str):
        return session.query(Category).filter(Category.slug == slug).one()

    @staticmethod
    def create_if_not_exist(session, name: str = None):
        test_slug = slugify(name)
        result = session.query(Category).filter(Category.slug == test_slug).one_or_none()
        if result:
            logger.debug("Category exists, associating recipe")
            return result
        else:
            logger.debug("Category doesn't exists, creating tag")
            return Category(name=name)


a = float('-inf')
b = float('inf')
m = int(input())
mas = []
for i in range(m):
    mas.append(list(map(int, input().split())))
for i in range(len(mas)):
    for j in range(len(mas[i])):
        if mas[i][j] > a:
            a = mas[i][j]
            c =  i
        if mas[i][j] < b:
            b = mas[i][j]
            d = i
for i in range(0, len(mas)):
    for j in range(0, len(mas[i])):
        print(mas[i][j], end=" ")
    print()
print('Минимум', b)
print('Максимум', a)
mas[d], mas[c] = mas[c], mas[d]
for i in range(0, len(mas)):
    for j in range(0, len(mas[i])):
        print(mas[i][j], end=" ")
    print()


from PIL import Image

weights = Image.open("weights.png")

for i in range(1, 11):
    case = Image.open(f"cases/{i}.png")
    circle = 0
    for x in range(case.height):
        for y in range(case.width):
            casePixel = case.getpixel((x, y))
            weightPixel = weights.getpixel((x, y))

            weightSum = ((weightPixel[3] - 225) / 10) / \
                (case.height * case.width)

            if casePixel == (0, 0, 0, 255) and circle < 1:
                circle += weightSum
            elif circle == 1:
                circle -= weightSum

    print(f"{i}: c") if circle > (1 - circle) else print(f"{i}: r")


from django.conf.urls import url
from . import views

app_name = 'music'

urlpatterns = [
    # Matches /music/
    url(r'^$', views.IndexView.as_view(), name='index'),  # Default page

    # Matches /music/albumID/
    url(r'^(?P<pk>[0-9]+)/$', views.DetailView.as_view(), name='detail'),  # /music/:albumid

    # Matches /music/album/add/
    url(r'^album/add/$', views.AlbumCreate.as_view(), name='album-add'),

    # Matches /music/album/:albumID/edit/
    url(r'^album/(?P<pk>[0-9]+)/edit$', views.AlbumUpdate.as_view(), name='album-edit'),

    # Matches /music/album/albumID/delete/
    url(r'^album/(?P<pk>[0-9]+)/delete', views.AlbumDelete.as_view(), name='album-delete'),

]


import torch
import codecs

def parse_byte(b):
    if isinstance(b, str):
        return ord(b)
    return b

def get_int(b):
    return int(codecs.encode(b, 'hex'), 16)

def read_image_file(path):
    with open(path, 'rb') as f:
        data = f.read()
        assert get_int(data[:4]) == 2051
        length = get_int(data[4:8])
        num_rows = get_int(data[8:12])
        num_cols = get_int(data[12:16])
        images = []
        idx = 16
        for l in range(length):
            img = []
            images.append(img)
            for r in range(num_rows):
                row = []
                img.append(row)
                for c in range(num_cols):
                    row.append(parse_byte(data[idx]))
                    idx += 1
        assert len(images) == length
        return torch.ByteTensor(images).view(-1, 28, 28)

def read_label_file(path):
    with open(path, 'rb') as f:
        data = f.read()
        assert get_int(data[:4]) == 2049
        length = get_int(data[4:8])
        labels = [parse_byte(b) for b in data[8:]]
        assert len(labels) == length
        return torch.LongTensor(labels)