import itertools
import math
import pickle
import time
from bisect import bisect_left
from datetime import date, datetime, timedelta
from pathlib import Path
from typing import Any, Dict, List, Optional, Tuple

import numpy as np
import pandas as pd
from tqdm.auto import tqdm

from wikipedia_cleanup.data_filter import (
    AbstractDataFilter,
    FeatureAdderFilter,
    KeepAttributesDataFilter,
    StaticInfoboxTemplateDataAdder,
)
from wikipedia_cleanup.data_processing import get_data
from wikipedia_cleanup.evaluation import (
    ALL_EVAL_METHODS,
    create_prediction_output,
    evaluate_prediction,
)
from wikipedia_cleanup.predictor import Predictor
from wikipedia_cleanup.property_correlation import PropertyCorrelationPredictor
from wikipedia_cleanup.random_forest import RandomForestPredictor
from wikipedia_cleanup.utils import plot_directory, result_directory

class TrainAndPredictFramework:
    TEST_DURATION = 365

    TEST_SET_START_DATE = datetime(2018, 9, 1)
    VALIDATION_SET_START_DATE = TEST_SET_START_DATE - timedelta(days=TEST_DURATION)

    def __init__(
        self,
        predictor: Predictor,
        group_key: List[str],
        test_start_date: datetime = VALIDATION_SET_START_DATE,
        test_duration: int = TEST_DURATION,
        run_id: Optional[str] = None,
    ):
        self.test_start_date = test_start_date
        self.test_duration = test_duration
        self.group_key = group_key
        self.testing_timeframes = [1, 7, 30, 365]
        self.timeframe_labels = ["day", "week", "month", "year"]

        self.predictor = predictor
        own_relevant_attributes = ["value_valid_from", "template"]
        self.relevant_attributes = list(
            set(own_relevant_attributes)
            | set(predictor.get_relevant_attributes())
            | set(self.group_key)
        )
        self.data: pd.DataFrame = pd.DataFrame()
        self.run_results: Dict[str, Any] = {}
        current_time = datetime.now()
        self.run_id = (
            run_id
            if run_id is not None
            else (
                f"{current_time.date()}:{current_time.hour}-"
                f"{current_time.minute}-{current_time.second}"
            )
        )

    def load_data(
        self,
        input_path: Path,
        n_files: int,
        n_jobs: int,
        appended_filters: List[AbstractDataFilter] = None,
        static_attribute_path: Optional[Path] = None,
    ):
        filters: List[AbstractDataFilter] = []
        if appended_filters is not None:
            print(
                f"WARNING: Using additional non standard "
                f"preceding filters for the data loading: {appended_filters}"
            )
            filters.extend(appended_filters)
        filters.append(KeepAttributesDataFilter(self.relevant_attributes))
        if static_attribute_path:
            filters += [StaticInfoboxTemplateDataAdder(static_attribute_path)]

        self.data = get_data(
            input_path, n_files=n_files, n_jobs=n_jobs, filters=filters  # type: ignore
        )
        self.data["value_valid_from"] = self.data["value_valid_from"].dt.tz_localize(
            None
        )
        self.data["key"] = list(
            zip(*(self.data[group_key] for group_key in self.group_key))
        )

    def fit_model(self):
        print("Start training.")
        start = time.time()
        train_data = self.data[self.data["value_valid_from"] < self.test_start_date]
        self.predictor.fit(train_data.copy(), self.test_start_date, self.group_key)
        end = time.time()
        print(f"Finished training. Time elapsed: {timedelta(seconds=end - start)}")

    def test_model(
        self,
        randomize: bool = False,
        predict_subset: float = 1.0,
        save_results: bool = False,
        generate_summary: bool = True,
    ) -> Optional[str]:
        keys = self._initialize_keys(randomize, predict_subset)
        all_day_labels = []

        (
            test_dates,
            test_dates_with_testing_timeframes,
        ) = self._calculate_test_date_metadata()

        predictions: List[List[List[bool]]] = [[] for _ in self.testing_timeframes]
        # it's ok to discard the time and only retain the date
        # since there is only one change per day.
        try:
            self.data["value_valid_from"] = self.data["value_valid_from"].dt.date
        except AttributeError:
            pass
        columns = self.data.columns.tolist()
        num_columns = len(columns)
        value_valid_from_column_idx = columns.index("value_valid_from")
        key_column_idx = columns.index("key")
        key_map = {
            key: np.array(list(group))
            for key, group in itertools.groupby(
                self.data.to_numpy(), lambda x: x[key_column_idx]
            )
        }

        progress_bar_it = tqdm(keys)
        for n_processed_keys, key in enumerate(progress_bar_it):
            current_data, additional_current_data = self._select_current_data(
                key, key_map, value_valid_from_column_idx, num_columns
            )

            timestamps = current_data[:, value_valid_from_column_idx]
            additional_timestamps = additional_current_data[
                :, value_valid_from_column_idx
            ]

            current_page_predictions = self._make_prediction(
                current_data,
                timestamps,
                additional_current_data,
                additional_timestamps,
                test_dates_with_testing_timeframes,
                columns,
            )
            # save labels and predictions
            for i, prediction in enumerate(current_page_predictions):
                predictions[i].append(prediction)
            timestamps_set = set(timestamps)
            day_labels = [test_date in timestamps_set for test_date in test_dates]
            all_day_labels.append(day_labels)
        self._reformat_preds_and_labels(predictions, all_day_labels)
        run_statistics = None
        if np.any(all_day_labels):
            if generate_summary:
                run_statistics = self._evaluate_predictions()
        else:
            print("Results could not be generated. No changes in the test timeframe.")
        self.run_results["keys"] = keys

        if run_statistics or save_results:
            output_folder = result_directory(self.run_id)
            output_folder.mkdir(parents=True, exist_ok=True)
        if run_statistics:
            self._save_run_stats(output_folder, run_statistics)
        if save_results:
            self._save_run_results(output_folder)
        return run_statistics

    def generate_plots(self, run_results: Optional[dict] = None) -> None:
        print("Starting generating plots.")
        start = time.time()

        if not run_results:
            run_results = self.run_results
        labels = run_results["labels"]
        predictions = run_results["predictions"]
        keys = run_results["keys"]
        output_folder = plot_directory(self.run_id)
        output_folder.mkdir(parents=True, exist_ok=True)

        train_data = self.data[
            self.data["value_valid_from"] < self.test_start_date.date()
        ]

        evaluation_methods = ALL_EVAL_METHODS
        for evaluation_method in evaluation_methods:
            try:
                evaluation_method(  # type: ignore
                    labels,
                    predictions,
                    self.testing_timeframes,
                    output_folder,
                    keys,
                    train_data,
                )
            except Exception:
                print(f"{evaluation_method.__name__} failed.")

        end = time.time()
        print(f"Finished evaluation. Time elapsed: {timedelta(seconds=end - start)}")

    def _initialize_keys(self, randomize: bool, predict_subset: float):
        keys = self.data["key"].unique()
        if randomize:
            np.random.shuffle(keys)
        if predict_subset < 1:
            print(f"Predicting only {predict_subset:.2%} percent of the data.")
            subset_idx = math.ceil(len(keys) * predict_subset)
            keys = keys[:subset_idx]
        return keys

    def _calculate_test_date_metadata(
        self,
    ) -> Tuple[List[date], List[Tuple[date, List[Tuple[int, date, int]]]]]:
        test_dates = [
            (self.test_start_date + timedelta(days=x)).date()
            for x in range(self.test_duration)
        ]

        # precalculate all predict day, week, month, year entries to reuse them
        test_dates_with_testing_timeframes = []
        for days_evaluated, first_day_to_predict in enumerate(test_dates):
            curr_testing_timeframes = []
            for idx, timeframe in enumerate(self.testing_timeframes):
                if (
                    days_evaluated % timeframe == 0
                    and days_evaluated + timeframe <= self.test_duration
                ):
                    prediction_end_date = first_day_to_predict + timedelta(
                        days=timeframe
                    )
                    curr_testing_timeframes.append(
                        (timeframe, prediction_end_date, idx)
                    )
            test_dates_with_testing_timeframes.append(
                (first_day_to_predict, curr_testing_timeframes)
            )
        # test_dates_with_testing_timeframes has the format:
        # first_date, [timeframe, end_date, timeframe_idx]
        return test_dates, test_dates_with_testing_timeframes

    def _evaluate_predictions(
        self,
    ) -> str:
        prediction_output = ""
        print("Starting evaluation.")
        start = time.time()

        prediction_stats = []
        pred_stats = []
        # needs to be commented out if working with probabilities as predictions
        for y_true, y_hat, title in zip(
            self.run_results["labels"],
            self.run_results["predictions"],
            self.timeframe_labels,
        ):
            pred_stats.append(
                {
                    "prec_recall": evaluate_prediction(y_true, y_hat),
                    "y_hat": y_hat,
                    "y_true": y_true,
                }
            )
            prediction_stats.append(create_prediction_output(y_true, y_hat, title))

        prediction_output = "\n\n".join(prediction_stats)

        self.pred_stats = pred_stats

        end = time.time()
        print(f"Finished evaluation. Time elapsed: {timedelta(seconds=end - start)}")

        return prediction_output

    def _reformat_preds_and_labels(
        self, predictions: List[List[List[bool]]], day_labels: List[List[bool]]
    ):

        for pred in predictions[0][0]:
            if type(pred) == float:
                predictions = [
                    np.array(prediction, dtype=float) for prediction in predictions
                ]
                break
            elif type(pred) == bool:
                predictions = [
                    np.array(prediction, dtype=bool) for prediction in predictions
                ]
                break
        all_day_labels = np.array(day_labels, dtype=np.bool)
        labels = [
            self._aggregate_labels(all_day_labels, timeframe)
            for timeframe in self.testing_timeframes
        ]
        self.run_results["labels"] = labels
        self.run_results["predictions"] = predictions

    @staticmethod
    def _get_data_until(
        data: np.ndarray, timestamps: np.ndarray, timestamp: date
    ) -> np.ndarray:
        if len(data) > 0:
            offset = bisect_left(timestamps, timestamp)
            return data[:offset]
        else:
            return data

    def _make_prediction(
        self,
        current_data: np.ndarray,
        timestamps: np.ndarray,
        related_current_data: np.ndarray,
        additional_timestamps: np.ndarray,
        test_dates_with_testing_timeframes: List[
            Tuple[date, List[Tuple[int, date, int]]]
        ],
        columns: List[str],
    ) -> List[List[bool]]:
        current_page_predictions: List[List[bool]] = [
            [] for _ in self.testing_timeframes
        ]
        for (
            first_day_to_predict,
            curr_testing_timeframes,
        ) in test_dates_with_testing_timeframes:
            property_to_predict_data = self._get_data_until(
                current_data, timestamps, first_day_to_predict
            )
            for timeframe, prediction_end_date, idx in curr_testing_timeframes:
                related_property_to_predict_data = self._get_data_until(
                    related_current_data,
                    additional_timestamps,
                    prediction_end_date,
                )
                current_page_predictions[idx].append(
                    self.predictor.predict_timeframe(
                        property_to_predict_data,
                        related_property_to_predict_data,
                        columns,
                        first_day_to_predict,
                        timeframe,
                    )
                )
        return current_page_predictions

    def _select_current_data(
        self,
        key: Tuple,
        key_map: Dict[Any, np.ndarray],
        value_valid_from_column_idx: int,
        num_columns: int,
    ) -> Tuple[np.ndarray, np.ndarray]:
        current_data = key_map[key]
        relevant_keys = self.predictor.get_relevant_ids(key).copy()
        relevant_keys = [
            key for key in filter(key.__ne__, relevant_keys) if key in key_map
        ]
        if len(relevant_keys) != 0:
            additional_current_data_list = [
                key_map[relevant_key] for relevant_key in relevant_keys
            ]
            additional_current_data = np.concatenate(additional_current_data_list)
            additional_current_data = additional_current_data[
                additional_current_data[:, value_valid_from_column_idx].argsort()
            ]
            return current_data, additional_current_data
        return current_data, np.empty((0, num_columns))

    def _aggregate_labels(self, labels: np.ndarray, n: int) -> np.ndarray:
        if n == 1:
            return labels
        if self.test_duration % n != 0:
            cut_labels = labels[:, : -(self.test_duration % n)]
        else:
            cut_labels = labels
        cut_labels = cut_labels.reshape((labels.shape[0], -1, n))
        return np.any(cut_labels, axis=2)

    @staticmethod
    def _save_run_stats(output_folder: Path, run_statistics: str) -> None:
        run_stats_path = output_folder / "stats.txt"
        with open(run_stats_path, "w") as f:
            f.write(run_statistics)

    def _save_run_results(self, output_folder: Path):
        run_results_path = output_folder / "results.pickle"
        with open(run_results_path, "wb") as f:
            pickle.dump(self.run_results, f)

if __name__ == "__main__":
    n_files = 2
    n_jobs = 4
    input_path = Path(
        "/run/media/secret/manjaro-home/secret/mp-data/custom-format-default-filtered"
    )
    input_path = Path("../../data/custom-format-default-filtered")

    model1 = PropertyCorrelationPredictor()
    model2 = RandomForestPredictor()
    framework = TrainAndPredictFramework(model2, ["infobox_key", "property_name"])
    # framework = TrainAndPredictFramework(model, ["page_id"])
    framework.load_data(input_path, n_files, n_jobs, [FeatureAdderFilter()])
    framework.fit_model()
    framework.test_model(predict_subset=0.1)
    framework.generate_plots()


import os
import csv

cd1 = "C:/Users/jorda/Desktop/Python/users_answers/"
cd2 = "C:/Users/jorda/Desktop/Python/files/"
ganesh = "Ganesh_template.csv"

user_amount = []

#reading user_amount:
#nog niet AF:
with open('./user_amount.csv', 'r') as amountfile:
    reader = csv.reader(amountfile)
    for row in reader:
        user_amount.append(row)
    amountfile.close()

print(user_amount)

with open('./files/{}'.format(ganesh), 'r') as file:
    reader1 = csv.reader(file)
    header_ganesh = next(reader1)
    print("Merging the answers of users....")

print("Merging the answers of users....")

with open("Merged.csv", "w", newline='') as output:
    writer = csv.writer(output)
    writer.writerow(header_ganesh)
    for filename in os.listdir("./user_answers"):
        print(filename)
        with open('./user_answers/{}'.format(filename), 'r') as file:
            reader = csv.reader(file)
            if next(reader) != None:
                for row in reader:
                    writer.writerow(row)
    output.close()

print("Merging the answers of users and corresponding amount....")
with open("Merged_amount.csv", "w", newline='') as output:
    writer = csv.writer(output)
    writer.writerow(header_ganesh)

    for filename in os.listdir("./user_answers"):
#        print(filename)
#        print(user_amount[teller][0])
#        print("Teller is", teller)
        for i in range(len(user_amount)):
            with open('./user_answers/{}'.format(filename), 'r') as file:
                print("File successfully opened")
                if filename == user_amount[i][0]+".csv":
                    reader = csv.reader(file)
                    if next(reader) != None:
                        print("se")
                        for row in reader:
                            for i in range(int(user_amount[i][1])):

                                writer.writerow(row)
                        print("All rows written\n")
                    else:
                        print("No")

    output.close()


from typing import List

import numpy as np
import torch

from ...utils.record import AverageMeter
from ..base import BaseMetric

IGNORE_INDEX_NONE_VALUE = -100

class SegmentationMetric(BaseMetric):
    metric_names: List[str] = ['iou', 'pixel_acc']
    primary_metric: str = 'iou'

    def __init__(self, num_classes=None, ignore_index=IGNORE_INDEX_NONE_VALUE):
        super().__init__()
        self.ignore_index = ignore_index if ignore_index is not None else IGNORE_INDEX_NONE_VALUE
        self.K = num_classes

    def intersection_and_union_gpu(self, output, target):

        # 'K' classes, output and target sizes are N or N * L or N * H * W, each value in range 0 to K - 1.
        assert (output.dim() in [1, 2, 3])

        assert output.shape == target.shape
        output = output.view(-1)
        target = target.view(-1)
        output[target == self.ignore_index] = self.ignore_index
        intersection = output[output == target]
        area_intersection = torch.histc(intersection, bins=self.K, min=0, max=self.K-1)
        area_output = torch.histc(output, bins=self.K, min=0, max=self.K-1)
        area_target = torch.histc(target, bins=self.K, min=0, max=self.K-1)
        area_union = area_output + area_target - area_intersection

        intersection, union, target, output = \
            area_intersection.cpu().numpy(), area_union.cpu().numpy(), area_target.cpu().numpy(), area_output.cpu().numpy()

        return {
            'intersection': intersection,
            'union': union,
            'target': target,
            'output': output
        }

    def calibrate(self, pred, target, **kwargs):
        result_dict = {k: AverageMeter(k) for k in self.metric_names}
        B = pred.size(0)

        metrics = self.intersection_and_union_gpu(pred, target)
        result_dict['iou'].update(sum(metrics['intersection']) / (sum(metrics['union']) + 1e-10), n=B)
        result_dict['pixel_acc'].update(sum(metrics['intersection']) / (sum(metrics['target']) + 1e-10), n=B)

        return {k: v.avg for k, v in result_dict.items()}


'''
Demonstrates two numerical integration methods (trapezoid and Simpson) and compares
them to values calculated with SymPy.
'''
from math import pi
import sympy as sym
import numpy as np
import matplotlib.pyplot as plt

def integrate_trapezoid(f, a, b, n):
    '''
    Numerical integration of definite integral from a to b of function f.
    Uses trapezoid rule (große Trapezregel), dividing the range [a, b]
    into n equal intervals.

    Parameters
    ----------
    f : function
        The function to be integrated.
    a : number
        Lower bound of integral.
    b : number
        Upper bound of integral.
    n : number
        Count of intervals into which [a, b] is divided for approximation.

    Returns
    -------
    number
        Integral as approximated value.
    '''
    x_sigma_sum = np.arange(a, b, (b-a)/n)[1:]
    sigma_sum = sum(list(map(f, x_sigma_sum)))
    return ((f(a) + f(b))/2 + sigma_sum) * (b - a) / n

def integrate_simpson(f, a, b, n):
    '''
    Numerical integration of definite integral from a to b of function f.
    Uses Simpson formula (große Simpsonformel), dividing the range [a, b]
    into n*2 equal intervals.

    Parameters
    ----------
    f : function
        The function to be integrated.
    a : number
        Lower bound of integral.
    b : number
        Upper bound of integral.
    n : number
        Controls precision of approximation. The range [a, b] is divided into 2*n intervals.

    Returns
    -------
    number
        Integral as approximated value.
    '''
    x_odd_indexes = np.arange(a, b, (b-a)/(2*n))[1::2]
    sum_x_odd_indexes = sum(list(map(f, x_odd_indexes)))

    x_even_indexes = np.arange(a, b, (b-a)/(2*n))[0::2][1:]
    sum_x_even_indexes = sum(list(map(f, x_even_indexes)))

    return (f(a) + f(b) + 2*sum_x_even_indexes + 4*sum_x_odd_indexes) * (b - a) / (6*n)

def plot_f(title, f_sympy, f, lower_bound, upper_bound, margin=0):
    '''
    Helper function for plotting.
    '''
    x_axis = np.linspace(lower_bound - margin,
                         upper_bound + margin, int(margin * 100))
    plt.title(title)
    plt.plot(x_axis, f(x_axis), color='blue', linewidth=2, label=str(f_sympy))
    plt.grid(True)
    section = np.linspace(lower_bound, upper_bound, 100)
    plt.fill_between(section, f(section), color='red', alpha=0.2)
    plt.legend()
    return plt

def input_number(prompt, default=1, minimum=0, maximum=1000):
    '''
    Helper function for reading a number from standard input.
    '''
    number_incomplete = True
    while number_incomplete:
        number_string = input(prompt + ' [' + str(default) + ']: ').strip()
        if not number_string:
            number_string = str(default)
        try:
            number_value = float(number_string)
            if number_value < minimum:
                print(number_value, 'is below minimum', minimum)
            elif number_value > maximum:
                print(number_value, 'is above maximum', maximum)
            else:
                number_incomplete = False
        except (ValueError) as bad_number_string:
            print(bad_number_string)
            continue
    return number_value

def parabola(comment, f_integrate, n, f_parabola):
    '''
    Helper function for calculating and printing the definite integral of a parabola.
    '''
    print('{0} {1}: '.format(comment, n),
          f_integrate(f_parabola, -1, 1, n))

def torus(comment, f_integrate, n, f_concave, f_convex):
    '''
    Helper function for calculating and printing the volume of a torus.
    '''
    print('{0} {1}: '.format(comment, n),
          pi * f_integrate(f_concave, -r, r, n)
          - pi * f_integrate(f_convex, -r, r, n))

x = sym.symbols('x')

########################################################################
# A parabola as a simple case for testing.
########################################################################
f_parabola_sympy = -x**2 + 1
f_parabola = sym.lambdify(x, f_parabola_sympy, "numpy")

parabola_integral_sympy = sym.integrate(f_parabola_sympy, (x, -1, 1))

title = 'Area under curve {0} from -1 to 1'.format(f_parabola_sympy)
print(' ')
print(title)
print('-'*len(title))

print('SymPy: ', parabola_integral_sympy)

n_values_trapezoid = [4, 10, 80, 400]
list(map(lambda x: parabola('Parabola Trapezoid', integrate_trapezoid, x, f_parabola),
         n_values_trapezoid))

n_values_simpson = [2, 5, 40, 200]
list(map(lambda x: parabola('Parabola Simpson', integrate_simpson, x, f_parabola),
         n_values_simpson))

plot_f('Area under curve with SymPy: ' +
       str(parabola_integral_sympy), f_parabola_sympy, f_parabola, -1, 1, 0.5)
plt.show()

########################################################################
# Torus volume (solid of revolution)
########################################################################
print(' ')
r = input_number('Enter Torus Minor Radius r', 0.5, 0.1, 10)
R = input_number('Enter Torus Major Radius R', 4, 1, 20)

plt.figure('Two Solids of Revolution for Calculating a Torus Volume')

# First solid of revolution is defined by a concave semicircle.
f_semicircle_concave_sympy = sym.sqrt(r**2 - x**2) + R
f_semicircle_concave = sym.lambdify(x, f_semicircle_concave_sympy, "numpy")

volume_semicircle_concave_sympy = pi * \
    sym.integrate(f_semicircle_concave_sympy**2, (x, -r, r))

plt.subplot(1, 2, 1, title='Semicircle Concave')
plt.axis('equal')
plt.ylim(-0.5, R*1.3)
plot_f('Volume: ' + str(volume_semicircle_concave_sympy),
       f_semicircle_concave_sympy, f_semicircle_concave, -r, r)

# Second solid of revolution is defined by a convex semicircle.
f_semicircle_convex_sympy = -sym.sqrt(r**2 - x**2) + R
f_semicircle_convex = sym.lambdify(x, f_semicircle_convex_sympy, "numpy")

volume_semicircle_convex_sympy = pi * \
    sym.integrate(f_semicircle_convex_sympy**2, (x, -r, r))

plt.subplot(1, 2, 2, title='Semicircle Convex')
plt.axis('equal')
plt.ylim(-0.5, R*1.3)
plot_f('Volume: ' + str(volume_semicircle_convex_sympy),
       f_semicircle_convex_sympy, f_semicircle_convex, -r, r)

title = 'Volume of Torus with r={0} R={1}'.format(r, R)
print(' ')
print(title)
print('-'*len(title))

f_semicircle_concave_squared = sym.lambdify(
    x, f_semicircle_concave_sympy**2, "numpy")
f_semicircle_convex_squared = sym.lambdify(
    x, f_semicircle_convex_sympy**2, "numpy")

print('SymPy: ', volume_semicircle_concave_sympy - volume_semicircle_convex_sympy)

# Calculate and print torus volume with trapezoid method for n from 2^2 to 2^15.
n_values_trapezoid = list(map(lambda x: 2**x, np.arange(2, 16)))  # 2^2 to 2^15
list(map(lambda x: torus('Trapezoid', integrate_trapezoid, x,
                         f_semicircle_concave_squared, f_semicircle_convex_squared),
         n_values_trapezoid))

# Calculate and print torus volume with Simpson method for n from 2^1 to 2^14.
n_values_simpson = list(map(lambda x: 2**x, np.arange(1, 15)))  # 2^1 to 2^14
list(map(lambda x: torus('Simpson', integrate_simpson, x,
                         f_semicircle_concave_squared, f_semicircle_convex_squared),
         n_values_simpson))

plt.show()


from win32com.client import Dispatch

speak = Dispatch("SAPI.SpVoice").Speak
print("Hii, I'm your Dev Speaker")

while True:
    inp = input("What shall I speak for you: ")
    if inp == 'bye':
        speak("Bye buddy")
        break
    speak(inp)


"""
This goes through your Plex_ TV library, on your *local* server, and tries to *shrink* episodes (using HEVC_ video quality 28) that start off with a bit rate *above* a certain level, by default 2000 kbps.

Lots of words, huh?

This can *either* tell a table of TV show data on your locally running Plex_ server, on your selected library. Each row is information about a TV show on the Plex_ library; columns are TV show name, and number of episodes above the default bit rate. Rows are organized from *most* number of episodes to fewest, and only includes those TV shows that have :math:`\ge 1` high bitrate episodes.

.. _Plex: https://plex.tv
.. _HEVC: https://en.wikipedia.org/wiki/High_Efficiency_Video_Coding
"""
import os, sys, logging, time, pandas, numpy, json, subprocess, shutil, re, redis
from enum import Enum
from howdy.core import core, session
from howdy.tv import tv, get_token, tv_attic, get_tvdb_api, TMDBShowIds
from howdy.music import music
from howdy.movie import tmdb_apiKey
from pathos.multiprocessing import Pool, cpu_count
from tabulate import tabulate
from shutil import which
from argparse import ArgumentParser

_MINBITRATE   = 1000
_ffmpeg_exec  = which( 'ffmpeg' )
_ffprobe_exec = which( 'ffprobe' )
assert( _ffmpeg_exec is not None )
assert( _ffprobe_exec is not None )

def get_tv_library_local( library_name = 'TV Shows' ):
    fullURL, token = core.checkServerCredentials( doLocal=True )
    library_names = list( core.get_libraries( token = token ).values( ) )
    assert( library_name in library_names )
    #
    tvdata = core.get_library_data(
        library_name, token = token, num_threads = cpu_count( ) )
    return tvdata

class DATAFORMAT( Enum ):
    IS_AVI_OR_MPEG = 1
    IS_LATER = 2

    @classmethod
    def check_format( cls, fullpath ):
        basename = os.path.basename( fullpath ).lower( )
        if any( map(lambda suffix: basename.endswith( '.%s' % suffix ), ( 'avi', 'mpg', 'mpeg' ) ) ):
            return DATAFORMAT.IS_AVI_OR_MPEG
        return DATAFORMAT.IS_LATER

def get_all_durations_dataframe( tvdata, min_bitrate = 2000, mode_dataformat = DATAFORMAT.IS_LATER ):
    if mode_dataformat == DATAFORMAT.IS_LATER: assert( min_bitrate >= _MINBITRATE )
    sizes = [ ]
    durations = [ ]
    shows = [ ]
    seasons = [ ]
    epnos = [ ]
    names = [ ]
    paths = [ ]

    for show in tvdata:
        for seasno in tvdata[show]['seasons']:
            for epno in tvdata[show]['seasons'][seasno]['episodes']:
                mypath = tvdata[show]['seasons'][seasno]['episodes'][epno]['path']
                if mode_dataformat != DATAFORMAT.check_format( mypath ):
                    continue
                durations.append( tvdata[show]['seasons'][seasno]['episodes'][epno]['duration'])
                sizes.append( tvdata[show]['seasons'][seasno]['episodes'][epno]['size'] )
                shows.append( show )
                seasons.append( seasno )
                epnos.append( epno )
                names.append( tvdata[show]['seasons'][seasno]['episodes'][epno]['title'] )
                paths.append( mypath )
    df = pandas.DataFrame({'sizes (MB)' : numpy.array(sizes)/1024**2, 'durations (s)' : durations,
                           'shows' : shows, 'seasons' : seasons, 'epnos' : epnos, 'names' : names,
                          'paths' : paths })
    df['bitrate (kbps)'] = numpy.array( df['sizes (MB)'] ) / numpy.array( df['durations (s)']) * 1024 * 8
    df_sub = df[ (df['bitrate (kbps)'] > min_bitrate ) ].sort_values('bitrate (kbps)', ascending = True).copy( )
    return df_sub

def summarize_shows_dataframe( df_sub ):
    def _get_num_shows( showname ):
        return {
            'num shows' : len( df_sub[ df_sub.shows == showname ] ),
            'min kbps' : df_sub[ df_sub.shows == showname ]['bitrate (kbps)'].min( ),
            'med kbps' : numpy.median( df_sub[ df_sub.shows == showname ]['bitrate (kbps)'] ),
            'max kbps' : df_sub[ df_sub.shows == showname ]['bitrate (kbps)'].max( ),
            }
    dict_of_shows = dict(map(lambda showname: ( showname, _get_num_shows( showname ) ),
                             set( df_sub.shows ) ) )
    df = pandas.DataFrame( {
        'shows' : sorted( dict_of_shows ),
        'num episodes' : list(map(lambda showname: dict_of_shows[ showname ][ 'num shows' ], sorted( dict_of_shows ) ) ),
        'min kbps'     : list(map(lambda showname: dict_of_shows[ showname ][ 'min kbps'  ], sorted( dict_of_shows ) ) ),
        'med kbps'     : list(map(lambda showname: dict_of_shows[ showname ][ 'med kbps'  ], sorted( dict_of_shows ) ) ),
        'max kbps'     : list(map(lambda showname: dict_of_shows[ showname ][ 'max kbps'  ], sorted( dict_of_shows ) ) ),
        } )
    df = df.sort_values( 'num episodes', ascending = False ).copy( )
    return df

def single_show_summary_dataframe( df_sub, showname, mode_dataformat = DATAFORMAT.IS_LATER ):
    df_show = df_sub[ df_sub.shows == showname ].copy( )
    assert( len( df_show ) != 0 )
    if mode_dataformat == DATAFORMAT.IS_AVI_OR_MPEG:
        return df_show
    #
    def get_ffprobe_json( filename ):
        stdout_val = subprocess.check_output(
            [ _ffprobe_exec, '-v', 'quiet', '-show_streams',
             '-show_format', '-print_format', 'json', filename ],
            stderr = subprocess.STDOUT )
        file_info = json.loads( stdout_val )
        return file_info

    def is_hevc( filename ):
        data = get_ffprobe_json( filename )
        return data['streams'][0]['codec_name'].lower( ) == 'hevc'

    with Pool( processes = min( cpu_count( ), len( df_show ) ) ) as pool:
        dict_of_episodes_hevc = dict( pool.map(
            lambda filename: ( filename, is_hevc( filename ) ),
            list( df_show.paths ) ) )
        df_show[ 'is hevc' ] = list(map(lambda filename: dict_of_episodes_hevc[ filename ],
                                        list( df_show.paths ) ) )
        return df_show

def summarize_single_show( df_sub, showname, minbitrate, mode_dataformat = DATAFORMAT.IS_LATER ):
    df_show = single_show_summary_dataframe( df_sub, showname, mode_dataformat = mode_dataformat )
    #
    ## now report it out
    print( 'information for %s, which has %d episodes >= %d kbps.\n' % ( showname, len( df_show ), minbitrate ) )
    data = [ [ 'NUM EPISODES', len( df_show ) ], ]
    data.append( [ 'MIN KBPS', df_show[ 'bitrate (kbps)' ].min( ) ] )
    data.append( [ 'MED KBPS', numpy.median( df_show[ 'bitrate (kbps)' ] ) ] )
    data.append( [ 'MAX KBPS', df_show[ 'bitrate (kbps)' ].max( ) ] )
    if mode_dataformat == DATAFORMAT.IS_AVI_OR_MPEG:
        print( '%s\n' % tabulate( data, headers = [ 'PARAMETER', 'INFO' ] ) )
        return
    #
    if len( df_show[ df_show[ 'is hevc' ] == True ] ) == 0:
        print( '%s\n' % tabulate( data, headers = [ 'PARAMETER', 'INFO' ] ) )
        return
    #
    ## now HEVC episodes
    df_show_ishevc = df_show[ df_show[ 'is hevc' ] == True ].copy( )
    data.append( [ 'NUM HEVC EPISODES', len( df_show_ishevc ) ] )
    data.append( [ 'MIN KBPS HEVC', df_show_ishevc[ 'bitrate (kbps)' ].min( ) ] )
    data.append( [ 'MED KBPS HEVC', numpy.median( df_show_ishevc[ 'bitrate (kbps)' ] ) ] )
    data.append( [ 'MAX KBPS HEVC', df_show_ishevc[ 'bitrate (kbps)' ].max( ) ] )
    print( '%s\n' % tabulate( data, headers = [ 'PARAMETER', 'INFO' ] ) )

def process_single_show( df_sub, showname, do_hevc = True, qual = 28, output_json_file = 'processed_stuff.json' ):
    #
    ## check we have nice and HandBrakeCLI
    nice_exec = which( 'nice' )
    hcli_exec = which( 'HandBrakeCLI' )
    assert( nice_exec is not None )
    assert( hcli_exec is not None )
    #
    df_show = single_show_summary_dataframe( df_sub, showname )
    df_show_sub = df_show.copy( )
    if not do_hevc:
        df_show_sub = df_show[ df_show[ 'is hevc' ] == False ].copy( )
    #
    ## now process those shows SLOWLY
    time00 = time.perf_counter( )
    df_show_sorted = df_show_sub.sort_values(by=['seasons', 'epnos']).reset_index( )
    episodes_sorted = list( df_show_sorted.paths )
    list_processed = [
        "found %02d files to dehydrate in '%'." % (
            len( episodes_sorted ), showname ), ]
    json.dump( list_processed, open( output_json_file, 'w' ), indent = 1 )
    for idx, filename in enumerate(episodes_sorted):
        time0 = time.perf_counter( )
        newfile = os.path.basename( filename )
        stdout_val = subprocess.check_output([
            nice_exec, '-n', '19', hcli_exec,
            '-i', filename, '-e', 'x265', '-q', '%d' % qual, '-E', 'av_aac', '-B', '160',
            '-a', ','.join(map(lambda num: '%d' % num, range(1,35))),
            '-s', ','.join(map(lambda num: '%d' % num, range(1,35))),
            '-o', newfile ],
            stderr = subprocess.PIPE )
        #
        os.chmod(newfile, 0o644 )
        shutil.move( newfile, filename )
        dt0 = time.perf_counter( ) - time0
        print('processed episode %02d / %02d in %0.3f seconds' % (
            idx + 1, len( episodes_sorted ), dt0 ) )
        list_processed.append( 'processed episode %02d / %02d in %0.3f seconds' % (
            idx + 1, len( episodes_sorted ), dt0 ) )
        json.dump( list_processed, open( output_json_file, 'w' ), indent = 1 )
    dt00 = time.perf_counter( ) - time00
    print( 'took %0.3f seconds to process %d episodes' % (
        dt00, len( episodes_sorted ) ) )
    list_processed.append( 'took %0.3f seconds to process %d episodes' % (
        dt00, len( episodes_sorted ) ) )
    json.dump( list_processed, open( output_json_file, 'w' ), indent = 1 )

def process_single_show_avi( df_sub, showname, qual = 20, output_json_file = 'processed_stuff_avi.json' ):
    #
    ## check we have nice and HandBrakeCLI
    nice_exec = which( 'nice' )
    hcli_exec = which( 'HandBrakeCLI' )
    assert( nice_exec is not None )
    assert( hcli_exec is not None )
    #
    df_show_sub = single_show_summary_dataframe(
        df_sub, showname, mode_dataformat = DATAFORMAT.IS_AVI_OR_MPEG )
    #
    ## now process those shows SLOWLY
    time00 = time.perf_counter( )
    df_show_sorted = df_show_sub.sort_values(by=['seasons', 'epnos']).reset_index( )
    episodes_sorted = list( df_show_sorted.paths )
    list_processed = [
        "found %02d files to deavify in '%'." % (
            len( episodes_sorted ), showname ), ]
    json.dump( list_processed, open( output_json_file, 'w' ), indent = 1 )
    #
    ## first go through redis database to see if shows are running
    for idx, filename in enumerate(episodes_sorted):
        time0 = time.perf_counter( )
        dirname = os.path.dirname( filename )
        newfile = re.sub( '\.avi$', '.mkv', os.path.basename( filename ) )
        try:
            stdout_val = subprocess.check_output([
                nice_exec, '-n', '19', hcli_exec,
                '-i', filename, '-e', 'x265', '-q', '%d' % qual, '-B', '160',
                '-a', ','.join(map(lambda num: '%d' % num, range(1,35))),
                '-o', newfile ],
                stderr = subprocess.PIPE )
            #
            os.chmod(newfile, 0o644 )
            shutil.move( newfile, dirname )
            os.remove( filename )
            dt0 = time.perf_counter( ) - time0
            print('processed episode %02d / %02d in %0.3f seconds' % (
                idx + 1, len( episodes_sorted ), dt0 ) )
            list_processed.append( 'processed episode %02d / %02d in %0.3f seconds' % (
                idx + 1, len( episodes_sorted ), dt0 ) )
        except:
            try: os.remove( newfile )
            except: pass
            dt0 = time.perf_counter( ) - time0
            print('failed episode %02d / %02d in %0.3f seconds' % (
                idx + 1, len( episodes_sorted ), dt0 ) )
            list_processed.append( 'failed episode %02d / %02d in %0.3f seconds' % (
                idx + 1, len( episodes_sorted ), dt0 ) )
        json.dump( list_processed, open( output_json_file, 'w' ), indent = 1 )
    dt00 = time.perf_counter( ) - time00
    print( 'took %0.3f seconds to process %d episodes' % (
        dt00, len( episodes_sorted ) ) )
    list_processed.append( 'took %0.3f seconds to process %d episodes' % (
        dt00, len( episodes_sorted ) ) )
    json.dump( list_processed, open( output_json_file, 'w' ), indent = 1 )

def main( ):
    parser = ArgumentParser( )
    #
    ## top level arguments
    parser.add_argument( '-t', '--tvlibrary', dest='tvlibrary', type=str, action = 'store', default = 'TV Shows',
                        help = 'Name of the TV library on the local PLEX server. Default is "TV Shows".' )
    parser.add_argument( '-M', '--minbitrate', dest = 'minbitrate', type = int, action = 'store', default = 2000,
                        help = ' '.join([
                            'The minimum total bitrate (in kbps) of episodes to dehydrate. Must be >= %d kbps.' % _MINBITRATE,
                            'Default is 2000 kbps.']))
    parser.add_argument( '--info', dest='do_info', action='store_true', default = False,
                        help = 'If chosen, then turn on INFO logging.' )
    #
    ## check on which TV shows are candidates for dehydration, or to dehydrate specific TV shows.
    subparsers = parser.add_subparsers( help = 'Choose on whether to list the TV shows to dehydrate, or to dehydrate a TV show.',
                                       dest = 'choose_option' )
    parser_listcandidates = subparsers.add_parser( 'list',      help = 'If chosen, then list the TV shows to dehydrate.' )
    parser_dehydrate      = subparsers.add_parser( 'dehydrate', help = 'If chosen, then dehydrate a single TV show.' )
    #
    ## list TV shows, do nothing more
    #
    ## dehydrate a single TV show
    parser_dehydrate.add_argument( '-s', '--show', metavar = 'SHOW', dest = 'parser_dehydrate_show', type = str, action = 'store',
                                  required = True, help = 'Name of a TV show (with episodes to dehydrate).' )
    parser_dehydrate.add_argument( '-I', dest = 'parser_dehydrate_do_info', action = 'store_true', default = False,
                                  help = 'If chosen, then only print out info on the selected TV show.' )
    parser_dehydrate.add_argument( '-Q', '--quality', dest = 'parser_dehydrate_quality', metavar = 'QUALITY', type = int, action = 'store',
                                  default = 28, help = 'Will dehydrate shows using HEVC video codec with this quality. Default is 28. Must be >= 20.' )
    parser_dehydrate.add_argument( '-N', '--nohevc', dest = 'parser_dehydrate_do_hevc', action = 'store_false', default = True,
                                  help = 'If chosen, then only process the big episodes that are NOT HEVC. Default is to process everything.' )
    parser_dehydrate.add_argument( '-J', '--jsonfile', dest = 'parser_dehydrate_jsonfile', metavar = 'JSONFILE', type = str, action = 'store', default = 'processed_stuff.json',
                                  help = 'Name of the JSON file to store progress-as-you-go-along on TV show dehydration. Default file name = "processed_stuff.json".' )
    #
    ## parsing arguments
    time0 = time.perf_counter( )
    args = parser.parse_args( )
    assert( args.minbitrate >= _MINBITRATE )
    logger = logging.getLogger( )
    if args.do_info: logger.setLevel( logging.INFO )
    df_sub = get_all_durations_dataframe(
        get_tv_library_local( library_name = args.tvlibrary ),
        min_bitrate = args.minbitrate )
    shownames = set( df_sub.shows )
    #
    ## list TV shows
    if args.choose_option == 'list':
        df_shows = summarize_shows_dataframe( df_sub )
        print( 'found %d shows and %d episodes with episodes >= %d kbps.' % (
            len( df_shows ), df_shows['num episodes'].sum( ), args.minbitrate ) )
        data = list(zip( list(df_shows.shows), list( df_shows['num episodes'] ),
                        list( df_shows[ 'min kbps' ] ), list( df_shows[ 'med kbps' ] ),
                        list( df_shows[ 'max kbps' ] ) ) )
        print( '%s\n' % tabulate( data, headers = [ 'SHOW', 'NUM EPISODES', 'MIN KBPS', 'MED KBPS', 'MAX KPBS' ] ) )
        print( 'took %0.3f seconds to process.' % ( time.perf_counter( ) - time0 ) )
        return
    #
    ## dehydrate single show
    elif args.choose_option == 'dehydrate':
        showname = args.parser_dehydrate_show
        assert( showname in shownames )
        jsonfile = os.path.expanduser( args.parser_dehydrate_jsonfile )
        assert( os.path.basename( jsonfile ).endswith( '.json' ) )
        #
        ## just do info then return
        if args.parser_dehydrate_do_info:
            summarize_single_show( df_sub, showname, args.minbitrate )
            print( 'took %0.3f seconds to process.' % ( time.perf_counter( ) - time0 ) )
            return
        #
        ## now do the big thing
        quality = args.parser_dehydrate_quality
        assert( quality >= 20 )
        process_single_show( df_sub, showname, do_hevc = args.parser_dehydrate_do_hevc,
                            qual = quality, output_json_file = jsonfile )

def main_avis( ):
    parser = ArgumentParser( )
    #
    ## top level arguments
    parser.add_argument( '-t', '--tvlibrary', dest='tvlibrary', type=str, action = 'store', default = 'TV Shows',
                        help = 'Name of the TV library on the local PLEX server. Default is "TV Shows".' )
    parser.add_argument( '--info', dest='do_info', action='store_true', default = False,
                        help = 'If chosen, then turn on INFO logging.' )
    #
    ## check on which TV shows are candidates for dehydration, or to deavify specific TV shows.
    subparsers = parser.add_subparsers( help = 'Choose on whether to list the TV shows to deavify, or to deavify a TV show, that have episodes that are AVI files.',
                                       dest = 'choose_option' )
    parser_listcandidates = subparsers.add_parser( 'list',    help = 'If chosen, then list the TV shows to deavify (ones that have AVI file episodes).' )
    parser_deavify      = subparsers.add_parser( 'deavify', help = 'If chosen, then deavify a single TV show.' )
    #
    ## list TV shows, do nothing more
    #
    ## deavify a single TV show
    parser_deavify.add_argument( '-s', '--show', metavar = 'SHOW', dest = 'parser_deavify_show', type = str, action = 'store',
                                  required = True, help = 'Name of a TV show (with episodes to deavify).' )
    parser_deavify.add_argument( '-I', dest = 'parser_deavify_do_info', action = 'store_true', default = False,
                                  help = 'If chosen, then only print out info on the selected TV show.' )
    parser_deavify.add_argument( '-Q', '--quality', dest = 'parser_deavify_quality', metavar = 'QUALITY', type = int, action = 'store',
                                  default = 21, help = 'Will deavify shows using HEVC video codec with this quality. Default is 21. Must be >= 18.' )
    parser_dehydrate.add_argument( '-J', '--jsonfile', dest = 'parser_deavify_jsonfile', metavar = 'JSONFILE', type = str, action = 'store', default = 'processed_stuff_avi.json',
                                  help = 'Name of the JSON file to store progress-as-you-go-along on TV show dehydration. Default file name = "processed_stuff_avi.json".' )
    #
    ## parsing arguments
    time0 = time.perf_counter( )
    args = parser.parse_args( )
    logger = logging.getLogger( )
    if args.do_info: logger.setLevel( logging.INFO )
    df_sub = get_all_durations_dataframe(
        get_tv_library_local( library_name = args.tvlibrary ),
        min_bitrate = 0.0, mode_dataformat = DATAFORMAT.IS_AVI_OR_MPEG )
    shownames = set( df_sub.shows )
    #
    ## list TV shows
    if args.choose_option == 'list':
        df_shows = summarize_shows_dataframe( df_sub )
        print( 'found %d shows and %d AVI episodes.' % (
            len( df_shows ), df_shows['num episodes'].sum( ) ) )
        data = list(zip( list(df_shows.shows), list( df_shows['num episodes'] ),
                        list( df_shows[ 'min kbps' ] ), list( df_shows[ 'med kbps' ] ),
                        list( df_shows[ 'max kbps' ] ) ) )
        print( '%s\n' % tabulate( data, headers = [ 'SHOW', 'NUM EPISODES', 'MIN KBPS', 'MED KBPS', 'MAX KPBS' ] ) )
        print( 'took %0.3f seconds to process.' % ( time.perf_counter( ) - time0 ) )
        return
    #
    ## deavify single show
    elif args.choose_option == 'deavify':
        showname = args.parser_deavify_show
        assert( showname in shownames )
        jsonfile = os.path.expanduser( args.parser_deavify_jsonfile )
        assert( os.path.basename( jsonfile ).endswith( '.json' ) )
        #
        ## just do info then return
        if args.parser_deavify_do_info:
            summarize_single_show( df_sub, showname, 0.0, mode_dataformat = DATAFORMAT.IS_AVI_OR_MPEG )
            print( 'took %0.3f seconds to process.' % ( time.perf_counter( ) - time0 ) )
            return
        #
        ## now the big thing
        quality = args.parser_deavify_quality
        assert( quality >= 20 )
        process_single_show_avi( df_sub, showname, qual = quality )


apt = [[15, 18, 17],
       [4, 6, 9],
       [10, 1, 3],
       [7, 8, 9],
       [15, 2, 6]]

family= list(map(int, input().split()))

def isPattern(list_1):
    global apt
    for i in range(5):
        if list_1 == apt[i]:
            print(f'{5-i}층')
isPattern(family)


from django.urls import path

from . import views

app_name = '{{cookiecutter.project_slug.lower()}}'
urlpatterns = [
    path('', views.cover, name='cover'),
    path('home/', views.home, name='home'),
    path('sobre/', views.sobre, name='sobre'),
]


class Solution:
    def minMeetingRooms(self, intervals: List[List[int]]) -> int:
        # trick is to decide when to assign a new room vs using an avaibable room
        # we can use a min heap to keep track of end time of used rooms to determine if a new room is needed for next meeting
        if len(intervals) == 0: return 0
        free_rooms = []
        intervals = sorted(intervals, key = lambda x: x[0]) # sort by start time
        heapq.heappush(free_rooms, intervals[0][1]) # start with the first room

        for i in intervals[1:]:
            # if the room on top of the queue ends before the next start
            if free_rooms[0] <= i[0]:
                heapq.heappop(free_rooms)
            heapq.heappush(free_rooms, i[1])

        return len(free_rooms)


import pytest
import semver
import warnings

import reframe
import reframe.core.runtime as rt
import reframe.core.warnings as warn
import reframe.utility.color as color
import unittests.fixtures as fixtures

@pytest.fixture(params=['colors', 'nocolors'])
def with_colors(request):
    with rt.temp_runtime(fixtures.BUILTIN_CONFIG_FILE, 'generic',
                         {'general/colorize': request.param == 'colors'}):
        yield request.param == 'colors'

def test_deprecation_warning():
    with pytest.warns(warn.ReframeDeprecationWarning):
        warn.user_deprecation_warning('deprecated')

def test_deprecation_warning_from_version():
    next_version = semver.VersionInfo.parse(reframe.VERSION).bump_minor()
    warn.user_deprecation_warning('deprecated', str(next_version))

def test_deprecation_warning_formatting(with_colors):
    message = warnings.formatwarning(
        'deprecated', warn.ReframeDeprecationWarning, 'file', 10, 'a = 1'
    )
    expected = 'file:10: WARNING: deprecated\na = 1\n'
    if with_colors:
        expected = color.colorize(expected, color.YELLOW)

    assert message == expected

def test_deprecation_warning_formatting_noline(tmp_path, with_colors):
    srcfile = tmp_path / 'file'
    srcfile.touch()

    message = warnings.formatwarning(
        'deprecated', warn.ReframeDeprecationWarning, srcfile, 10
    )
    expected = f'{srcfile}:10: WARNING: deprecated\n<no line information>\n'
    if with_colors:
        expected = color.colorize(expected, color.YELLOW)

    assert message == expected

def test_random_warning_formatting():
    message = warnings.formatwarning(
        'deprecated', UserWarning, 'file', 10, 'a = 1'
    )
    assert message == f'file:10: UserWarning: deprecated\n  a = 1\n'


import turtle

def lt(t,s):
 t.lt(s)

def polygon(t,l,n):
 for i in range(n):
  t.fd(l)
  lt(t,360/n)

bob = turtle.Turtle()
print(bob)
polygon(bob,100,6)
turtle.mainloop()


# 100점
n = int(input())
m = int(input())
S = input()

ans = 0
p = 0
i = 1
while i < m-1:
    if S[i-1] == 'I' and S[i] == 'O' and S[i+1] == 'I':
        p += 1
        if p == n:
            p -= 1
            ans += 1
        i += 1
    else:
        p = 0
    i += 1
print(ans)

# 50점
P = 'I'
Pn = 'OI'
n = int(input())
m = int(input())
S = input()

P = P + Pn * n
idx = len(P)
cnt = 0
while idx < m:
    data = S[idx-len(P):idx]
    if P == data:
        cnt += 1
        idx += 2
    else:
        idx += 1
print(cnt)


import os
import shutil
import logging

import minio

from . import utils
from . import tool
from . import sshkey

log = logging.getLogger(__name__)

def run(step, **kwargs):  # pylint: disable=unused-argument
    timeout = int(step.get('timeout', 60))
    url = step['checkout']
    dest = ''
    if 'destination' in step:
        dest = step['destination']

    # determine repo dir
    if dest:
        repo_dir = dest
    else:
        repo_dir = url.split('/')[-1]
        if repo_dir.endswith('.git'):
            repo_dir = repo_dir[:-4]

    # prepare accessing git repo
    ssh_agent = None
    access_token = None
    if 'ssh-key' in step:
        # username = step['ssh-key']['username']
        # url = '%s@%s' % (username, url)
        key = step['ssh-key']['key']
        ssh_agent = sshkey.SshAgent()
        ssh_agent.add_key(key)
    elif 'access-token' in step:
        access_token = step['access-token']
        if url.startswith('git@'):
            url = url[4:]
        url = 'https://%s@%s' % (access_token, url.replace(':', '/'))

    # prepare git config
    cfg_params = []
    git_cfg = step.get('git_cfg', {})
    for k, v in git_cfg.items():
        cfg_params.append('-c %s=%s' % (k, v))
    cfg_params = ' '.join(cfg_params)

    # setup connection to minio
    minio_addr = step['minio_addr']
    minio_access_key = step['minio_access_key']
    minio_secret_key = step['minio_secret_key']
    minio_bucket = step['minio_bucket']
    minio_folder = step['minio_folder']
    minio_repo_bundle_path = '%s/repo.bundle' % minio_folder
    mc = minio.Minio(minio_addr, access_key=minio_access_key, secret_key=minio_secret_key, secure=False)
    try:
        mc.bucket_exists(minio_bucket)
    except Exception as e:
        log.exception('problem with connecting to minio %s', minio_addr)
        msg = 'problem with connecting to minio %s: %s' % (minio_addr, str(e))
        return 1, msg

    # retrieve git repo bundle
    repo_bundle_path = 'repo.bundle'
    log.info('try to retrieve repo bundle %s / %s -> %s', minio_bucket, minio_repo_bundle_path, repo_bundle_path)
    try:
        mc.fget_object(minio_bucket, minio_repo_bundle_path, repo_bundle_path)
        bundle_present = True
    except Exception as e:
        if 'Object does not exist' in str(e):
            log.info('repo bundled not archived yet')
        else:
            log.exception('retriving repo from bundle failed, skipping it')
        bundle_present = False

    restore_ok = False
    if bundle_present:
        try:
            # restore git repo bundle
            utils.execute('git clone %s %s %s' % (cfg_params, repo_bundle_path, repo_dir), out_prefix='', timeout=timeout, raise_on_error=True)

            # restore original remote URL
            utils.execute('git remote set-url origin %s' % url, cwd=repo_dir, out_prefix='', timeout=timeout, raise_on_error=True)

            # pull latest stuff from remote
            utils.execute('git pull --ff-only', cwd=repo_dir, out_prefix='', timeout=timeout, raise_on_error=True)

            restore_ok = True
        except Exception:
            log.exception('cloning repo from bundle failed, skipping it')
            if os.path.exists(repo_dir):
                shutil.rmtree(repo_dir)

        # delete old bundle
        os.unlink(repo_bundle_path)

    # if restoring repo from bundle failed then do normal clone repo
    if not restore_ok:
        try:
            ret, _ = utils.execute('git clone %s %s %s' % (cfg_params, url, dest), mask=access_token, out_prefix='', timeout=timeout)
            if ret != 0:
                return ret, 'git clone exited with non-zero retcode'
        finally:
            if ssh_agent is not None:
                ssh_agent.shutdown()

    # bundle repo and cache it
    repo_bundle_path = os.path.abspath(os.path.join(repo_dir, '..', 'repo.bundle'))
    ret, _ = utils.execute('git bundle create %s --all' % repo_bundle_path, cwd=repo_dir, out_prefix='', timeout=300)
    if ret != 0:
        log.warning('repo bundle failed, skipping it')
    else:
        minio_bucket = step['minio_bucket']
        log.info('store repo bundle %s -> %s / %s', repo_bundle_path, minio_bucket, minio_repo_bundle_path)
        try:
            mc.fput_object(minio_bucket, minio_repo_bundle_path, repo_bundle_path)
        except Exception:
            log.exception('problem with storing repo bundle, skipping it')

    # checkout commit that comes from trigger, otherwise checkout master/main
    if 'trigger_data' in step and step['trigger_data'][0]['repo'] == step['http_url']:
        branch_or_commit = step['trigger_data'][0]['after']
    else:
        branch_or_commit = step.get('branch', 'master')  # TODO: detect if there is master, if not then checkout main

    # do checkout
    ret, _ = utils.execute('git checkout %s' % branch_or_commit, cwd=repo_dir, out_prefix='')
    if ret != 0:
        return ret, 'git checkout exited with non-zero retcode'

    return 0, ''

if __name__ == '__main__':
    tool.main()


import argparse
import gym
import numpy as np
import os
import tensorflow as tf
import tempfile
import time
import json
import random

import rlattack.common.tf_util as U

from rlattack import logger
from rlattack import deepq
from rlattack.deepq.replay_buffer import ReplayBuffer, PrioritizedReplayBuffer
from rlattack.common.misc_util import (
    boolean_flag,
    pickle_load,
    pretty_eta,
    relatively_safe_pickle_dump,
    set_global_seeds,
    RunningAvg,
    SimpleMonitor,
)
from rlattack.common.schedules import LinearSchedule, PiecewiseSchedule

# when updating this to non-deprecated ones, it is important to
# copy over LazyFrames
from rlattack.common.atari_wrappers_deprecated import wrap_dqn
from rlattack.common.azure_utils import Container
from model import model, dueling_model
from statistics import statistics

def parse_args():
    parser = argparse.ArgumentParser("DQN experiments for Atari games")
    # Environment
    parser.add_argument("--env", type=str, default="Pong", help="name of the game")
    parser.add_argument("--seed", type=int, default=42, help="which seed to use")
    # Core DQN parameters
    parser.add_argument(
        "--replay-buffer-size", type=int, default=int(1e6), help="replay buffer size"
    )
    parser.add_argument(
        "--lr", type=float, default=1e-4, help="learning rate for Adam optimizer"
    )
    parser.add_argument(
        "--num-steps",
        type=int,
        default=int(2e8),
        help="total number of steps to \
                        run the environment for",
    )
    parser.add_argument(
        "--batch-size",
        type=int,
        default=32,
        help="number of transitions to optimize \
                        at the same time",
    )
    parser.add_argument(
        "--learning-freq",
        type=int,
        default=4,
        help="number of iterations between \
                        every optimization step",
    )
    parser.add_argument(
        "--target-update-freq",
        type=int,
        default=40000,
        help="number of iterations between \
                        every target network update",
    )
    # Bells and whistles
    boolean_flag(parser, "noisy", default=False, help="whether or not to NoisyNetwork")
    boolean_flag(
        parser, "double-q", default=True, help="whether or not to use double q learning"
    )
    boolean_flag(
        parser, "dueling", default=False, help="whether or not to use dueling model"
    )
    boolean_flag(
        parser,
        "prioritized",
        default=False,
        help="whether or not to use prioritized replay buffer",
    )
    parser.add_argument(
        "--prioritized-alpha",
        type=float,
        default=0.6,
        help="alpha parameter for prioritized replay buffer",
    )
    parser.add_argument(
        "--prioritized-beta0",
        type=float,
        default=0.4,
        help="initial value of beta \
                        parameters for prioritized replay",
    )
    parser.add_argument(
        "--prioritized-eps",
        type=float,
        default=1e-6,
        help="eps parameter for prioritized replay buffer",
    )
    # Checkpointing
    parser.add_argument(
        "--save-dir",
        type=str,
        default=None,
        required=True,
        help="directory in which \
                        training state and model should be saved.",
    )
    parser.add_argument(
        "--save-azure-container",
        type=str,
        default=None,
        help="It present data will saved/loaded from Azure. \
                        Should be in format ACCOUNT_NAME:ACCOUNT_KEY:\
                        CONTAINER",
    )
    parser.add_argument(
        "--save-freq",
        type=int,
        default=1e6,
        help="save model once every time this many \
                        iterations are completed",
    )
    boolean_flag(
        parser,
        "load-on-start",
        default=True,
        help="if true and model was previously saved then training \
                 will be resumed",
    )

    # V: Attack Arguments #
    parser.add_argument(
        "--attack", type=str, default=None, help="Method to attack the model."
    )
    parser.add_argument(
        "--attack-init", type=int, default=0, help="Iteration no. to begin attacks"
    )
    parser.add_argument(
        "--attack-prob",
        type=float,
        default=0.0,
        help="Probability of attack at each step, \
                        float in range 0 - 1.0",
    )
    return parser.parse_args()

def make_env(game_name):
    env = gym.make(game_name + "NoFrameskip-v4")
    monitored_env = SimpleMonitor(env)
    env = wrap_dqn(monitored_env)
    return env, monitored_env

def maybe_save_model(savedir, container, state):
    if savedir is None:
        return
    start_time = time.time()
    model_dir = "model-{}".format(state["num_iters"])
    U.save_state(os.path.join(savedir, model_dir, "saved"))
    if container is not None:
        container.put(os.path.join(savedir, model_dir), model_dir)
    relatively_safe_pickle_dump(
        state, os.path.join(savedir, "training_state.pkl.zip"), compression=True
    )
    if container is not None:
        container.put(
            os.path.join(savedir, "training_state.pkl.zip"), "training_state.pkl.zip"
        )
    relatively_safe_pickle_dump(
        state["monitor_state"], os.path.join(savedir, "monitor_state.pkl")
    )
    if container is not None:
        container.put(os.path.join(savedir, "monitor_state.pkl"), "monitor_state.pkl")
    logger.log("Saved model in {} seconds\n".format(time.time() - start_time))

def maybe_load_model(savedir, container):
    """Load model if present at the specified path."""
    if savedir is None:
        return

    state_path = os.path.join(os.path.join(savedir, "training_state.pkl.zip"))
    if container is not None:
        logger.log("Attempting to download model from Azure")
        found_model = container.get(savedir, "training_state.pkl.zip")
    else:
        found_model = os.path.exists(state_path)
    if found_model:
        state = pickle_load(state_path, compression=True)
        model_dir = "model-{}".format(state["num_iters"])
        if container is not None:
            container.get(savedir, model_dir)
        U.load_state(os.path.join(savedir, model_dir, "saved"))
        logger.log(
            "Loaded models checkpoint at {} iterations".format(state["num_iters"])
        )
        return state

if __name__ == "__main__":
    args = parse_args()
    # Parse savedir and azure container.
    savedir = args.save_dir
    if args.save_azure_container is not None:
        account_name, account_key, container_name = args.save_azure_container.split(":")
        container = Container(
            account_name=account_name,
            account_key=account_key,
            container_name=container_name,
            maybe_create=True,
        )
        if savedir is None:
            # Careful! This will not get cleaned up.
            savedir = tempfile.TemporaryDirectory().name
    else:
        container = None
    # Create and seed the env.
    env, monitored_env = make_env(args.env)
    if args.seed > 0:
        set_global_seeds(args.seed)
        env.unwrapped.seed(args.seed)

    # V: Save arguments, configure log dump path to savedir #
    if savedir:
        with open(os.path.join(savedir, "args.json"), "w") as f:
            json.dump(vars(args), f)
        logger.configure(dir=savedir)  # log to savedir

    with U.make_session(4) as sess:
        # Create training graph and replay buffer
        act, train, update_target, debug, craft_adv = deepq.build_train(
            make_obs_ph=lambda name: U.Uint8Input(
                env.observation_space.shape, name=name
            ),
            q_func=dueling_model if args.dueling else model,
            num_actions=env.action_space.n,
            optimizer=tf.train.AdamOptimizer(learning_rate=args.lr, epsilon=1e-4),
            gamma=0.99,
            grad_norm_clipping=10,
            double_q=args.double_q,
            noisy=args.noisy,
            attack=args.attack,
        )
        approximate_num_iters = args.num_steps / 4
        exploration = PiecewiseSchedule(
            [
                (0, 1.0),
                (approximate_num_iters / 50, 0.1),
                (approximate_num_iters / 5, 0.01),
            ],
            outside_value=0.01,
        )

        if args.prioritized:
            replay_buffer = PrioritizedReplayBuffer(
                args.replay_buffer_size, args.prioritized_alpha
            )
            beta_schedule = LinearSchedule(
                approximate_num_iters, initial_p=args.prioritized_beta0, final_p=1.0
            )
        else:
            replay_buffer = ReplayBuffer(args.replay_buffer_size)

        U.initialize()
        update_target()
        num_iters = 0

        # Load the model
        state = maybe_load_model(savedir, container)
        if state is not None:
            num_iters, replay_buffer = (
                state["num_iters"],
                state["replay_buffer"],
            )
            monitored_env.set_state(state["monitor_state"])

        start_time, start_steps = None, None
        steps_per_iter = RunningAvg(0.999)
        iteration_time_est = RunningAvg(0.999)
        obs = env.reset()
        # Record the mean of the \sigma
        sigma_name_list = []
        sigma_list = []
        for param in tf.trainable_variables():
            # only record the \sigma in the action network
            if "sigma" in param.name and "deepq/q_func/action_value" in param.name:
                summary_name = (
                    param.name.replace("deepq/q_func/action_value/", "")
                    .replace("/", ".")
                    .split(":")[0]
                )
                sigma_name_list.append(summary_name)
                sigma_list.append(tf.reduce_mean(tf.abs(param)))
        f_mean_sigma = U.function(inputs=[], outputs=sigma_list)
        # Statistics
        writer = tf.summary.FileWriter(savedir, sess.graph)
        im_stats = statistics(
            scalar_keys=["action", "im_reward", "td_errors", "huber_loss"]
            + sigma_name_list
        )
        ep_stats = statistics(scalar_keys=["ep_reward", "ep_length"])
        # Main trianing loop
        ep_length = 0
        while True:
            num_iters += 1
            ep_length += 1

            # V: Perturb observation if we are past the init stage
            # and at a designated attack step
            # if craft_adv != None and (num_iters >= args.attack_init)
            # and ((num_iters - args.attack_init) % args.attack_freq == 0) :
            if (
                craft_adv is not None
                and (num_iters >= args.attack_init)
                and (random.random() <= args.attack_prob)
            ):
                obs = craft_adv(np.array(obs)[None])[0]

            # Take action and store transition in the replay buffer.
            if args.noisy:
                # greedily choose
                action = act(np.array(obs)[None], stochastic=False)[0]
            else:
                # epsilon greedy
                action = act(
                    np.array(obs)[None], update_eps=exploration.value(num_iters)
                )[0]
            new_obs, rew, done, info = env.step(action)
            replay_buffer.add(obs, action, rew, new_obs, float(done))
            obs = new_obs
            if done:
                obs = env.reset()

            if (
                num_iters > max(5 * args.batch_size, args.replay_buffer_size // 20)
                and num_iters % args.learning_freq == 0
            ):
                # Sample a bunch of transitions from replay buffer
                if args.prioritized:
                    experience = replay_buffer.sample(
                        args.batch_size, beta=beta_schedule.value(num_iters)
                    )
                    (
                        obses_t,
                        actions,
                        rewards,
                        obses_tp1,
                        dones,
                        weights,
                        batch_idxes,
                    ) = experience
                else:
                    obses_t, actions, rewards, obses_tp1, dones = replay_buffer.sample(
                        args.batch_size
                    )
                    weights = np.ones_like(rewards)
                # Minimize the error in Bellman's and compute TD-error
                td_errors, huber_loss = train(
                    obses_t, actions, rewards, obses_tp1, dones, weights
                )
                # Update the priorities in the replay buffer
                if args.prioritized:
                    new_priorities = np.abs(td_errors) + args.prioritized_eps
                    replay_buffer.update_priorities(batch_idxes, new_priorities)
                # Write summary
                mean_sigma = f_mean_sigma()
                im_stats.add_all_summary(
                    writer,
                    [action, rew, np.mean(td_errors), np.mean(huber_loss)] + mean_sigma,
                    num_iters,
                )

            # Update target network.
            if num_iters % args.target_update_freq == 0:
                update_target()

            if start_time is not None:
                steps_per_iter.update(info["steps"] - start_steps)
                iteration_time_est.update(time.time() - start_time)
            start_time, start_steps = time.time(), info["steps"]

            # Save the model and training state.
            if num_iters > 0 and (
                num_iters % args.save_freq == 0 or info["steps"] > args.num_steps
            ):
                maybe_save_model(
                    savedir,
                    container,
                    {
                        "replay_buffer": replay_buffer,
                        "num_iters": num_iters,
                        "monitor_state": monitored_env.get_state(),
                    },
                )

            if info["steps"] > args.num_steps:
                break

            if done:
                steps_left = args.num_steps - info["steps"]
                completion = np.round(info["steps"] / args.num_steps, 1)
                mean_ep_reward = np.mean(info["rewards"][-100:])
                logger.record_tabular("% completion", completion)
                logger.record_tabular("steps", info["steps"])
                logger.record_tabular("iters", num_iters)
                logger.record_tabular("episodes", len(info["rewards"]))
                logger.record_tabular(
                    "reward (100 epi mean)", np.mean(info["rewards"][-100:])
                )
                if not args.noisy:
                    logger.record_tabular("exploration", exploration.value(num_iters))
                if args.prioritized:
                    logger.record_tabular("max priority", replay_buffer._max_priority)
                fps_estimate = (
                    float(steps_per_iter) / (float(iteration_time_est) + 1e-6)
                    if steps_per_iter._value is not None
                    else "calculating:"
                )
                logger.dump_tabular()
                logger.log()
                logger.log("ETA: " + pretty_eta(int(steps_left / fps_estimate)))
                logger.log()
                # add summary for one episode
                ep_stats.add_all_summary(writer, [mean_ep_reward, ep_length], num_iters)
                ep_length = 0


"""
This file carries out the analysis of the data
"""

import csv
import math

from util import float_range

class RAEAnalysis:

    """Initialise global variables to use throughout script"""
    def __init__(self, output_dir, config):
        self.OUTPUT_DIR = output_dir
        self.CONF = config
        self.weights = config['settings']['weights']
        self.vuln_data = {}
        self.host_data = {}

    def run_analysis(self, vuln_data, host_data):
        self.vuln_data = vuln_data
        self.host_data = host_data
        self.calculate_risk()
        self.calculate_impact()
        self.calculate_final()

    def calculate_risk(self):
        print('Calculating Risk')
        attack_table = self.CONF["attack_table"]
        for qid in self.vuln_data.keys():
            plugin = self.vuln_data[qid]

            if "analysis" not in plugin:
                plugin["analysis"] = {}

            """Keyword Match"""
            description = plugin['threat'] + plugin['impact']
            matches = {}
            for vulntype in attack_table:
                keywords = attack_table[vulntype]["keywords"]
                keywords = keywords.lower().split(',')
                for key in keywords:
                    key = key.lstrip()
                    if key in description and vulntype not in matches.keys():
                        matches[vulntype] = float(attack_table[vulntype]["score"])

            sev_score = 0.0
            types = matches.keys()
            type = ", ".join(types)
            divider = 1
            allscores = []

            """Algorithm for multiple matches"""
            if matches:
                for score in matches.values():
                    allscores.append(score)
                allscores.sort(reverse=True)
                for score in allscores:
                    temp = int((score * 9) / 10)
                    if temp is 0:
                        sev_score = float(sev_score + 0.5 / divider)
                    else:
                        sev_score = float(sev_score + float(temp / divider))
                    divider *= 10
            else:
                sev_score = 10
                type = 'No type found'

            cvss_weight = self.CONF["settings"]["weights"]["risk"]["cvss"]
            sev_weight = self.CONF["settings"]["weights"]["risk"]["sev"]
            risk_score = float((cvss_weight * float(plugin['cvss'])) + (sev_weight * sev_score))
            sev_score = round(sev_score, 3)
            risk_score = round(risk_score, 3)

            plugin['analysis']['types'] = type
            plugin['analysis']['sev_score'] = sev_score
            plugin['analysis']['risk_score'] = risk_score

    def calculate_impact(self):
        print('Calculating Impact')

        max_attack_surface = 0

        for qid in self.vuln_data.keys():
            plugin = self.vuln_data[qid]

            """Calculate Attack Surface"""
            numaffected = len(plugin['hosts'])
            num_total_connections = 0
            for host in plugin['hosts']:
                num_total_connections = sum(self.host_data[host]['connections'].values()) + 1
            try:
                attack_surface = 2.2 * (math.log(num_total_connections) + math.log(numaffected))
            except ValueError:
                attack_surface = 10

            plugin['analysis']['attack_surface'] = round(attack_surface, 3)

            if attack_surface > max_attack_surface:
                max_attack_surface = attack_surface

            """Calculate Protection and Availability"""
            total_protection = 0
            total_availability = 0
            for host in plugin['hosts']:
                hostdata = self.host_data[host]
                total_protection += hostdata['protection']
                total_availability += hostdata['availability']
            total_protection /= numaffected
            total_availability /= numaffected

            plugin['analysis']['protection'] = round(total_protection, 3)
            plugin['analysis']['availability'] = round(total_availability, 3)

        # Normalize Attack Surface Scores
        for qid in self.vuln_data.keys():
            plugin = self.vuln_data[qid]
            plugin['analysis']['attack_surface'] = round(((plugin['analysis']['attack_surface'] / max_attack_surface) * 10), 3)

            """Calculate Total Impact"""
            attack_surface_weight = self.weights["impact"]["attack_surface"]["total"]
            protection_weight = self.weights["impact"]["protection"]["total"]
            availability_weight = self.weights["impact"]["availability"]["total"]

            impact_score = (attack_surface_weight * plugin['analysis']['attack_surface']) + \
                           (protection_weight * plugin['analysis']['protection']) + \
                           (availability_weight * plugin['analysis']['availability'])
            plugin['analysis']['impact_score'] = round(impact_score, 3)

        print('Calculate Impact Complete')

    def calculate_final(self):
        print('Calculating Final')
        writer = csv.writer(open(self.OUTPUT_DIR + self.CONF["settings"]["csv_output"], 'w+', newline=''))

        header = ["Plugin ID", "Name", "# Affected", "First Seen", "CVSS Score", "sevScore", "Risk Score",
                  "Attack Surface", "Protection", "Availability", "Impact Score", "Final Score", "Class", "Out of Band",
                  "Synopsis", "Comments"]
        writer.writerow(header)

        critical_types = self.CONF["settings"]['thresholds']["critical_types"].split(",")
        outofband_types = self.CONF["settings"]['thresholds']["outofband_types"].split(",")

        for qid in self.vuln_data.keys():
            plugin = self.vuln_data[qid]
            row = [qid]
            outofband = "no"

            risk_weight = self.weights["overall"]["risk"]
            impact_weight = self.weights["overall"]["impact"]
            final_score = (risk_weight * float(plugin['analysis']['risk_score'])) + \
                          (impact_weight * float(plugin['analysis']['impact_score']))
            plugin['analysis']['final_score'] = round(final_score, 3)

            critical_threshold = self.CONF["settings"]["thresholds"]["critical"]
            high_threshold = self.CONF["settings"]["thresholds"]["high"]
            medium_threshold = self.CONF["settings"]["thresholds"]["medium"]

            if float_range(final_score, float(critical_threshold.split("-")[0]), float(critical_threshold.split("-")[1])):
                classification = "critical"
            elif float_range(final_score, float(high_threshold.split("-")[1]), float(critical_threshold.split("-")[0])):
                for vuln_type in critical_types:
                    if vuln_type in plugin['analysis']['types']:
                        classification = "critical"
                        break
                    else:
                        classification = "high"
            elif float_range(final_score, float(high_threshold.split("-")[0]), float(high_threshold.split("-")[1])):
                classification = "high"
            elif float_range(final_score, float(medium_threshold.split("-")[0]), float(medium_threshold.split("-")[1])):
                classification = "medium"
            else:
                classification = "low"

            self.vuln_data[qid]['analysis']['class'] = classification

            if classification == "critical":
                for vuln_type in outofband_types:
                    if vuln_type in plugin['analysis']['types']:
                        if plugin['exploitability'] == "Yes":
                            outofband = "yes"

            plugin['analysis']['outofband'] = outofband

            row.append(plugin['title'])
            row.append(len(plugin['hosts']))
            row.append(plugin['first_seen'])
            row.append(plugin['cvss'])
            row.append(plugin['analysis']['sev_score'])
            row.append(plugin['analysis']['risk_score'])
            row.append(plugin['analysis']['attack_surface'])
            row.append(plugin['analysis']['protection'])
            row.append(plugin['analysis']['availability'])
            row.append(plugin['analysis']['impact_score'])
            row.append(round(plugin['analysis']['final_score'], 3))
            row.append(classification)
            row.append(outofband)
            row.append(plugin['threat'] + plugin['impact'])
            writer.writerow(row)

        print('Calculate Final Complete')


from PyQt5.QtSql import QSqlDatabase, QSqlQuery
from PyQt5.Qt import QMessageBox

class MyDb(object):
    db = QSqlDatabase.addDatabase('QSQLITE')
    db.setDatabaseName('zhgl.db')
    query = QSqlQuery()

    def loaddb(self, sql):
        if not self.db.open():
            return False
        if self.query.exec_(sql):
            return self.query
            self.db.close()
        else:
            return 0  #当SQl语句执行失败返回一个标识0，让后后续代码捕获并作出相应的处理。

    def columnRow(self):
        datacolumn = self.query.record().count()
        datarow = 0
        if self.query.last():
            datarow = self.query.at() + 1
        # 把索引指向回第一条的上一条。这样next() 第一次才能指向第一条。
        self.query.first()
        self.query.previous()
        return (datacolumn, datarow)

'''测试代码'''
# MyDb().loaddb('SELECT * FROM "Test"')
# print(MyDb.columnRow())
# my_db = MyDb()
# data = my_db.loaddb('SELECT * FROM "Test"')
# print(my_db.columnRow())
#
# my_db1 = MyDb()
# data = my_db1.loaddb('SELECT * FROM "Paypal信息"')
# print(my_db1.columnRow())
# # 表的列数
# print(type(data.record().count()))
# print(data.record().count())
# # 表的行数
# i = 0
# while data.next():
#     i += 1
# print(i)
# print(data.size())
# 通过索引名称找到对应的值
# id_index = data.record().indexOf('Id')
# # name_index = data.record().indexOf('Name')
# # age_index = data.record().indexOf('Age')
# # while data.next():
# #     id = data.value(id_index)
# #     name = data.value(name_index)
# #     age = data.value(age_index)
# #     print(id, name, age)
# 通过
# id_index = data.record().indexOf('Id')
# name_index = data.record().indexOf('Name')
# age_index = data.record().indexOf('Age')
# while data.next():
#     id = data.value(0)
#     name = data.value(1)
#     age = data.value(2)
#     print(id, name, age)


from django.conf.urls import include, url
from django.conf import settings
from django.conf.urls.static import static
from django.contrib.auth.decorators import login_required
from glims import views
from django.contrib import admin
from plugins.utils import PluginManager
from bioshare import urls as bioshare_urls
admin.autodiscover()
from permissions import urls as permission_urls
from attachments import urls as attachment_urls
from proteomics import urls as proteomics_urls
from extensible import urls as extensible_urls
from django_compute import urls as compute_urls
from django_formly import urls as formly_urls
from notifications import urls as notification_urls
import api
from utils.jsutils import jsurls
from django.contrib.auth import views as auth_views
# if USE_CAS:
#     admin.site.login = login_required(admin.site.login)
#     urlpatterns += patterns('',
#         url(r'^login/$', 'cas.views.login, name='login'),
#         url(r'^logout/$', 'cas.views.logout, name='logout'),
#         url(r'^admin/logout/$', 'cas.views.logout'),
#     )

from rest_framework import routers
from glims.api.viewsets import ProjectViewSet, SampleViewSet, LabViewSet, ModelTypeSerializerViewSet, PoolViewSet, JobViewset, UserViewSet, GroupViewSet,\
    StatusSerializerViewSet, AdapterViewSet, LibraryViewSet, ExtendedNoteViewSet

router = routers.DefaultRouter()
router.register(r'users', UserViewSet,'User')
router.register(r'groups', GroupViewSet,'Group')
router.register(r'model_types', ModelTypeSerializerViewSet,'ModelType')
router.register(r'projects', ProjectViewSet,'Project')
router.register(r'samples', SampleViewSet,'Sample')
router.register(r'adapters', AdapterViewSet,'Adapter')
router.register(r'libraries', LibraryViewSet,'Library')
router.register(r'pools', PoolViewSet,'Pool')
router.register(r'jobs', JobViewset,'Job')
# router.register(r'submissions', JobSubmissionViewset)
router.register(r'labs', LabViewSet,'Lab')
router.register(r'statuses', StatusSerializerViewSet,'Status')
router.register(r'notes', ExtendedNoteViewSet,'Note')

urlpatterns = [
    # Examples:
#     url(r'^$', views.home, name='home'),
    # url(r'^blog/', include('blog.urls')),

    url(r'^admin/', include(admin.site.urls)),
    url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')),
    url(r'^$', views.home, name='home'),
    url(r'^projects/(?P<pk>[\-\w]+)/$', views.project, name='project'),
#     url(r'^samples/(?P<pk>[\-\w]+)/$', views.sample, name='sample'),
#     url(r'^samples/(?P<pk>[\-\w]+)/update/$', login_required(glims_views.SampleUpdate.as_view()), name='update_sample'),
#     url(r'^file/(?P<pk>\d+)/get/$', views.get_file, name='get_file'),
#     url(r'^files/(?P<model>\w+)/(?P<pk>[\-\w]+)/attach/$', views.attach_file, name='attach_file'),
    url(r'^labs/$', views.labs, name='labs'),
    url(r'^labs/(?P<pk>\d+)/modify/$', views.modify_lab, name='modify_lab'),
    url(r'^labs/create$', views.create_lab, name='create_lab'),
    url(r'^labs/(?P<pk>\d+)/$', views.lab, name='lab'),
    url(r'^projects/$', views.projects, name='projects'),
#     url(r'^projects/(?P<pk>[\-\w]+)/data/$', views.project_files, name='project_files'),
    url(r'^samples/$', views.samples, name='samples'),
    url(r'^pools/$', views.pools, name='pools'),
#     url(r'^pools/create$', views.create_pool, name='create_pool'),
    url(r'^pools/(?P<pk>[\-\w]+)/$', views.pool, name='pool'),
    url(r'^pools/(?P<pk>[\-\w]+)/delete/$', views.delete_pool, name='delete_pool'),
    url(r'^cart/$', views.cart, name='cart'),
#     url(r'^workflow/$', views.workflow, name='workflow'),

    url(r'^jobs/$', views.jobs, name='jobs'),
#     url(r'^job_submissions/(?P<id>[\d_A-Za-z]+)/$', views.job_submission, name='job_submission'),
    url(r'^jobs/(?P<id>[A-Z0-9]{10})/$', views.job, name='job'),
    url(r'^jobs/(?P<id>[A-Z0-9]{10})/files/$', views.job_files, name='job_files'),
    url(r'^accounts/login/$', auth_views.login, {'template_name': 'registration/login.html','redirect_authenticated_user': True}, name='login'),
    url(r'^accounts/logout/$', auth_views.logout, {'template_name': 'registration/logout.html','next_page':'home'}, name='logout'),
    url(r'^admin/model_types/$', views.model_types, name='model_types'),
    url(r'^admin/model_types/(?P<pk>\d+)/$', views.model_type, name='model_type'),
    url(r'^permissions/', include(permission_urls.urlpatterns)),
    url(r'^attachments/', include(attachment_urls.urlpatterns)),
    url(r'^proteomics/', include(proteomics_urls.urlpatterns)),
#     url(r'^bioinformatics/', include(bioinformatics_urls.urlpatterns)),
    url(r'^sample/(?P<pk>[\-\w]+)/$', views.sample, name='sample'),
    url(r'^api/add_samples_to_cart/$', api.views.add_samples_to_cart, name='add_samples_to_cart'),
    url(r'^api/remove_samples_from_cart/$', api.views.remove_samples_from_cart, name='remove_samples_from_cart'),
    url(r'^api/', include(router.urls)),
    url(r'^jsurls.js$', jsurls, {}, 'jsurls'),
#    url(r'^api/job/(?P<job_id>\d+\.?\d*)/update/$', api.views.update_job, name='update_job'),
    url(r'^api/pool/(?P<pk>\d+)/update/$', api.views.update_pool, name='update_pool'),
    url(r'^api/pool/(?P<pool_id>\d+)/sample/(?P<sample_id>\d+)/update/$', api.views.update_pool_sample, name='update_pool_sample'),
    url(r'^api/pool/(?P<pk>\d+)/remove_libraries/$', api.views.remove_pool_libraries, name='remove_pool_libraries'),
    url(r'^api/pool/(?P<pk>\d+)/add_libraries/$', api.views.add_pool_libraries, name='add_pool_libraries'),
    url(r'^api/projects/(?P<project_id>[\-\w]+)/data/(?:(?P<path>.*/))?$', api.views.project_files, name='get_project_files'),
    url(r'^samples/', include('glims.samples.urls')),
    url(r'^plugins/', include('plugins.urls')),
    url(r'^tasks/', include('tasks.urls')),
    url(r'^extensible/', include(extensible_urls.urlpatterns)),
    url(r'^compute/', include(compute_urls.urlpatterns)),
    url(r'^formly_forms/', include(formly_urls.urlpatterns)),
    url(r'^notifications/', include(notification_urls.urlpatterns)),
    url(r'^bioshare/', include(bioshare_urls.urlpatterns)),
    url(r'^sequencing/', include('sequencing.urls')),
]+ static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

plugin_manager = PluginManager()
urlpatterns += plugin_manager.get_urls()


from multiprocessing.spawn import old_main_modules

import pygame

from sprite import Sprite

class Player(Sprite):
    def __init__(self, x, y, game, name, obstacles):
        super().__init__(x, y, game.PLAYER_WIDTH, game.PLAYER_HEIGHT, game,
                         "main_characters", name, animation_speed=0.5)
        self.obstacles = obstacles
        self.speed = 5
        self.velocity = pygame.math.Vector2(0, 0)
        self.flip_sprite = False
        self.grounded = False
        self.jump_force = 15
        self.jump_counter = 0
        self.jumping = False
        self.jump_time = 0
        self.jump_cooldown = 400

    def move(self, dx=0, dy=0):
        self.rect.x += dx
        self.rect.y += dy

    def move_left(self, running=False):
        if running:
            self.velocity.x = -self.speed * 2
        else:
            self.velocity.x = -self.speed
        self.flip_sprite = True

    def move_right(self, running=False):
        if running:
            self.velocity.x = self.speed * 2
        else:
            self.velocity.x = self.speed
        self.flip_sprite = False

    def jump(self):
        if self.jumping:
            return
        self.jumping = True
        if self.grounded:
            self.velocity.y = -self.jump_force / self.game.GRAVITY
            self.jump_counter = 1
            self.grounded = False
            self.jump_time = pygame.time.get_ticks()
        elif self.jump_counter < 2:
            self.velocity.y = -self.jump_force / self.game.GRAVITY
            self.jump_counter += 1
            self.jump_time = pygame.time.get_ticks()

    def handle_input(self):
        keys = pygame.key.get_pressed()
        self.velocity.x = 0
        if (keys[pygame.K_LEFT] or keys[pygame.K_a]) and (keys[pygame.K_RIGHT] or keys[pygame.K_d]):
            self.velocity.x = 0
        elif keys[pygame.K_LEFT] or keys[pygame.K_a]:
            if keys[pygame.K_LSHIFT] or keys[pygame.K_RSHIFT]:
                self.move_left(running=True)
            else:
                self.move_left()
        elif keys[pygame.K_RIGHT] or keys[pygame.K_d]:
            if keys[pygame.K_LSHIFT] or keys[pygame.K_RSHIFT]:
                self.move_right(running=True)
            else:
                self.move_right()
        if keys[pygame.K_SPACE] or keys[pygame.K_UP] or keys[pygame.K_w]:
            self.jump()

    def update_status(self):
        old_status = self.status
        if self.velocity.y > self.game.GRAVITY * 2:
            self.status = "fall"
        elif self.velocity.y < -self.game.GRAVITY * 2:
            self.status = "jump" if self.jump_counter == 1 else "double_jump"
        elif self.velocity.x != 0:
            self.status = "run"
        else:
            self.status = "idle"
        if old_status != self.status:
            self.frame_index = 0

    def handle_gravity(self):
        if not self.grounded:
            self.velocity.y += self.game.GRAVITY
            if self.velocity.y > self.game.MAX_GRAVITY:
                self.velocity.y = self.game.MAX_GRAVITY

    def check_horizontal_collisions(self):
        for obstacle in self.obstacles:
            if pygame.sprite.collide_mask(self, obstacle):
                self.move(-self.velocity.x, 0)
                break

    def check_vertical_collisions(self):
        self.grounded = False
        for obstacle in self.obstacles:
            if pygame.sprite.collide_mask(self, obstacle):
                if self.velocity.y > 0:
                    self.rect.bottom = obstacle.rect.top
                    self.velocity.y = 0
                    self.grounded = True
                    self.jump_counter = 0
                elif self.velocity.y < 0:
                    self.rect.top = obstacle.rect.bottom
                    self.velocity.y = -self.velocity.y

    def handle_cooldowns(self):
        current_time = pygame.time.get_ticks()
        if self.jumping and current_time - self.jump_time > self.jump_cooldown:
            self.jumping = False

    def update(self):
        self.handle_gravity()
        self.handle_cooldowns()
        self.handle_input()
        self.move(self.velocity.x, self.velocity.y)
        self.check_horizontal_collisions()
        self.check_vertical_collisions()
        self.update_status()
        self.animate(self.flip_sprite)


"""
102. Binary Tree Level Order Traversal
Given the root of a binary tree, return the level order traversal of its nodes' values.
(i.e., from left to right, level by level).

The number of nodes in the tree is in the range [0, 2000].
-1000 <= Node.val <= 1000

"""

# Definition for a binary tree node.
from typing import List

class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

class Solution:
    def levelOrder(self, root: TreeNode) -> List[List[int]]:
        if root is None:
            return []

        answer = []
        def levelTraverse(node: TreeNode, level):
            if len(answer) <= level:
                answer.append([node.val])
            else:
                answer[level].append(node.val)

            if node.left is not None:
                levelTraverse(node.left, level+1)
            if node.right is not None:
                levelTraverse(node.right, level + 1)
        # level을 tracking 하면서 없으면 추가해서 넣는거로.

        levelTraverse(root, 0)
        return answer

if __name__ == "__main__":
    s = Solution()
    root = TreeNode(3)
    root.left = TreeNode(9)
    root.right = TreeNode(20)
    root.right.left = TreeNode(15)
    root.right.right = TreeNode(7)
    print(s.levelOrder(root), "[[3],[9,20],[15,7]]")
    root = TreeNode(1)
    print(s.levelOrder(root), "[[1]]")
    root = None
    print(s.levelOrder(root), "[]")

"""
Runtime: 36 ms, faster than 61.92% of Python3 online submissions for Binary Tree Level Order Traversal.
Memory Usage: 15.2 MB, less than 12.86% of Python3 online submissions for Binary Tree Level Order Traversal.
"""