import numpy as np

N=100000
results = []

def upgrade(dice):
    dice = dice +2
    if dice > 12:
        dice = 20
    return dice

def roll(dice, running=0):
    a = np.random.randint(dice) + 1
    b = np.random.randint(dice) + 1
    total = np.abs(a-b) + running
    if a == b:
        if a == 1:
            return 1
        dice = upgrade(dice)
        additional = roll(dice, total)
        total = total + a + additional
    return total

import sys
for i in range(N):
    result = roll(int(sys.argv[1]))
    results.append(result)

running=0
average = 0
last_per = 0
cumlist = []
perlist = []
for i in range(1, np.max(results)):
    percentage=np.around(results.count(i)/N, 4)
    running=running+last_per
    average += i*percentage
    last_per = percentage
    cumlist.append(running)
    perlist.append(percentage)

for i in range(1, 12):
    percentage = perlist[i]
    running = cumlist[i]
    print((i,np.around( percentage*100,3), np.around(100*(1- running), 3) ))
print(f"Average result: {average}")


from locust import HttpUser, task, between
import os

FILE_PATH = "server/generation/samples/results/sample.ipynb"

class WebsiteUser(HttpUser):
    wait_time = between(1, 2.5)

    def on_start(self):
        self.file_key = None
        self.state = "upload"

    @task(1)
    def post_file(self):
        with open(FILE_PATH, "rb") as f:
            data = {"file": (os.path.basename(FILE_PATH), f)}
            response = self.client.post("file/test/upload", files=data)
            if response.status_code == 200:
                self.file_key = response.text

    @task(2)
    def get_file(self):
        if self.file_key:
            self.client.get(
                f"file/test/get?file_key={self.file_key}&file_name=test_file"
            )


class row_of_entities():
    def __init__(self,perliminary_string):
        self.entities = {}
        self.entities['perliminary_string'] = perliminary_string

    def set_year_from_row(self):
        split_row = self.entities['perliminary_string'].split()
        if 'yr' in split_row[-1]:
            val = split_row[-1][0]
        elif 'years' in split_row[-1]:
            val = split_row[-2][0]
        elif 'yr' in split_row[-3]:
            val = split_row[-3][0]
        else:
            print('Not recognize')
            return None
        self.entities['years'] = val+' years'
        return val

    def set_Amount_from_row(self):
        split_row = self.entities['perliminary_string'].split()
        val = None
        for word in split_row:

            if word[-2:] == 'MM':
                val = int(word[0])*1000000
                break

            if word == 'some':
                val = -1
                break

            if word[-1] == 'a': #for the 6'th line
                try:
                    val = int(word[:-1])
                    break
                except:
                    pass
                break

            try:
                val = int(word)
                break
            except:
                pass

        self.entities['amount'] = val
        return val

    def set_stock_from_row(self):
        split_row = self.entities['perliminary_string'].split()
        val = None
        for word in split_row:
            if len(word) == 3 and word.isupper():
                val = word
                break

            if len(word) == 4 and word[0:3].isupper():
                val = word[0:3]
                break

        self.entities['stock'] = val
        return val

    def set_entities_offset(self,type):
        try:
            self.entities[type+'_offset'] = self.entities['perliminary_string'].split().index(self.entities[type])+1 #return the place (1 for the first)
        except:
            self.entities[type+'_offset'] = 'cant_find_need_to_work_harder'


#!/usr/bin/python3
"""defines a class FileStorage"""

import json
from models.base_model import BaseModel
from models.user import User
from models.state import State
from models.city import City
from models.place import Place
from models.amenity import Amenity
from models.review import Review

class FileStorage:
    __file_path = "file.json"
    __objects = {}

    def all(self):
        return FileStorage.__objects

    def new(self, obj):
        key = "{}.{}".format(obj.__class__.__name__, obj.id)
        FileStorage.__objects[key] = obj

    def save(self):
        obj_dict = {}
        for key, value in FileStorage.__objects.items():
            obj_dict[key] = value.to_dict()
        with open(FileStorage.__file_path, 'w', encoding='utf-8') as file:
            json.dump(obj_dict, file)

    def reload(self):
        try:
            with open(FileStorage.__file_path, 'r', encoding='utf-8') as file:
                obj_dict = json.load(file)
                for key, value in obj_dict.items():
                    class_name, obj_id = key.split('.')
                    class_ = globals().get(class_name)
                    if class_ is None:
                        class_ = BaseModel
                    instance = class_(**value)
                    self.new(instance)

        except FileNotFoundError:
            pass


"""
This is an example of a weird pattern in OO python
in which the local attributes of an
instance get populated from the global (class based)
ones.

This is based on the loop
"""

class MyClass:
    var_member = 14

    def __init__(self, member):
        if member:
            self.var_member = member
        else:
            self.var_member = self.var_member

    def do_print(self):
        print(f"var_member is {self.var_member}")

# Lets show how we use our object
b = MyClass(15)
b.do_print()
b = MyClass(16)
b.do_print()
b = MyClass(None)
b.do_print()


from sfsimodels import output as mo
from collections import OrderedDict

def save_figure(ap, figure, name, publish=True, name_ext="", ftype=".png", latex=False, dpi=150):
    """
    Saves a figure and produces python output.

    :param ap: (module), all paths
    :param figure: (Figure object)
    :param name: (str), the name of the figure
    :param publish: (bool) if True then save in publication location, else in temp dir
    :param name_ext: (str) An extension for the file name
    :param ftype: (str) the suffix for file type (e.g. '.png')
    :param latex: (bool), if true then return a string of latex script to include the figure
    :param dpi: (int), the dots per inch of the saved figure
    :return: (str)
    """
    figure.tight_layout()

    if not publish:
        figure.savefig(ap.TEMP_FIGURE_PATH + name + name_ext + ftype, dpi=dpi)
    else:
        figure.savefig(ap.PUBLICATION_FIGURE_PATH + name + name_ext + ftype, dpi=dpi)
    if latex:
        return latex_for_figure(ap.FIGURE_FOLDER, name, ftype)
    return ""

def latex_for_figure(figure_folder_name, name, ftype):
    str_parts = ["",
                    "\\begin{figure}[H]",
                    "\centering",
                    "\\includegraphics{%s/%s%s}" % (figure_folder_name, name, ftype),
                    "\\caption{%s \label{fig: %s}}" % (name.replace("_", " "), name),
                    "\\end{figure}"
                 ]
    return "\n".join(str_parts)

def output_to_table(**kwargs):
    mo.output_to_table(**kwargs)

def add_table_ends(**kwargs):
    mo.add_table_ends(**kwargs)

def get_file_name_for_build_results(python_name):
    name = python_name.replace('.py', '')
    name = name.split("figure_")[-1]
    name = "fig_results_" + name + ".txt"
    return name

def unpack_build_results(python_name, titles):
    res_fpath = get_file_name_for_build_results(python_name)
    res_file = open(res_fpath)
    res_lines = res_file.readlines()
    data = OrderedDict()
    for line in res_lines:
        # name, sd_start, sd_end, period, max_disp_time
        items = line.split(",")
        if items[0] not in data:
            data[items[0]] = OrderedDict()
            for i in range(len(items)):
                data[items[0]][titles[i]] = []

        for i in range(len(items)):
            try:
                data[items[0]][titles[i]].append(float(items[i]))
            except ValueError:
                data[items[0]][titles[i]].append(items[i])
    return data


import abc
from typing import Any

import gymnasium as gym
import numpy as np
import pygame

class Renderer(abc.ABC):
    """Abstact base class for renderers that render BAS wrappers.

    `self.` should be used. to access public attributes of the render wrapper
    and private attributes of the corresponding `WRAPPER` instance.

    This class also contains bindings for pygame draw functions
    that automatically convert from world size to window size.
    """

    WRAPPER = None

    def __init__(self, render_wrapper: "RenderWrapper", instance: gym.Wrapper):
        """Initalize renderer and store wrappers.

        Args:
            render_wrapper: Render wrapper needed for window scale.
            instance: Actual `WRAPPER` instance corresponding to this renderer with private attributes.
        """
        self._render_wrapper = render_wrapper
        self._instance = instance

    def __getattr__(self, name: str) -> Any:
        """Provide access to public attributes of the render_wrapper
        and private attributes of the corresponding `WRAPPER` instance.
        """
        if name.startswith("_"):
            return getattr(self._instance, name)

        return getattr(self._render_wrapper, name)

    @abc.abstractmethod
    def render(self, canvas: pygame.Surface):
        """Draw additional stuff from the wrapper on the canvas."""

    def line(
        self,
        canvas: pygame.Surface,
        color: str,
        start_position: np.ndarray,
        end_position: np.ndarray,
        width: float,
    ):
        """Draw a line on the canvas in window size from geometric arguments in world size."""
        scale = self.window_scale
        pygame.draw.line(
            canvas, color, scale * start_position, scale * end_position, width
        )

    def circle(
        self,
        canvas: pygame.Surface,
        color: str,
        center: np.ndarray,
        radius: float,
        width: int = 0,
    ):
        """Draw a circle on the canvas in window size from geometric arguments in world size."""
        scale = self.window_scale
        pygame.draw.circle(canvas, color, scale * center, scale * radius, width)


from lxml import etree
import webob

from nova.api.openstack.compute.contrib import quota_classes
from nova.api.openstack import wsgi
from nova import test
from nova.tests.api.openstack import fakes

def quota_set(class_name):
    return {'quota_class_set': {'id': class_name, 'metadata_items': 128,
                                'ram': 51200, 'floating_ips': 10,
                                'fixed_ips': -1, 'instances': 10,
                                'injected_files': 5, 'cores': 20,
                                'injected_file_content_bytes': 10240,
                                'security_groups': 10,
                                'security_group_rules': 20, 'key_pairs': 100,
                                'injected_file_path_bytes': 255}}

class QuotaClassSetsTest(test.TestCase):

    def setUp(self):
        super(QuotaClassSetsTest, self).setUp()
        self.controller = quota_classes.QuotaClassSetsController()

    def test_format_quota_set(self):
        raw_quota_set = {
            'instances': 10,
            'cores': 20,
            'ram': 51200,
            'floating_ips': 10,
            'fixed_ips': -1,
            'metadata_items': 128,
            'injected_files': 5,
            'injected_file_path_bytes': 255,
            'injected_file_content_bytes': 10240,
            'security_groups': 10,
            'security_group_rules': 20,
            'key_pairs': 100,
            }

        quota_set = self.controller._format_quota_set('test_class',
                                                      raw_quota_set)
        qs = quota_set['quota_class_set']

        self.assertEqual(qs['id'], 'test_class')
        self.assertEqual(qs['instances'], 10)
        self.assertEqual(qs['cores'], 20)
        self.assertEqual(qs['ram'], 51200)
        self.assertEqual(qs['floating_ips'], 10)
        self.assertEqual(qs['fixed_ips'], -1)
        self.assertEqual(qs['metadata_items'], 128)
        self.assertEqual(qs['injected_files'], 5)
        self.assertEqual(qs['injected_file_path_bytes'], 255)
        self.assertEqual(qs['injected_file_content_bytes'], 10240)
        self.assertEqual(qs['security_groups'], 10)
        self.assertEqual(qs['security_group_rules'], 20)
        self.assertEqual(qs['key_pairs'], 100)

    def test_quotas_show_as_admin(self):
        req = fakes.HTTPRequest.blank(
            '/v2/fake4/os-quota-class-sets/test_class',
            use_admin_context=True)
        res_dict = self.controller.show(req, 'test_class')

        self.assertEqual(res_dict, quota_set('test_class'))

    def test_quotas_show_as_unauthorized_user(self):
        req = fakes.HTTPRequest.blank(
            '/v2/fake4/os-quota-class-sets/test_class')
        self.assertRaises(webob.exc.HTTPForbidden, self.controller.show,
                          req, 'test_class')

    def test_quotas_update_as_admin(self):
        body = {'quota_class_set': {'instances': 50, 'cores': 50,
                                    'ram': 51200, 'floating_ips': 10,
                                    'fixed_ips': -1, 'metadata_items': 128,
                                    'injected_files': 5,
                                    'injected_file_content_bytes': 10240,
                                    'injected_file_path_bytes': 255,
                                    'security_groups': 10,
                                    'security_group_rules': 20,
                                    'key_pairs': 100}}

        req = fakes.HTTPRequest.blank(
            '/v2/fake4/os-quota-class-sets/test_class',
            use_admin_context=True)
        res_dict = self.controller.update(req, 'test_class', body)

        self.assertEqual(res_dict, body)

    def test_quotas_update_as_user(self):
        body = {'quota_class_set': {'instances': 50, 'cores': 50,
                                    'ram': 51200, 'floating_ips': 10,
                                    'fixed_ips': -1, 'metadata_items': 128,
                                    'injected_files': 5,
                                    'injected_file_content_bytes': 10240,
                                    'security_groups': 10,
                                    'security_group_rules': 20,
                                    'key_pairs': 100,
                                    }}

        req = fakes.HTTPRequest.blank(
            '/v2/fake4/os-quota-class-sets/test_class')
        self.assertRaises(webob.exc.HTTPForbidden, self.controller.update,
                          req, 'test_class', body)

class QuotaTemplateXMLSerializerTest(test.TestCase):
    def setUp(self):
        super(QuotaTemplateXMLSerializerTest, self).setUp()
        self.serializer = quota_classes.QuotaClassTemplate()
        self.deserializer = wsgi.XMLDeserializer()

    def test_serializer(self):
        exemplar = dict(quota_class_set=dict(
                id='test_class',
                metadata_items=10,
                injected_file_path_bytes=255,
                injected_file_content_bytes=20,
                ram=50,
                floating_ips=60,
                fixed_ips=-1,
                instances=70,
                injected_files=80,
                security_groups=10,
                security_group_rules=20,
                key_pairs=100,
                cores=90))
        text = self.serializer.serialize(exemplar)

        tree = etree.fromstring(text)

        self.assertEqual('quota_class_set', tree.tag)
        self.assertEqual('test_class', tree.get('id'))
        self.assertEqual(len(exemplar['quota_class_set']) - 1, len(tree))
        for child in tree:
            self.assertTrue(child.tag in exemplar['quota_class_set'])
            self.assertEqual(int(child.text),
                             exemplar['quota_class_set'][child.tag])

    def test_deserializer(self):
        exemplar = dict(quota_class_set=dict(
                metadata_items='10',
                injected_file_content_bytes='20',
                ram='50',
                floating_ips='60',
                fixed_ips='-1',
                instances='70',
                injected_files='80',
                security_groups='10',
                security_group_rules='20',
                key_pairs='100',
                cores='90'))
        intext = ("<?xml version='1.0' encoding='UTF-8'?>\n"
                  '<quota_class_set>'
                  '<metadata_items>10</metadata_items>'
                  '<injected_file_content_bytes>20'
                  '</injected_file_content_bytes>'
                  '<ram>50</ram>'
                  '<floating_ips>60</floating_ips>'
                  '<fixed_ips>-1</fixed_ips>'
                  '<instances>70</instances>'
                  '<injected_files>80</injected_files>'
                  '<cores>90</cores>'
                  '<security_groups>10</security_groups>'
                  '<security_group_rules>20</security_group_rules>'
                  '<key_pairs>100</key_pairs>'
                  '</quota_class_set>')

        result = self.deserializer.deserialize(intext)['body']
        self.assertEqual(result, exemplar)


# Standard library modules
import logging
import os
import pandas as pd
import awswrangler as wr
import boto3
import sys
import json
import numpy as np
from ast import literal_eval
from datetime import datetime

# Local reusable modules
from hr_data_csv_schema import ( read_schema,
                                 date_columns_schema,
                                 json_columns_schema,
                                 custom_data_schema,
                                 write_schema )

# Amazon Simple Storage Service (S3)
s3 = boto3.client('s3')

# Event logging
logger = logging.getLogger(__name__)
logging.getLogger().setLevel(logging.INFO)

'''
Helper functions
'''

def write_to_s3(df, target_path):
    """
    Write pandas dataframe data as parquet file(s) on Amazon S3.
    :param df: Pandas DataFrame name
    :param target path: full target path to store data
    :return: Dictionary with the following information:
        ‘paths’: List of all stored files paths on S3.
        ‘partitions_values’: Dictionary of partitions added with keys as S3 path locations and values as a list of partitions values as str.
    """

    try:
        stored_files = wr.s3.to_parquet(
            df=df,
            path=target_path,
            index=False,
            compression="snappy",
            use_threads=False,
            dataset=True,
            mode="overwrite"
        )
    except Exception as e:
        raise(e)

    logging.info("stored_files {}".format(stored_files))
    return stored_files

def read_hr_data_csv(file_path, file_schema, date_cols):
    """
    Extract HR Data column values from the CSV file
    :param file_path: HR Data file path on the S3
    :param file_schema: CSV schema
    :param date_cols: date columns to parse
    :return: returns raw_df pandas dataframe
    """

    # Read HR Data CSV on S3 using pandas dataframe
    raw_df = wr.s3.read_csv(path=file_path, dtype=file_schema, parse_dates=date_cols)

    # Return pandas dataframe
    return raw_df

def get_file_name(ib_bucket_name, ib_bucket_prefix):
    """
    Extract HR Data CSV file name from source path
    :param ib_bucket_name: HR Data S3 bucket name
    :param ib_bucket_prefix: HR Data CSV file path prefix on S3
    :return: returns file name as a string
    """
    try:
        result = s3.list_objects_v2(Bucket=ib_bucket_name, Prefix=ib_bucket_prefix)
        result_list = result['Contents'][0]['Key'].split("/")

        # Return file name based on source file path
        return result_list[-1]
    except:
        return None

def get_source_file_date_time(file_name):
    """
    Extract HR Data CSV file date from file name
    :param file_name: HR Data CSV file name on S3
    :return: returns file date as timestamp
    """
    try:
        # Split the file name based on .csv and _ to extract file name elements
        file_name_list = file_name.split(".csv")[0].split("_")

        # Extract date and date time from list
        source_file_date_str = file_name_list[-2]
        source_file_date_time_str = file_name_list[-2] + file_name_list[-1]

        logging.info("source_file_date_str= {}".format(source_file_date_str))
        logging.info("source_file_date_time_str= {}".format(source_file_date_time_str))

        # Return list of timestamp by converting string to datetime
        return [pd.to_datetime(source_file_date_str), pd.to_datetime(source_file_date_time_str)]
    except:
        return [None, None]

def flatten_json_columns(df, json_cols, custom_df):
    """
    This function flattens JSON columns to individual columns
    It merges the flattened dataframe with expected dataframe to capture missing columns from JSON
    :param df: HR Data CSV raw dataframe
    :param json_cols: custom data columns in CSV's
    :param custom_df: expected dataframe
    :return: returns df pandas dataframe
    """

    # Loop through all JSON columns
    for column in json_cols:
        if not df[column].isnull().all():
            # create a temp col to preserve the orginal data
            df['custom_data_temp'] = df[column]
            # Replace None and NaN with empty braces
            df[column].fillna(value='{}', inplace=True)
            try:
                # Deserialize's a str instance containing a JSON document to a Python object
                df[column] = [json.loads(row, strict=False) for row in df[column]]
            except TypeError:
                # Convert all values to string using literal eval
                df[column] = df[column].apply(lambda x: literal_eval(str(x)))
            # Normalize semi-structured JSON data into a flat table
            column_as_df = pd.json_normalize(df[column])
            # Extract main column name and attach it to each sub column name
            column_as_df.columns = [f"{column}_{subcolumn}" for subcolumn in column_as_df.columns]
            # Replace empty strings with None
            column_as_df.replace('', np.nan, inplace=True)
            # Replace orginal data with temp data
            df[column] = df['custom_data_temp']
            # Merge extracted result from custom_data field with expected fields
            result_df = pd.merge(column_as_df, custom_df, how='left')
            # Drop the temp column and merge the flattened dataframe with orginal dataframe
            df = df.drop('custom_data_temp', axis=1).merge(result_df, right_index=True, left_index=True)
        else:
            df = pd.concat([df, custom_df], axis=1)

    # Return dataframe with flatten columns
    return df

def parse_hr_data_csv_df(df, bucket_name, bucket_prefix, entity_name):
    """
    Parse HR Data CSV column(s) values from the dataframe
    :param df: HR Data CSV file path on the S3
    :param bucket_name: AWS S3 bucket name
    :param bucket_prefix: AWS S3 bucket prefix
    :param entity_name: HR Data CSV entity name
    :return: returns df pandas dataframe
    """

    # Check if the entity is part of json columns schema
    if entity_name in json_columns_schema.keys():

        # Pre defined custom data expected data frame
        custom_data_expected_df=pd.DataFrame.from_dict(custom_data_schema[entity_name])

        if entity_name == 'user_point_transactions':
            df['reference_type_json'] = [column if column[0] == '{' else None for column in df['reference_type']]
            df['reference_type'] = [None if column[0] == '{' else column for column in df['reference_type']]

            # Flatten reference_type_json CSV column to individual columns
            upt_df = flatten_json_columns(df=df,
                                          json_cols=json_columns_schema[entity_name],
                                          custom_df=custom_data_expected_df)

            parsed_df = upt_df.drop('reference_type_json', axis=1)
        else:
            # Flatten CSV JSON Columns to individual columns
            parsed_df = flatten_json_columns(df=df,
                                             json_cols=json_columns_schema[entity_name],
                                             custom_df=custom_data_expected_df)
    else:
        # Return raw dataframe
        parsed_df = df

    # Get file name based on s3 source path
    hr_data_csv_file_name = get_file_name(ib_bucket_name=bucket_name, ib_bucket_prefix=bucket_prefix)
    logging.info("hr_data_csv_file_name= {}".format(hr_data_csv_file_name))

    # Create file name column in parsed data frame
    parsed_df['source_file_name'] = hr_data_csv_file_name

    # Get source file date and datetime into a list based on HR Data csv file name
    hr_data_csv_source_file_date_and_datetime = get_source_file_date_time(file_name = hr_data_csv_file_name)

    # Create source file date and datetime column(s) in parsed data frame
    parsed_df['source_file_date'] = hr_data_csv_source_file_date_and_datetime[0]
    parsed_df['source_file_datetime'] = hr_data_csv_source_file_date_and_datetime[1]

    # Return pandas dataframe
    return parsed_df

'''
CSV Parsing function with parameters
'''

def parsing_launcher(ib_bucket_name, ib_bucket_name_prefix, dlp_bucket_name, dlp_bucket_name_prefix, hive_style_partitions, source_entities_list):
    """
    Invokes the HR Data CSV Parser to ingest data to data lake
    :param ib_bucket_name: AWS S3 bucket name in data platform accounts
    :param ib_bucket_name_prefix: AWS S3 bucket name prefix
    :param dlp_bucket_name: AWS S3 data lake processing bucket name in data platform accounts
    :param dlp_bucket_name_prefix: AWS S3 datalake processing bucket name prefix
    :param hive_style_partitions: S3 partitions to read the file
    :param source_entities_list: List of entities to process
    :return: None
    """

    for entity in source_entities_list:

        logging.info("entity_name= {}".format(entity))

        # Constructing source path based on source entity
        s3_bucket = "s3://" + ib_bucket_name + "/"
        s3_bucket_prefix = ib_bucket_name_prefix + "/" + entity + "/" + hive_style_partitions
        source_path = s3_bucket + s3_bucket_prefix
        logging.info("Full source_path {}".format(source_path))

        # Constructing target path based on source entity
        target_path =  "s3://" + dlp_bucket_name + "/" + dlp_bucket_name_prefix + "/" + entity + "/" + hive_style_partitions
        logging.info("Full target_path {}".format(target_path))

        # Read HR Data CSV data from S3 path
        raw_df = read_hr_data_csv(file_path=source_path,
                                                file_schema=read_schema[entity],
                                                date_cols=date_columns_schema[entity])

        # If raw data frame is not empty then parse it
        if not raw_df.empty:
            # Parse CSV data
            parsed_hr_data_df = parse_hr_data_csv_df(df=raw_df,
                                                                                bucket_name=ib_bucket_name,
                                                                                bucket_prefix=s3_bucket_prefix,
                                                                                entity_name= entity)

            try:
                # Convert column data types using astype with write schema
                hr_data_astype_dtypes = parsed_hr_data_df.astype(write_schema[entity], copy=True)
            except:
                # Convert column data types using astype with write schema and ignore errors
                hr_data_astype_dtypes = parsed_hr_data_df.astype(write_schema[entity], copy=True, errors='ignore')

            # Write result to S3
            write_to_s3(df=hr_data_astype_dtypes,
                        target_path=target_path)

if __name__ == "__main__":
    # Accept parameters
    s3_bucket_name = sys.argv[1]
    s3_bucket_name_prefix = sys.argv[2]
    s3_dlp_bucket_name = sys.argv[3]
    s3_dlp_bucket_name_prefix = sys.argv[4]
    date_partitions = sys.argv[5]
    list_of_source_entities = literal_eval(sys.argv[6])

    # Invoke launcher
    parsing_launcher(ib_bucket_name=s3_bucket_name,
                     ib_bucket_name_prefix=s3_bucket_name_prefix,
                     dlp_bucket_name=s3_dlp_bucket_name,
                     dlp_bucket_name_prefix=s3_dlp_bucket_name_prefix,
                     hive_style_partitions=date_partitions,
                     source_entities_list=list_of_source_entities)


from typing import List
from urllib import request

import os

def get_stopwords_from_slothlib(filepath: str):
    """
    SlothLibからストップワードを取得する.
    :param filepath キャッシュ先ファイルパス
    :return: list of step words.
    """
    stopwords = []
    try:
        with open(filepath, 'r') as file:
            stopwords = file.read().split(",")
    except FileNotFoundError:
        ja_slothlib_file = request.urlopen('http://svn.sourceforge.jp/svnroot/slothlib/CSharp/'
                                           'Version1/SlothLib/NLP/Filter/StopWord/word/Japanese.txt')
        stopwords.extend(
            list(filter(lambda x: not x == u'', [line.decode('utf-8').strip() for line in ja_slothlib_file])))

        english_stopwords_file = request.urlopen("http://algs4.cs.princeton.edu/35applications/stopwords.txt")
        stopwords.extend(
            list(filter(lambda x: not x == u'',
                        [line.decode('utf-8').strip() for line in english_stopwords_file])))
        with open(filepath, 'w') as file:
            file.write(",".join(stopwords))
    return stopwords

def get_stopwords_from_csv_files(filepath_list: List[str] = None, dirpath: str = None):
    stopwords = []
    if dirpath:
        filepath_ls = list(map(lambda x: os.path.join(dirpath, x), os.listdir(dirpath)))
    else:
        filepath_ls = filepath_list
    for filepath in filepath_ls:
        with open(filepath, 'r') as file:
            stopwords.extend(file.read().split(","))
    return list(set(stopwords))


"""CFPS = Categories, Formats, People, Songs. They are quite complex, so they were implemented
separately for the POST /releases handling. Note that ALL of the functions here insert things into
their "standalone" tables. We also test the mapping."""

import unittest

from sqlalchemy import asc

from app import db
from apps.songs.models import Songs, ReleasesSongsMapping
from apps.releases.add_cfps import add_songs
from apps.releases.models import Releases
from apps.utils.time import get_datetime

class TestAddPeople(unittest.TestCase):
    def setUp(self):
        # The release we use for these tests
        release = Releases(
            Title="UnitTest",
            Date=get_datetime(),
            Artist="UnitTest Arts",
            Credits="UnitTest is a good and fun activity",
            Created=get_datetime(),
            ReleaseCode="TEST001"
        )
        db.session.add(release)
        db.session.commit()
        self.release_id = release.ReleaseID

        # For testing an existing song
        song = Songs(
            Title="UnitTest Song",
            Duration=99,
        )
        db.session.add(song)
        db.session.commit()
        self.existing_song_id = song.SongID

    def tearDown(self):
        for song in Songs.query.filter(Songs.Title.like("UnitTest%")).all():
            db.session.delete(song)
        db.session.commit()

        release = Releases.query.filter_by(ReleaseID=self.release_id).first()
        db.session.delete(release)
        db.session.commit()

    def test_adding_songs_by_existing_id(self):
        """Should not add new entries to Songs, but should map the songs to the current release."""
        songs_dict = {self.existing_song_id: 240}
        add_songs(self.release_id, [songs_dict])

        songs = Songs.query.all()
        mapping = ReleasesSongsMapping.query.filter_by(ReleaseID=self.release_id).all()

        self.assertEqual(1, len(songs))
        self.assertEqual(1, len(mapping))
        self.assertEqual(self.existing_song_id, mapping[0].SongID)
        self.assertEqual(240, mapping[0].ReleaseSongDuration)

    def test_adding_songs_with_new_string_values(self):
        """Should create new entries to Songs and then do the mapping for the current release."""
        song1 = {"UnitTest Songie": 125}
        song2 = {"UnitTest Secunda": 290}
        songs = [song1, song2]
        add_songs(self.release_id, songs)

        songs = Songs.query.all()
        mapping = ReleasesSongsMapping.query.filter_by(ReleaseID=self.release_id).order_by(
            asc(ReleasesSongsMapping.ReleasesSongsMappingID)
        ).all()

        # Should be connected to the release
        s1 = Songs.query.filter_by(Title="UnitTest Songie").first()
        s2 = Songs.query.filter_by(Title="UnitTest Secunda").first()

        self.assertEqual(3, len(songs))
        self.assertFalse(s1 is None)
        self.assertFalse(s2 is None)
        self.assertEqual(mapping[0].SongID, s1.SongID)
        self.assertEqual(mapping[0].ReleaseSongDuration, 125)
        self.assertEqual(mapping[1].SongID, s2.SongID)
        self.assertEqual(mapping[1].ReleaseSongDuration, 290)

    def test_adding_songs_with_existing_string_values(self):
        """Should not add a new entry to Songs, and the mapping should be correct for current
        release."""
        songs_list = [{"UnitTest Song": 320}]
        add_songs(self.release_id, songs_list)

        songs = Songs.query.all()
        mapping = ReleasesSongsMapping.query.filter_by(ReleaseID=self.release_id).all()

        self.assertEqual(1, len(songs))
        self.assertEqual(songs[0].SongID, mapping[0].SongID)
        self.assertEqual(320, mapping[0].ReleaseSongDuration)
        # The original duration should be intact in Songs table
        self.assertEqual(99, songs[0].Duration)

    def test_adding_songs_with_nonexisting_ids(self):
        """When you add SongIDs that do not exist, no mapping should happen and no new entries
        should appear in ReleasesSongsMapping."""
        add_songs(self.release_id, [{0: 234}])

        songs = Songs.query.all()
        mapping = ReleasesSongsMapping.query.filter_by(ReleaseID=self.release_id).first()

        self.assertEqual(1, len(songs))
        self.assertTrue(mapping is None)


"""added deleted column to user

Revision ID: 96bd08d540f1
Revises: 80b88a99a8fd
Create Date: 2021-10-09 19:00:44.532811

"""
from alembic import op
import sqlalchemy as sa

# revision identifiers, used by Alembic.
revision = '96bd08d540f1'
down_revision = '80b88a99a8fd'
branch_labels = None
depends_on = None

def upgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.add_column('user', sa.Column('deleted', sa.Boolean(), nullable=True))
    # ### end Alembic commands ###

def downgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.drop_column('user', 'deleted')
    # ### end Alembic commands ###


import numpy as np
import matplotlib.pyplot as plt

def findCircle(array):
    M = np.empty((0,3),int)
    b = np.empty((0,1),int)
    x = np.array([])
    y = np.array([])
    startx = []
    starty = []
    endx = []
    endy = []
    f = "./meres/keres105241.txt"
    with open(f,"r") as file:
        for line in file:
            currentline = line.split(",")
            if (len(currentline) == 2):
                xi = int(currentline[0])
                yi = int(currentline[1])
                M = np.append(M, np.array([[xi,yi,1]]),axis=0)
                b = np.append(b, np.array([[-xi*xi-yi*yi]]),axis=0)
                x = np.append(x,xi)
                y = np.append(y,yi)
            elif(len(currentline) == 3):
                xi = int(currentline[0])
                yi = int(currentline[1])
                if ("start" in currentline[2]):
                    startx.append(xi)
                    starty.append(yi)
                if ("end" in currentline[2]):
                    endx.append(xi)
                    endy.append(yi)

    a = np.linalg.lstsq(M, b)[0]
    a1 = -a[0]/2
    a2 = -a[1]/2

    print(a1)
    print(a2)

    plt.scatter(x, y)
    plt.scatter(a1,a2,color='red')
    plt.scatter(startx,starty,color='yellow')
    plt.scatter(endx,endy,color='green')
    plt.xlabel('X')
    plt.ylabel('Y')
    plt.autoscale('True','both','False')
    plt.show()

findCircle([])


from kivy.uix.screenmanager import Screen
from kivy.uix.actionbar import ActionBar, ActionView, ActionPrevious, ActionButton, ActionItem
from kivy.uix.gridlayout import GridLayout
from kivy.uix.scrollview import ScrollView
from kivy.uix.stacklayout import StackLayout
from kivy.uix.textinput import TextInput
from kivy.uix.button import Button
from kivy.uix.image import Image
from kivy.uix.relativelayout import RelativeLayout
from kivy.core.window import Window

class AddNew(RelativeLayout, ActionItem):
    def __init__(self, **kwargs):
        super(AddNew, self).__init__(**kwargs)
        self.size_hint = (None, None)
        self.height = Window.height*.06
        self.width = Window.width*.20
        self.button = Button(background_color = (0,0.75,1,1))
        self.img = Image(source = 'plus.png',
                         pos_hint={'center_x': .5, 'center_y': .5},
                         size_hint = (None, None),
                         height = (Window.height*.06)*.90,
                         width = (Window.width*.20)*.30)
        self.add_widget(self.button)
        self.add_widget(self.img)

class SearchButton(RelativeLayout, ActionItem):
    def __init__(self, **kwargs):
        super(SearchButton, self).__init__(**kwargs)
        self.size_hint = (None, None)
        self.height = Window.height*.06
        self.width = Window.width*.20
        self.button = Button(background_color = (0.25,0.41,0.88,1))
        self.img = Image(source = 'searchbar.png',
                         pos_hint={'center_x': .5, 'center_y': .5},
                         size_hint = (None, None),
                         height = (Window.height*.06)*.90,
                         width = (Window.width*.20)*.30)
        self.add_widget(self.button)
        self.add_widget(self.img)

class CloseButton(Button, ActionItem):
    def __init__(self, **kwargs):
        super(CloseButton, self).__init__(**kwargs)
        self.background_color = (1,0,0,1)
        self.font_size = '25sp'
        self.text = '[b]X[/b]'
        self.size_hint = (None, None)
        self.height = Window.height*.06
        self.width = Window.width*.20
        self.markup = True

class Txt(TextInput, ActionItem):

    def __init__(self, **kwargs):
        super(Txt, self).__init__(**kwargs)
        self.size_hint_y = None
        self.size_hint_x = None
        self.height = Window.height*.06
        self.width = Window.width*.30
        self.multiline = False
        self.font_size = '20sp'

class Notes(Screen):
    def __init__(self, **kwargs):
        super(Notes, self).__init__(**kwargs)
        self.name = 'notes'
        self.stackcon = StackLayout(cols = 1,
                                    spacing=0,)

        #Actionbar
        self.actionbar = ActionBar(background_color = (0.50,0.50,0.50,1),
                                   pos_hint={'top':1},
                                   height = Window.height*.06,
                                   width = Window.width)

        self.actionview = ActionView()
        self.actionprevious = ActionPrevious(with_previous=False,
                                        app_icon = 'icon.png',
                                        previous_image = '',
                                        title = '')

        self._addnotebutton = AddNew()
        self.addnotebutton = self._addnotebutton.button
        self.stack = GridLayout(cols = 1,
                               spacing=0,
                               size_hint_y=None)

        self.scroll = ScrollView(size_hint=(1, None),
                                 size=(Window.width, Window.height-50))

        self.txt = Txt(text = '')

        self.close_button = CloseButton()
        self._search = SearchButton()
        self.search_button = self._search.button

        self.stack.bind(minimum_height=self.stack.setter('height'))

        self.actionbar.add_widget(self.actionview)
        self.actionview.add_widget(self.actionprevious)
        self.actionview.add_widget(self.txt)
        self.actionview.add_widget(self._search)
        self.actionview.add_widget(self._addnotebutton)

        self.stackcon.add_widget(self.actionbar)
        self.scroll.add_widget(self.stack)
        self.stackcon.add_widget(self.scroll)
        self.add_widget(self.stackcon)

        self.search_button.bind(on_release = self.hidetextbar)
        self.close_button.bind(on_release = self.showtextbar)

    def hidetextbar(self, touch):
        if self.txt.text != '':
            self.actionview.remove_widget(self.txt)
            self.actionview.remove_widget(self._search)
            self.actionview.add_widget(self.close_button, 1)
    def showtextbar(self, touch):
        self.actionview.add_widget(self.txt, -2)
        self.actionview.add_widget(self._search, -3)
        self.actionview.remove_widget(self.close_button)


__all__ = ["PLAYER1", "PLAYER2", "Connect4"]

PLAYER1, PLAYER2 = "red", "yellow"

class Connect4:
    """
    A Connect Four game.

    Play moves with :meth:`play`.

    Get past moves with :attr:`moves`.

    Check for a victory with :attr:`winner`.

    """

    def __init__(self):
        self.moves = []
        self.top = [0 for _ in range(7)]
        self.winner = None

    @property
    def last_player(self):
        """
        Player who played the last move.

        """
        return PLAYER1 if len(self.moves) % 2 else PLAYER2

    @property
    def last_player_won(self):
        """
        Whether the last move is winning.

        """
        b = sum(1 << (8 * column + row) for _, column, row in self.moves[::-2])
        return any(b & b >> v & b >> 2 * v & b >> 3 * v for v in [1, 7, 8, 9])

    def play(self, player, column):
        """
        Play a move in a column.

        Returns the row where the checker lands.

        Raises :exc:`RuntimeError` if the move is illegal.

        """
        if player == self.last_player:
            raise RuntimeError("It isn't your turn.")

        row = self.top[column]
        if row == 6:
            raise RuntimeError("This slot is full.")

        self.moves.append((player, column, row))
        self.top[column] += 1

        if self.winner is None and self.last_player_won:
            self.winner = self.last_player

        return row


from core import *
from manim import *
from common import *

class p18(MovingCameraScene):
    def construct(self):
        ax = NumberLine(
            x_range=[0, 8],
            length=10,
            color=BLUE,
            include_numbers=False,
            line_to_number_buff=MED_LARGE_BUFF,
            z_index=-5
        )
        self.bring_to_back(ax)

        ant = Ant(ax=ax, pos=7)
        P = Portal(x=8, y=0, open=1, ax=ax)
        p1 = Portal(x=3.5, y=2.25, open=1, ax=ax)
        p2 = Portal(x=4.75, y=2.75, open=1, ax=ax)
        p3 = Portal(x=5.75, y=4, open=1, ax=ax)
        pmobs = [p.mob for p in (p1,p2,p3)]

        equation = VGroup().shift(3*DOWN)
        def eq_add(*o : VMobject):
            equation.add(*o)
            self.play(equation.animate.set_x(0))

        # Suppose we're calculating the return trip time, or the dp value for portal i.
        dp_i = MathTex("dp_i = ").shift(3*DOWN)
        eq_add(dp_i)
        self.play( FadeIn(P.mob), Create(ant.mob), *map(FadeIn, pmobs) )

        anim = [ScaleInPlace(o, scale_factor=1.75, rate_func=hint) for o in P.mobs]
        self.play( *anim )

        # TODO: add X_i and Y_i label

        # let's first ignore the portals in the middle
        tf = lambda o : o.animate.set_opacity(0.3)
        self.play( *map(tf, pmobs) )
        self.wait(1)

        # No matter what, it's clear that on the return trip,
        # we will need to at least travel this yellow distance X[i] - Y[i]. Let's call this term dist
        brace = BraceBetweenPoints(ax.n2p(0), ax.n2p(8)).shift(0.5*DOWN)
        self.play( GrowFromCenter(brace), FadeToColor(ax,color=RED) )
        dist = MathTex("dist_i = X_i - Y_i", color=YELLOW).next_to(brace, DOWN)
        self.play( Write(dist, run_time=0.75) )
        self.wait(1.5)
        tf = Transform(dist, MathTex("dist_i", color=YELLOW).next_to(equation, RIGHT))
        self.play( tf, ShrinkToCenter(brace) )
        eq_add(dist)
        self.wait(1)

        # Next, let's see how we can deal with the portals in the middle.
        # TODO: actually have portals in the middle
        # TODO: figure out how to do the starry thing
        # Let's first abstract away this messy cluster of interleaved portals as a single term:
        # we'll call it cost
        BB = BlackBox(self, height=2.5, width=6)
        self.play( GrowFromCenter(BB) )
        pent = MathTex("cost_i = \,\,?", color=PINK)
        self.add_foreground_mobject(pent)
        self.bring_to_front(pent)
        plus = MathTex("+").next_to(equation,RIGHT)
        self.play( Write(pent, run_time=1.7) )
        self.wait(1)

        # TODO: continuously center the VGroup of labels at the bottom
        pent2 = MathTex("cost_i",color=PINK).next_to(plus, RIGHT)
        # self.add_foreground_mobject(pent2)
        self.play( Transform(pent, pent2), Write(plus) )
        eq_add(plus, pent)

        # TODO: consider this zoom option
        # scene = VGroup(*[o for o in self.mobjects if isinstance(o, VMobject)])
        # self.play( ScaleInPlace(scene, scale_factor=3) )

        # TODO: if use this, do magic in post? with a separate scene?
        self.camera.frame.save_state()
        self.play(self.camera.frame.animate.set_width(BB.width*1.05))

        tf = lambda o : o.animate.set_opacity(1)
        self.add_foreground_mobjects(*pmobs)
        self.play( *map(tf, pmobs), rate_func=rate_functions.ease_in_out_sine )

        # in terms of calculating how much time these middle portals will set us back
        # we've already done most of the heavy lifting!
        # recall that we're calculating dp_i. This means that, since
        # we've made it this far, the dp values for 0, all the way thru i-1
        # are already solved!

        # first of all, we can ignore the exit points of the portals.
        exits = [p.mobs.exit for p in (p1,p2,p3)]
        self.play( *map(Uncreate, exits) )
        self.wait(3)

        # in fact, even if the exits lay outside of the range of this black box, i.e.
        # to the left of Y_i, we can still ignore them, because we've already accounted
        # for them in our previous dp calculations.

        # next, we're absolutely sure that each of these portals are open, since they're
        # behind the ant, which we proved a while ago. We also already know each of their return
        # trip times, so we can effectively abstract away each of these entrances as a *flat*
        # penalty time!
        bbs = [BlackBox(self, height=1, width=1).move_to(p.mobs.entrance) for p in (p1,p2,p3)]
        labs = [MathTex(f"dp_{{i-{3-i}}}", font_size=24).move_to(bbs[i]) for i in range(3)]
        vg = [VGroup(bbs[i], labs[i]) for i in range(3)]
        self.play( *map(GrowFromCenter, bbs) )
        for l in labs: self.add_foreground_mobject(l)
        self.play( *map(Write, labs) )

        # we also don't care exactly where they are, just as long as they're caught in the middle
        # of our return trip, we'll have to pay the penalty time.
        for p in (p1,p2,p3):
            p.remove(self)
        mtp1, mtp2 = MathTex("+"), MathTex("+")
        boxes = VGroup(vg[0], mtp1, vg[1], mtp2, vg[2])
        self.play( Transform(boxes, boxes.copy().center().arrange(RIGHT)) )
        self.add_foreground_mobjects(mtp1, mtp2)
        self.play( Write(mtp1), Write(mtp2) )

        self.wait(2)
        self.play(Restore(self.camera.frame))
        self.wait(1)

        keep = {*equation.submobjects}
        self.play( *map(FadeOut, all_vmobs_in(self, exclude=keep)) )

        self.play( equation.animate.center().shift(UP) )
        self.play( equation.animate.arrange(RIGHT, buff=0.75))

        dist_mt = MathTex("X_i - Y_i", color=YELLOW).next_to(dp_i, RIGHT)
        plus2 = plus.copy().next_to(dist_mt, RIGHT)
        pent_mt = MathTex("\sum^{i-1}_{k=j} dp_k", color=PINK).next_to(plus2, RIGHT)
        labs = VGroup(dist_mt, pent_mt)
        tf = lambda o,mt : o.animate.move_to( mt.get_center()+1.2*UP )
        self.play( *map(Write, labs.submobjects),
                   Transform(plus, plus2),
                   tf(dist, dist_mt),
                   tf(pent, pent_mt)  )

        p = lambda o : isinstance(o, MathTex)
        mobs = VGroup( *all_vmobs_in(self, pred=p) )
        self.play(mobs.animate.center())

        where = Tex("where $j$ is the smallest index such that $X_j > Y_i$", font_size=20).shift(1.5*DOWN)
        self.play( Write(where) )

        self.wait(3)


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Edit_OEM_ROM_Project
Copyright (C) 2022 Abdalrohman Alnasier

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from __future__ import print_function

import argparse
import os
import re
import subprocess
import sys
import time
from pathlib import Path
from typing import List, Optional, Tuple

from eorp.config import Configs
from eorp.create_ext4 import CreateExt4
from eorp.create_super import CreateSuper
from eorp.debloater import Debloater
from eorp.extract_firmware import ExtractFirmware
from eorp.interface import (
    Banner,
    Color,
    Colors,
    Fx,
    Mv,
    Term,
)
from eorp.logging import Log
from eorp.system_info import SystemDetector
from eorp.utils import (
    count,
    dir_size,
    find_text_in_file,
    init_project,
    list_folder,
    remove,
    replace_in_file,
    run_command,
)
from eorp.variables import CONFIG_FILE, CONFIG_DIR, LOG_FILE, ROOT_DIR

def check_pip() -> Optional[bool]:
    """
    Check if pip is installed on the system. If not, try to install it.

    Returns:
        True if pip is found or installed successfully, False otherwise.
    """
    try:
        # Check if pip is already installed by running "pip --version"
        python_executable = sys.executable
        subprocess.run(
            [python_executable, "-m", "pip", "--version"],
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            check=True,
        )
        print("pip is already installed.")
        return True
    except subprocess.CalledProcessError:
        # If pip is not found, try to install it
        print("pip is not installed. Installing pip...")
        try:
            # Try to install pip using the ensurepip module
            subprocess.run([sys.executable, "-m", "ensurepip", "--default-pip"], check=True)
            print("pip installed successfully.")
            return True
        except subprocess.CalledProcessError:
            # If ensurepip fails, try to install pip using the package manager for the current Linux distribution
            distro_name = None
            try:
                with open("/etc/os-release", "r", encoding="utf-8") as os_release:
                    for line in os_release:
                        if line.startswith("ID="):
                            distro_name = line.split("=")[1].strip().lower()
            except:
                pass

            if distro_name in ["ubuntu", "debian", "kali"]:
                install_command = ["sudo", "apt", "install", "-y", "python3-pip"]
            elif distro_name == "arch":
                install_command = ["sudo", "pacman", "-S", "python-pip"]
            else:
                print(
                    f"Unsupported Linux distribution: {distro_name}. Please install pip manually."
                )
                return False

            try:
                subprocess.run(install_command, check=True)
                print("pip installed successfully.")
                return True
            except subprocess.CalledProcessError:
                print("Error installing pip. Please install it manually.")
                return False

def install_missing_module(module_name: str) -> Optional[bool]:
    """
    Install a missing Python module and the requirements from a requirements.txt file using pip.

    Args:
        module_name: The name of the module to install.

    Returns:
        True if the module and requirements are installed successfully, False otherwise.
    """
    try:
        python_executable = sys.executable
        # Install the missing module
        subprocess.run(
            [python_executable, "-m", "pip", "install", "--user", module_name],
            check=True,
        )
        print(f"Module '{module_name}' installed successfully.")

        # Install the requirements from the requirements.txt file
        if Path(ROOT_DIR).joinpath("./requirements.txt").exists():
            subprocess.run(
                [python_executable, "-m", "pip", "install", "-r", "requirements.txt"],
                check=True,
            )
            print("Requirements installed successfully.")

        return True
    except subprocess.CalledProcessError:
        print(
            f"Error installing module '{module_name}' or requirements. Please install them manually."
        )
        return False

def restart_script():
    """
    Restart the current script.
    """
    print("Restarting script...")
    os.execv(sys.executable, ["python"] + sys.argv)

try:
    from loguru import logger
except (ImportError, ModuleNotFoundError) as e:
    # Extracting module name from error message
    module_name = str(e).split()[-1].strip("'")
    print(
        f"Error: Module '{module_name}' not found. Make sure you have all required modules installed."
    )

    if check_pip():
        if install_missing_module(module_name):
            restart_script()
        else:
            exit(1)
    else:
        print("Error installing pip. Please install it manually.")
        exit(1)

# ? Variables ------------------------------------------------------------------------------------->

# Banner source data: list of tuples containing foreground color, background color, and text
BANNER_SRC: List[Tuple[str, str, str]] = [
    ("#1976d2", "#42a5f5", "███████╗ ██████╗ ██████╗ ██████╗"),
    ("#1976d2", "#42a5f5", "██╔════╝██╔═══██╗██╔══██╗██╔══██╗"),
    ("#1976d2", "#42a5f5", "█████╗  ██║   ██║██████╔╝██████╔╝"),
    ("#1976d2", "#42a5f5", "██╔══╝  ██║   ██║██╔══██╗██╔═══╝"),
    ("#1976d2", "#42a5f5", "███████╗╚██████╔╝██║  ██║██║"),
    ("#FFFFFF", "#000000", "╚══════╝ ╚═════╝ ╚═╝  ╚═╝╚═╝"),
]

# Project version
VERSION: str = "1.1"
# Project name
PROJECT_NAME: str = "EORP"

# ? Setup ----------------------------------------------------------------------------------------->
# TODO
try:
    os.makedirs(f"{CONFIG_DIR}/logs", exist_ok=True)
except PermissionError:
    print(f'ERROR!\nNo permission to write to "{CONFIG_DIR}" directory!')
    raise SystemExit(1)

# Initiate the Configs instance
config_file_path = Path(CONFIG_FILE)
config = Configs(config_file_path)

# set log level
LOG_LEVEL: str = config.load_config("MAIN", "log_level")

# Instantiate the Log class with a log file path
log = Log(log_file_path=LOG_FILE, loglevel=LOG_LEVEL)

# ? args ------------------------------------------------------------------------------------------>

def create_parser() -> argparse.ArgumentParser:
    """Create an argument parser for the command line interface.

    Returns:
        argparse.ArgumentParser: An argument parser object that can parse the command line arguments.
    """
    # TODO: Add argument to ZipCreator
    parser = argparse.ArgumentParser(
        description="Command line arguments",
        formatter_class=argparse.RawTextHelpFormatter,
    )

    parser.add_argument(
        "-p",
        "--project-name",
        dest="name",
        metavar="PROJECT",
        help="Specify project name and init folder project",
    )

    parser.add_argument(
        "-f",
        "--input-firmware",
        dest="input",
        metavar="FILE",
        required=False,
        help="Input firmware file (e.g. payload.bin, super.img, ext4.img)",
    )

    parser.add_argument(
        "-u",
        "--update-project",
        dest="project",
        action="store_true",
        help="Update project name in config.ini",
    )
    parser.add_argument(
        "-ep",
        "--extract-project",
        dest="extract_project",
        action="store_true",
        help="Extract images inside Source dir without [-f]",
    )
    parser.add_argument(
        "-l", "--list-projects", dest="lst", action="store_true", help="Projects List"
    )
    parser.add_argument(
        "-w",
        "--write-config",
        dest="write_config",
        action="store_true",
        help="Write config.ini",
    )

    parser.add_argument(
        "-b",
        "--build-image",
        dest="build_image",
        metavar="TYPE",
        type=lambda s: [t.strip() for t in s.split(",")],
        help="""Build image of type [raw:r, sparse:s, brotli:b] (Linux only).
             Multiple values can be passed separated by commas.
             raw:     This will build an ext4 filesystem.
             sparse:  This will build a sparse filesystem.
             brotli:  This will compress the sparse image with brotli.
             These options must be used in the following order: first raw, then sparse, then brotli.""",
    )

    parser.add_argument(
        "--flash",
        nargs="?",
        const="",
        dest="flash",
        metavar="FLASH",
        type=lambda s: [t.strip() for t in s.split(",")],
        help="""Flash images to devices using fastboot [recovery:r, system:s, wipe:w].
             Multiple values can be passed separated by commas.
             wipe:     This will delete all your files and photos stored on internal storage.
             recovery: This will reboot the device to recovery mode after finishing the flashing process.
             system:   This will reboot the device to system mode after finishing the flashing process.""",
    )

    parser.add_argument(
        "-s",
        "--super-image",
        nargs="?",
        const="",
        dest="super_image",
        metavar="METHOD",
        type=lambda s: [t.strip() for t in s.split(",")],
        help="""Build super partition and compress with:
            none:no - no compression
            sparse:s - sparse compression
            zstd:zst - zstd compression
            gzip:gz - gzip compression
            brotli:br - brotli compression""",
    )

    # parser.add_argument(
    #     '--debloat',
    #     nargs="?",
    #     const="",
    #     dest='debloat',
    #     metavar="DEBLOAT",
    #     type=lambda s: [t.strip() for t in s.split(',')],
    #     help='''Debloat project with:
    #         path_to_debloating_list - debloat using file_name.txt
    #         project_path - debloat using project_path'''
    # )

    parser.add_argument(
        "--debloat",
        nargs="*",
        dest="debloat",
        metavar=("DEBLOAT_LIST_PATH", "PROJECT_PATH"),
        help="Debloat project using DEBLOAT_LIST_PATH and PROJECT_PATH",
    )

    parser.add_argument(
        "-ui",
        "--update-info",
        dest="update_info",
        action="store_true",
        help="Update project info inside _filesystem_features.txt",
    )
    parser.add_argument("--clean", dest="clean", action="store_true", help="Clean up projects dir")
    parser.add_argument("-v", "--version", action="store_true", help="show version info and exit")

    return parser

# ? Main ------------------------------------------------------------------------------------------>

def update_images_info(specified_proj: Path) -> None:
    """Update the size and inode count of the images in the project.

    Args:
        specified_proj (Path): The path to the project directory.

    Returns:
        None
    """
    if isinstance(specified_proj, str):
        specified_proj = Path(specified_proj)

    # Load configurations
    configs = config

    # Set default values for extra_size and extra_inode
    default_extra_size: int = 104857600
    default_extra_inode: int = 2000

    # Create a dictionary to store the new size and inode count of each image
    image_info: dict = {}

    # Iterate over the output directory to calculate the new size and inode count of each image
    for extracted_image in os.listdir(Path(specified_proj, "Output")):
        new_size = dir_size(Path(specified_proj, "Output", extracted_image))
        new_inode_count = count(Path(specified_proj, "Output", extracted_image))
        image_info[extracted_image] = (new_size, new_inode_count)

    # Iterate over the file features files to update the size and inode count of each image
    for file_features in Path(specified_proj, "Config").rglob("*_filesystem_features.txt"):
        extracted_image = re.search(r"(.+)_filesystem_.*", file_features.name).group(1)
        if extracted_image in image_info:
            new_size, new_inode_count = image_info[extracted_image]
            old_size = find_text_in_file(file_features, "Partition Size:")
            old_inode_count = find_text_in_file(file_features, "Inode count:")

            # Load extra size and inode values for different images from config.ini
            extra_size_config = configs.load_config("EXTRA_SIZE", extracted_image)
            if extra_size_config is not None:
                extra_size = int(extra_size_config)
            else:
                extra_size = default_extra_size

            extra_inode_config = configs.load_config("EXTRA_INODE", extracted_image)
            if extra_inode_config is not None:
                extra_inode = int(extra_inode_config)
            else:
                extra_inode = default_extra_inode

            # Log the update of the size and inode count of the image
            logger.info(
                f"Updating the size of [{extracted_image}] from [{old_size}] to [{new_size + extra_size}]"
            )
            replace_in_file(
                file_features,
                "^Partition Size:.*",
                f'Partition Size:{" " * 12}{new_size + extra_size}',
            )
            logger.info(
                f"Updating the inode count of [{extracted_image}] from "
                f"[{old_inode_count}] to [{new_inode_count + extra_inode}]"
            )
            replace_in_file(
                file_features,
                "^Inode count:.*",
                f'Inode count:{" " * 12}{new_inode_count + extra_inode}',
            )

            # Print a separator line
            print(f"{Colors.green}----------------------------------------------------------------")

def list_projects():
    """List all available projects in the Projects directory."""
    projects_dir: Path = Path(ROOT_DIR).joinpath("Projects")

    return list_folder(projects_dir)

def get_fastboot_path() -> str:
    """Get the path to the fastboot executable depending on the current platform.

    Returns:
        str: The path to the fastboot executable.
    """
    os_type: str = SystemDetector().get_os_type()

    if "WSL" in os_type.upper():
        print("Running on WSL")
        return config.load_config("LINUX", "fastboot_wsl")
    elif "Linux" in os_type.upper():
        print("Running on Linux")
        return config.load_config("LINUX", "fastboot")
    elif "Windows" in os_type.upper():
        print("Not supported on Windows. Install WSL and try to run on WSL")
        sys.exit(1)
    else:
        raise Exception("Unsupported platform")

def flash_device(fastboot: Path, flash: List[str]) -> None:
    """Flash the device with the given images.

    Args:
        fastboot (Path): The path to the fastboot executable.
        flash (List[str]): A list of options for flashing the device.
    """
    if isinstance(fastboot, str):
        fastboot = Path(fastboot)

    # Load configurations
    main_project = config.load_config("MAIN", "main_project")
    source_dir = Path(main_project, "source")
    build_dir = Path(main_project, "Build")
    partitions = config.load_config("MAIN", "partitions").split(" ")
    other_part = "boot dtbo vbmeta vbmeta_system logo".split()

    # Check if fastboot executable exists
    if not fastboot.is_file():
        sys.exit(1)

    working_dir = os.getcwd()

    # Flash partitions
    choose_part = list_folder(build_dir)
    if choose_part is not None:
        print(f"Choose partition to flash: {choose_part}")
        print("Choose a/all to flash all partitions.")
        ans = input("Enter partition: ")
        if ans not in ("a", "all"):
            run_command([fastboot, "flash", str(choose_part[int(ans)]).removesuffix(".img"),
                        os.path.relpath(os.path.join(build_dir, f"{choose_part[int(ans)]}"), working_dir)],
                        verbose=True,
                        stdout=sys.stdout)
        else:
            for part in partitions:
                for path in Path(build_dir).rglob(f"{part}.img"):
                    run_command([fastboot, "flash", part, str(os.path.relpath(path, working_dir))],
                                verbose=True, stdout=sys.stdout)

    # Install boot and dtbo if 'r' is present in flash list
    ans = input("Do you want to install boot and dtbo? [y/n]: ")
    if ans.lower() == "y":
        for part in other_part:
            for path in Path(source_dir).rglob(f"{part}.img"):
                run_command(
                    [fastboot, "flash", part, str(path)],
                    verbose=True,
                    stdout=sys.stdout,
                )

    # Erase metadata and userdata if 'w' is present in flash list
    if "w" in flash:
        print("WARNING: This will delete all your files and photos stored on internal storage.")
        ans = input("Are you sure you want to proceed? [y/n]: ")
        if ans.lower() == "y":
            run_command([fastboot, "erase", "metadata"], verbose=True)
            run_command([fastboot, "erase", "userdata"], verbose=True)

    # Reboot device if 's' is present in flash list
    if "s" in flash:
        ans = input("Do you want to reboot the device? [y/n]: ")
        if ans.lower() == "y":
            run_command([fastboot, "reboot"], verbose=True)

    if "r" in flash:
        ans = input("Do you want to reboot the recovery? [y/n]: ")
        if ans.lower() == "y":
            run_command([fastboot, "reboot", "recovery"], verbose=True)

if __name__ == "__main__":
    # check_terminal_size(80, 25)
    # print banner
    banner = (
        f'{Term.clear}{Banner(BANNER_SRC).draw(Term.height // 2 - 10, center=True)}{Mv.l(46)}{Color("#17e364")}{Fx.b}BY: ABDULRAHMAN'
        f"{Mv.r(5)}{Fx.i}Version: {VERSION}{Fx.ui}{Fx.ub}{Term.bg}{Term.fg}{Mv.d(1)}"
    )
    print(banner)
    time.sleep(0.5)

    os_type = SystemDetector().get_os_type()
    if os_type == "WSL 2":
        print("Recommended to run this script must be run on WSL 1, (WSL 2 make the script slower).")
    elif os_type == "Windows":
        print("Not supported on Windows yet.")

    start_time = time.time()

    # Parse the command line arguments using the create_parser() function
    parser = create_parser()
    args = parser.parse_args()

    # Check if at least one argument is provided
    if len(sys.argv) < 2:
        logger.error("You must enter at least one argument!!!")
        parser.print_usage()
        raise SystemExit(1)

    # Print version information if the --version flag is provided
    if args.version:
        print(f"{PROJECT_NAME} version: {VERSION}")
        raise SystemExit(0)

    # Initialize a new project if the --project-name flag is provided
    if args.name:
        data = {"main_project": Path(ROOT_DIR, "Projects", args.name)}
        config.update_config("MAIN", data)
        init_project(args.name)

    # Update project name in config.ini if the --update-project flag is provided
    if args.project:
        dict_projects = list_projects()

        try:
            logger.info("Choose Project: ")
            ans = int(input("Enter the project number: "))
            logger.info(f"Your choice is [{dict_projects[ans]}].")
            specified_proj = dict_projects[ans]
            data = {"main_project": Path(ROOT_DIR, "Projects", specified_proj)}
            config.update_config("MAIN", data)
            logger.info(f"Updated project to [{config.load_config('MAIN', 'main_project')}]")
        except (KeyboardInterrupt, KeyError, ValueError):
            logger.exception(f"Aborting Your choice [{ans}] not found.")
            sys.exit(1)

    # Write config.ini if the --write-config flag is provided
    if args.write_config:
        logger.info(f"Update {CONFIG_FILE}")
        config.write_default_config()

    # List projects if the --list-projects flag is provided
    if args.lst:
        list_projects()
        sys.exit(0)

    main_project = config.load_config("MAIN", "main_project")

    # Extract firmware if the --input-firmware flag is provided
    if args.input:
        # ExtractFirmware(main_project).ext_fw(args.input, Path(main_project, "Source"))
        # ExtractFirmware(main_project).extract_img()
        ExtractFirmware(
            input_firmware_path=args.input,
            main_project=main_project,
            verbose=True,
        )

    # Extract images if the --extract-project flag is provided
    if args.extract_project:
        try:
            # ExtractFirmware(main_project).extract_img()
            ExtractFirmware(main_project=main_project, verbose=True)
        except FileNotFoundError:
            logger.error("Project folder not exist try update project with [-u]")

    if args.update_info:
        dict_projects = list_projects()

        try:
            logger.info("Choose Project: ")
            ans = int(input("Enter the project number: "))
            logger.info(f"Your choice is [{ans}].")
            specified_proj = Path(ROOT_DIR, "Projects", dict_projects[ans])
        except (KeyboardInterrupt, KeyError, ValueError):
            logger.exception(f"Aborting Your choice [{ans}] not found.")
            sys.exit(1)

        update_images_info(specified_proj)

    # Build super partition and compress it with different methods if the --super-image flag is provided
    # Loop through the values of args.super_image and call CreateSuper for each method
    if args.super_image:
        for method in args.super_image:
            if method not in ["none", "no", "sparse", "s"]:
                CreateSuper(compression_type=method).create_super_img()
            else:
                CreateSuper(sparse_super_partition=True, compression_type=method).create_super_img()

    # Build image with different types if the --build-image flag is provided
    # Loop through the values of args.build_image and set the corresponding flags for CreateExt4
    if args.build_image:
        build_image_set = set(args.build_image)
        raw = "raw" in build_image_set or "r" in build_image_set
        sparse = "sparse" in build_image_set or "s" in build_image_set
        brotli = "brotli" in build_image_set or "b" in build_image_set

        if raw or sparse or brotli:
            CreateExt4().main(raw, sparse, brotli)

    if args.flash:
        # Get path to fastboot executable
        fastboot = get_fastboot_path()

        # Flash device
        flash_device(fastboot, args.flash)

    if args.debloat is not None:
        if len(args.debloat) == 0:
            project = list_projects()
            logger.info("Choose Project: ")
            ans = int(input("Enter the project number: "))
            project_path = Path(ROOT_DIR, "Projects", project[ans])

            debloater_list = list_folder(Path(ROOT_DIR, "debloat_lists"))
            logger.info("Choose debloat file list: ")
            ans = int(input("Enter the file number: "))
            debloat_list_path = Path("debloat_lists", debloater_list[ans])

            if debloat_list_path.exists() and project_path.exists():
                Debloater(
                    debloating_list=debloat_list_path, project_path=project_path
                ).backup_and_debloat()
            else:
                logger.error(
                    f"Debloat list path '{debloat_list_path}' or project path '{project_path}' does not exist"
                )

        elif len(args.debloat) == 1:
            # One argument was passed to the --debloat option
            project = list_projects()
            logger.info("Choose Project: ")
            ans = int(input("Enter the project number: "))
            project_path: Path = Path(ROOT_DIR, "Projects", project[ans])
            debloat_list_path: Path = Path(args.debloat[0])

            if debloat_list_path.exists() and project_path.exists():
                Debloater(
                    debloating_list=debloat_list_path, project_path=project_path
                ).backup_and_debloat()
            else:
                logger.error(
                    f"Debloat list path '{debloat_list_path}' or project path '{project_path}' does not exist"
                )

        elif len(args.debloat) == 2:
            # Two arguments were passed to the --debloat option
            debloat_list_path: Path = Path(args.debloat[0])
            project_path = args.debloat[1]

            if debloat_list_path.exists() and project_path.exists():
                Debloater(
                    debloating_list=debloat_list_path, project_path=project_path
                ).backup_and_debloat()
            else:
                logger.error(
                    f"Debloat list path '{debloat_list_path}' or project path '{project_path}' does not exist"
                )

    # Clean up projects directory if the --clean flag is provided
    if args.clean:
        dict_projects = list_projects()
        logger.info("Choose Project to delete: ")
        ans = input("Enter the project number: [1, 2 ...] or 'c' to cancel: ")

        if ans.lower() in ["c", "cancel"]:
            logger.info("Deletion canceled.")
            sys.exit(0)
        else:
            try:
                ans = {int(x) for x in ans.split(",")}
                for prj_num in ans:
                    specified_proj = dict_projects[prj_num]
                    logger.info(f"Clean Project {Path(ROOT_DIR, 'Projects', dict_projects[prj_num])}")
                    remove(Path(ROOT_DIR, "Projects", dict_projects[prj_num]))
            except (KeyboardInterrupt, KeyError, ValueError):
                logger.exception(f"Aborting Your choice {ans} not found.")
                sys.exit(1)

    runtime = time.time() - start_time

    print(f"{Mv.d(3)}")
    print(
        f"{Fx.italic}{Colors.green}[SUCCESS] {Colors.default}Total Excution time: {runtime} seconds{Fx.ui}{Fx.ub}{Term.bg}{Term.fg}"
    )


from django import template

register = template.Library()

@register.filter
def column_mappings(col_name):
    col_names = {}
    col_names["stable_id"] = "StableID"
    col_names["stable_id_version"] = "Version"
    col_names["loc_start"] = "Start"
    col_names["loc_end"] = "End"
    col_names["loc_strand"] = "Strand"
    col_names["loc_region"] = "Region"
    col_names["genes"] = "Gene"

    if col_name in col_names:
        return col_names[col_name]

    return col_name

@register.filter
def look_up_exon_by_order(exon_sets, exon_order):
    for exon in exon_sets:
        if exon_order == exon['exon_order']:
            return exon


import math
def fact(X):
    count = 0
    while ((X % 2 > 0) == False):
        X >>= 1
        count += 1
    if (count > 0):
        print(2,count)
    for i in range(3, int(math.sqrt(X)) + 1):
        count = 0
        while (X % i == 0):
            count += 1
            X = X//i
        if (count > 0):
            print(i,count)
        i += 2
    if (X > 2):
        print(X,1)

T = int(input())
for _ in range(T):
    X = int(input())
    fact(X)


from django.contrib import admin

from .models import Qualification, Speciality

class QualificationAdmin(admin.ModelAdmin):
    list_display = ('id', 'title')
admin.site.register(Qualification, QualificationAdmin)

class SpecialityAdmin(admin.ModelAdmin):
    list_display = (
        'id',
        'uid',
        'okso',
        'title',
        'ministerialCode',
        'ugnTitle',
        'standard',
    )
    raw_id_fields = ('qualifications',)
admin.site.register(Speciality, SpecialityAdmin)


"""
words longer than 12-chars in turkish dictionary
"""
import re

pattern = "\w{12,}"

with open("dictionary-tur.txt", "rt") as tur_dic:
    for word in tur_dic.readlines():
        if re.fullmatch(pattern, word.strip()):
            print(word.strip())


#Boggle is a popular word game in which players attempt to find words in sequences of adjacent letters on a rectangular board.
#Given a two-dimensional array board that represents the character cells of the Boggle board and an array of unique strings words, find all the possible words from words that can be formed on the board.
#Note that in Boggle when you're finding a word, you can move from a cell to any of its 8 neighbors, but you can't use the same cell twice in one word.
#Example
#For
#board = [['R', 'L', 'D'],
#         ['U', 'O', 'E'],
#         ['C', 'S', 'O']]
#and words = ["CODE", "SOLO", "RULES", "COOL"], the output should be
#wordBoggle(board, words) = ["CODE", "RULES"].
#Input/Output
#[time limit] 4000ms (py)
#[input] array.array.char board
#A 2-dimensional array of uppercase English characters representing a rectangular Boggle board.
#Constraints:
#2 ≤ board.length ≤ 4,
#2 ≤ board[i].length ≤ 4.
#[input] array.string words
#An array of unique English words consisting only of uppercase characters.
#Constraints:
#0 ≤ words.length ≤ 100,
#3 ≤ words[i].length ≤ 16.
#[output] array.string
#An array of all the words from words that can be formed on the Boggle board.
#Code:
def wordBoggle(board, words):
    r = []
    for word in words:
        if canBoggle(board,word):
            r.append(word)
    return sorted(r)

def canBoggle(board, word, used = []):
    if len(word) == 0:
        return True
    for i in range(len(board)):
        for j in range(len(board[0])):
            if (i,j) not in used and board[i][j] == word[0]:
                if len(used)==0 or (abs(used[-1][0] - i)<=1 and abs(used[-1][1] - j)<= 1):
                    if canBoggle(board,word[1:],used + [(i,j)]):
                        return True
    return False

print(wordBoggle([['R', 'L', 'D'], ['U', 'O', 'E'], ['C', 'S', 'O']], ["CODE", "SOLO", "RULES", "COOL"]))


# -*- coding: utf-8 -*-
"""
Created on Wed Jun 26 02:39:05 2019

@author: NRAD
"""

import cv2
import os

from matplotlib import pyplot as plt

path=r'C:\Users\NRAD.NRAD-28\Desktop\BCDR-F03_dataset-20190626T062636Z-001'

files_list=[]

for root, dirs, files in os.walk(path):
    for file in files:
        if file.endswith(".tif"):
             files_list.append(os.path.join(root, file))

             #print(os.path.join(root, file))

for i in range(len(files_list)):

    img=cv2.imread(files_list[i])

    shape=img.shape[0]

    img=img[200:shape,:,:]

    cv2.imwrite(files_list[i],img)

import pandas as pd

df=pd.read_csv(r"C:\Users\NRAD.NRAD-28\Desktop\BCDR-F03_dataset-20190626T062636Z-001\BCDR-F03_dataset\BCDR-F03\bcdr_f03_features.csv")

truth_values=df['classification']

y_truth_list=[]

for i in range(668):

    index1=files_list[i].rindex('_')

    index2=files_list[i].rindex('_',0,index1)

    index3=files_list[i].rindex('_',0,index2)

    view=files_list[i][index1+1:-4]

    study_id=files_list[i][index2+1:index2+5]

    patient_id=files_list[i][index3+1:index3+4]

    #print(view)

    if(df['patient_id'][i] == patient_id and df['study_id'][i]==study_id):

        y_truth_list.append(df['classification'][i])


import logging
import sys

from lmi.scripts.common import configuration
from lmi.scripts.common import errors

TERM_COLOR_NAMES = (
        'black',
        'red',
        'green',
        'yellow',
        'blue',
        'magenta',
        'cyan',
        'white',
        )

# Following are terminal color codes of normal mode.
CN_BLACK   = 0
CN_RED     = 1
CN_GREEN   = 2
CN_YELLOW  = 3
CN_BLUE    = 4
CN_MAGENTA = 5
CN_CYAN    = 6
CN_WHITE   = 7
# Following are terminal color codes of bright mode.
CB_BLACK   = 8
CB_RED     = 9
CB_GREEN   = 10
CB_YELLOW  = 11
CB_BLUE    = 12
CB_MAGENTA = 13
CB_CYAN    = 14
CB_WHITE   = 15

WARNING_COLOR  = CB_YELLOW
ERROR_COLOR    = CB_RED
CRITICAL_COLOR = CB_MAGENTA

#: Dictionary assigning color code to logging level.
LOG_LEVEL_2_COLOR = {
        logging.WARNING : WARNING_COLOR,
        logging.ERROR   : ERROR_COLOR,
        logging.CRITICAL: CRITICAL_COLOR
}

class LogRecord(logging.LogRecord):
    """
    Overrides :py:class:`logging.LogRecord`. It adds new attributes:

        * `levelname_` - Name of level in lowercase.
        * `cseq`       - Escape sequence for terminal used to set color
                         assigned to particular log level.
        * `creset`     - Escape sequence for terminal used to reset foreground
                         color.

    These can be used in format strings initializing logging formatters.

    Accepts the same arguments as base class.
    """

    def __init__(self, name, level, *args, **kwargs):
        use_colors = kwargs.pop('use_colors', True)
        logging.LogRecord.__init__(self, name, level, *args, **kwargs)
        self.levelname_ = self.levelname.lower()
        if use_colors and level >= logging.WARNING:
            if level >= logging.CRITICAL:
                color = LOG_LEVEL_2_COLOR[logging.CRITICAL]
            elif level >= logging.ERROR:
                color = LOG_LEVEL_2_COLOR[logging.ERROR]
            else:
                color = LOG_LEVEL_2_COLOR[logging.WARNING]
            self.cseq = get_color_sequence(color)
            self.creset = "\x1b[39m"
        else:
            self.cseq = self.creset = ''

class ScriptsLogger(logging.getLoggerClass()):

    #: Boolean saying whether the color sequences for log messages shall be
    #: generated.
    USE_COLORS = True

    def makeRecord(self, name, level, fn, lno, msg, args, exc_info, func=None,
            extra=None):
        """
        Overriden method that just changes the *LogRecord* class to our
        predefined and ensures that exception's traceback is printed once at
        most.
        """
        if exc_info:
            err = exc_info[1]
            if getattr(err, '_traceback_logged', False):
                # do not print traceback second time
                exc_info = None
            elif self.isEnabledFor(level):
                try:
                    err._traceback_logged = True
                except AttributeError:
                    pass
        rv = LogRecord(name, level, fn, lno, msg, args, exc_info, func,
                use_colors=self.USE_COLORS)
        if extra is not None:
            for key in extra:
                if (key in ["message", "asctime"]) or (key in rv.__dict__):
                    raise KeyError("Attempt to overwrite %r in LogRecord" % key)
                rv.__dict__[key] = extra[key]
        return rv

    def exception(self, msg, *args, **kwargs):
        lmi_config = configuration.Configuration.get_instance()
        exc_info = sys.exc_info()
        err = exc_info[1]
        if (   lmi_config.trace
           and (  not isinstance(err, errors.LmiError)
               or (   not getattr(err, '_traceback_logged', False)
                  and lmi_config.verbosity >= lmi_config.OUTPUT_DEBUG))):
            kwargs['exc_info'] = exc_info
        else:
            kwargs.pop('exc_info', None)
        self.error(msg, *args, **kwargs)

class LevelDispatchingFormatter(object):
    """
    Formatter class for logging module. It allows to predefine different format
    string used for some level ranges.

    :param dict formatters: Mapping of module names to *format*.
        It is a dictionary of following format: ::

            { max_level1 : format1
            , max_level2 : format2
            , ... }

        *format* in parameters description can be either ``string`` or another
        formatter object.

        For example if you want to have format3 used for *ERROR* and *CRITICAL*
        levels, *format2* for *INFO* and *format1* for anything else, your
        dictionary will look like this: ::

            { logging.INFO - 1 : format1
            , logging.INFO     : format2 }

        And the *default* value should have *format3* assigned.

    :param default: Default *format* to use. This format is used for all levels
        higher than the maximum of *formatters*' keys.
    """
    def __init__(self, formatters, default=configuration.DEFAULT_FORMAT_STRING,
            datefmt=None):
        for k, formatter in formatters.items():
            if isinstance(formatter, basestring):
                formatters[k] = logging.Formatter(formatter, datefmt=datefmt)
        #: This a a tuple of pairs sorted by the first item in descending
        #: order (highest priority ordered first).
        self._formatters = tuple(sorted(formatters.items(),
            key=lambda t: -t[0]))
        if isinstance(default, basestring):
            default = logging.Formatter(default, datefmt=datefmt)
        self._default_formatter = default

    def format(self, record):
        """
        Interface for logging module.
        """
        record.levelname_ = record.levelname.lower()
        formatter = self._default_formatter
        for level, fmt in self._formatters:
            if level < record.levelno:
                break
            formatter = fmt
        try:
            return formatter.format(record)
        except KeyError:
            # in some modules or libraries it may happen that logger is
            # initialized before our logger class is set as default
            record.cseq = record.creset = ''
            return formatter.format(record)

def get_logger(module_name):
    """
    Convenience function for getting callable returning logger for particular
    module name. It's supposed to be used at module's level to assign its
    result to global variable like this: ::

        from lmi.scripts import common

        LOG = common.get_logger(__name__)

    This can be used in module's functions and classes like this: ::

        def module_function(param):
            LOG().debug("This is debug statement logging param: %s", param)

    Thanks to ``LOG`` being a callable, it always returns valid logger object
    with current configuration, which may change overtime.

    :param string module_name: Absolute dotted module path.
    :rtype: :py:class:`logging.Logger`
    """
    def _logger():
        """ Callable used to obtain current logger object. """
        return logging.getLogger(module_name)
    return _logger

def get_color_sequence(color_code):
    """
    Computer color sequence for particular color code.

    :returns: Escape sequence for terminal used to set foreground color.
    :rtype: str
    """
    if color_code <= 7:
        return "\x1b[%dm" % (30 + color_code)
    return "\x1b[38;5;%dm" % color_code

def setup_logger(use_colors=True):
    """
    This needs to be called before any logging takes place.
    """
    ScriptsLogger.USE_COLORS = use_colors
    logging.setLoggerClass(ScriptsLogger)


import cv2
import os
import numpy as np
import json
from cam import camera_streams
cap=camera_streams(mode="Industrial")

def hconcat_resize(img_list,
                   interpolation
                   = cv2.INTER_CUBIC):
      # take minimum hights
    h_min = min(img.shape[0]
                for img in img_list)

    # image resizing
    im_list_resize = [cv2.resize(img,
                       (int(img.shape[1] * h_min / img.shape[0]),
                        h_min), interpolation
                                 = interpolation)
                      for img in img_list]

    # return final image
    return cv2.hconcat(im_list_resize)

#function is for concatinate the image vertically

def vconcat_resize(img_list, interpolation
                   = cv2.INTER_CUBIC):
      # take minimum width
    w_min = min(img.shape[1]
                for img in img_list)

    # resizing images
    im_list_resize = [cv2.resize(img,
                      (w_min, int(img.shape[0] * w_min / img.shape[1])),
                                 interpolation = interpolation)
                      for img in img_list]
    # return final image
    return cv2.vconcat(im_list_resize)

def image_loc(foldername):
    # extractte file
    fileName = []
    for root, dirs, files in os.walk(os.path.abspath(foldername)):
        for namef in files:
            #                 print(os.path.abspath(os.path.join(root, namef)))
            file_name = os.path.abspath(os.path.join(root, namef))
            fileName.append(file_name)
    return fileName

good=image_loc("muvro/sona_data")
# good=image_loc("sona_data/wbolt_wpin")

# pin=[[171, 146, 44, 45], [475, 160, 35, 38], [468, 326, 38, 38], [174, 319, 35, 36]]
pin=[[257, 194, 6, 5], [378, 168, 5, 3], [394, 235, 5, 5], [273, 263, 5, 6]]
bolt=[[328, 109, 26, 27], [359, 115, 25, 31], [386, 129, 25, 34], [408, 155, 22, 29], [419, 182, 23, 30], [423, 215, 21, 26], [412, 244, 23, 29], [393, 270, 28, 26], [367, 292, 33, 23], [341, 299, 30, 29], [309, 300, 29, 30], [280, 292, 30, 31], [255, 279, 27, 29], [237, 257, 24, 30], [226, 229, 27, 31], [225, 196, 22, 34], [232, 168, 26, 29], [247, 144, 21, 23], [267, 121, 31, 26], [293, 113, 36, 22]]

# param={"min1": 153, "max1": 219, "min2": 164, "max2": 223, "min3": 184, "max3": 255, "min4": 183, "max4": 259, "min5": 192, "max5": 252, "min6": 138, "max6": 245, "min7": 126, "max7": 237, "min8": 97, "max8": 230, "min9": 85, "max9": 252, "min10": 75, "max10": 210, "min11": 62, "max11": 187, "min12": 51, "max12": 209, "min13": 102, "max13": 253, "min14": 77, "max14": 245, "min15": 88, "max15": 238, "min16": 164, "max16": 256, "min17": 116, "max17": 240, "min18": 71, "max18": 217, "min19": 94, "max19": 256, "min20": 170, "max20": 286}
# param={'min1': 192, 'max1': 196, 'min2': 193, 'max2': 199, 'min3': 240, 'max3': 248, 'min4': 236, 'max4': 243, 'min5': 238, 'max5': 245, 'min6': 232, 'max6': 242, 'min7': 222, 'max7': 232, 'min8': 222, 'max8': 230, 'min9': 241, 'max9': 248, 'min10': 199, 'max10': 206, 'min11': 177, 'max11': 184, 'min12': 197, 'max12': 205, 'min13': 232, 'max13': 240, 'min14': 221, 'max14': 229, 'min15': 230, 'max15': 236, 'min16': 232, 'max16': 240, 'min17': 231, 'max17': 237, 'min18': 173, 'max18': 177, 'min19': 211, 'max19': 216, 'min20': 231, 'max20': 241}

param={}
pins={}
print(len(bolt))
pause=0
# for i in good:
while(1):

    img1=cap.get_frame((680,500))

    img=cv2.cvtColor(img1,cv2.COLOR_BGR2GRAY)

    img2=img.copy()

    rois=[]

    thresh=cv2.threshold(img2,80,255,cv2.THRESH_BINARY)[1]

    cv2.imshow("thresh",thresh)

    k=cv2.waitKey(1)
    if k==ord('s'):
        pause=1
        print("stop")
    if k==ord('p'):
        pause=0
        print('play')
    if pause==0:
        for r,j in zip(bolt,range(1,21)):

            img1=cv2.rectangle(img1,(r[0],r[1]),(r[0]+r[2],r[1]+r[3]),255,1)
            roi = thresh[int(r[1]):int(r[1]+r[3]),
                        int(r[0]):int(r[0]+r[2])]

            bl=cv2.medianBlur(roi, 9)

            roi=cv2.Canny(bl,100,180)

            rh,rw=roi.shape
            n_black=np.count_nonzero(roi == 255)

            if len(param)<40:
                param[f"min{j}"]=n_black
                param[f"max{j}"]=n_black
            else:
                if param[f"min{j}"]>n_black:param[f"min{j}"]=n_black
                if param[f"max{j}"]<n_black:param[f"max{j}"]=n_black
            # print(n_white,n_black,rh*rw)
            rois.append(roi)

            # cv2.imshow(str(j),roi)
        # print("-----------------------------------------------------------------------------------")
        roiss=vconcat_resize([hconcat_resize(rois[:5]),hconcat_resize(rois[5:10]),hconcat_resize(rois[10:15]),hconcat_resize(rois[15:20])])
        roiss=cv2.resize(roiss,(400,400))
        print(param)
        cv2.imshow("roiss",roiss)

        cv2.imshow("image",img1)
    if k ==ord("q"):break
# while True:
#     frame=cap.get_frame((680,500))
#     cv2.imshow("img",img)
#     if cv2.waitKey(200)==ord('q'):break
cv2.destroyAllWindows()
print(param)

# param=json.dumps(param)
# with open("muvro/boltsparam.json", "w") as dic:
#     dic.write(param)

# bolt=json.dumps(bolt)
# with open("muvro/bolts.json", "w") as dic:
#     dic.write(bolt)

# pin=json.dumps(pin)
# with open("muvro/pin.json", "w") as dic:
#     dic.write(pin)

# pins=json.dumps(pins)
# with open("muvro/pinsparam.json", "w") as dic:
    # dic.write(pins)

# img=cap.get_frame((680,500))

# cv2.circle(img,(333,221),90,(255,255,255),-1)
# a = cv2.selectROIs("select the area", img)

# print(a.tolist())

# for r in a:
#     img=cv2.rectangle(img,(r[0],r[1]),(r[0]+r[2],r[1]+r[3]),255,1)
# cv2.imshow("img",img)
# cv2.waitKey(0)
# cv2.destroyAllWindows()