import sys

## my files
import settings as api_settings
from models import *

## external libs
import json as old_json
import orjson as json

import requests

## py libs
import time
from datetime import datetime, timedelta, time as datetime_time
from os import path
from dateutil.parser import parse
import settings as api_settings

session = requests.Session()

import logging

LOG_SQL = False

## tortoise uses the DEBUG level to log SQL queries.
## we can use this, to see which queries are being made at what time.
## log it a file, and whalla
## also, to see if we can optimize queries etc
if LOG_SQL:
	logging.basicConfig(
		level=logging.DEBUG,
		format='[%(asctime)s](%(levelname)s) %(message)s',
		datefmt='%Y-%m-%d %H:%M:%S',
		handlers=[
			logging.FileHandler(
				filename="../logs/sql.log"
			)
		]
	)
else:
	logging.basicConfig(
		level=logging.INFO,
		format='[%(asctime)s](%(levelname)s) %(message)s',
		datefmt='%Y-%m-%d %H:%M:%S',
		handlers=[
			logging.FileHandler(
				filename="../logs/errors_and_debug.log"
			),
			logging.StreamHandler()
		]
	)

logger = logging.getLogger()

def build_url(platform):
	if 'steam' in platform.strip().lower() or 'pc' in platform.strip().lower():

		if 'pc' in platform.strip().lower():
			PC_SHARD = "shards/{}/".format(platform)
			return "{}{}".format(
				api_settings.BASE_API_URL,
				PC_SHARD
			)
		else:
			return "{}{}".format(
				api_settings.BASE_API_URL,
				api_settings.PC_SHARD
			)

	elif 'xbox' in platform.strip().lower():
		return "{}{}".format(
			api_settings.BASE_API_URL,
			api_settings.XBOX_SHARD
		)
	elif "psn" in platform.strip().lower():
		return "{}{}".format(
			api_settings.BASE_API_URL,
			api_settings.PLAYSTATION_SHARD
		)
	elif "kakao" in platform.strip().lower():
		return "{}{}".format(
			api_settings.BASE_API_URL,
			api_settings.KAKAO_SHARD
		)
	elif "tour" in platform.strip().lower():
		return "{}{}".format(
			api_settings.BASE_API_URL,
			api_settings.TOURNAMENT_SHARD
		)
	elif 'stad' in platform.strip().lower():
		return "{}{}".format(
			api_settings.BASE_API_URL,
			api_settings.STADIA_SHARD
		)

def get_platform(url):

	if 'steam' in url.strip().lower():
		return 'steam'
	elif 'xbox' in url.strip().lower():
		return 'xbox'
	elif "psn" in url.strip().lower():
		return 'psn'
	elif "kakao" in url.strip().lower():
		return 'kakao'
	elif 'stadia' in url.strip().lower():
		return 'stadia'
	elif "tour" in url.strip().lower():
		return 'tour'

def build_player_url(base_url, player_name):
	return "{}{}{}".format(
		base_url,
		api_settings.PLAYER_FILTER,
		player_name
	)

def build_player_account_id_url(base_url, player_id):
	return "{}{}".format(
		base_url,
		api_settings.PLAYER_ACCOUNT_FILTER
	).replace('$accountId', player_id)

def build_season_url(base_url, season_id, player_id, is_ranked):

	if is_ranked:
		return "{}{}{}".format(
			base_url,
			api_settings.SEASON_FILTER,
			'/ranked'
		).replace('$accountId', player_id).replace('$seasonID', season_id)
	else:
		return "{}{}".format(
			base_url,
			api_settings.SEASON_FILTER
		).replace('$accountId', player_id).replace('$seasonID', season_id)

def build_lifetime_url(base_url, player_id):
	return "{}{}".format(
		base_url,
		api_settings.LIFETIME_FILTER
	).replace('$accountId', player_id)

def build_tournament_url(tournament_id):
	return "{}{}".format(
		api_settings.BASE_API_URL,
		api_settings.TOURNAMENTS_FILTER
	).replace('$tourneyID', tournament_id)

def build_match_url(base_url, platform):
	return "{}{}".format(
		base_url,
		api_settings.MATCH_FILTER
	).replace('$matchID', platform)

def build_leaderboard_url(base_url, season_id, game_mode):
	return "{}{}".format(
		base_url,
		api_settings.LEADERBOADS_FILTER
	).replace('$seasonID', season_id).replace('$gameMode', game_mode)

def correct_perspective(perspective):
	return perspective.lower() if perspective and 'all' not in perspective.lower() else None

def correct_mode(mode):
	return mode.lower() if mode and 'all' not in mode.lower() else None

def get_map_name(map_codename):
	return api_settings.MAP_BINDING.get(map_codename)

def make_request(url):

	if 'season' in url or 'leaderboard' in url:
		time.sleep(6)

	try:
		response = json.loads(session.get(url, headers=api_settings.API_HEADER).content)
		return response
	except:
		time.sleep(6)

		if not LOG_SQL:
			logger.info(f'For some reason, requesting {url} failed with the following error: {sys.exc_info()[1]}.')

		make_request(url)

async def parse_player_object(player_id, platform_url, matches):

	player_ids = await Player.all().only('api_id').distinct().values_list('api_id', flat=True)

	if not player_id in player_ids:
		player = Player(
			api_id=player_id,
			platform_url=platform_url,
			api_url=build_player_account_id_url(platform_url, player_id)
		)
		await player.save()

	from concurrent.futures import as_completed
	from requests_futures.sessions import FuturesSession

	return_matches = []

	with FuturesSession() as session:
		session.headers.update(api_settings.API_HEADER)
		futures = [
			session.get(
				build_match_url(platform_url, match['id'])
			) for match in matches
		]
		for future in as_completed(futures):
			resp = future.result()
			return_matches.append(resp.json())

	return return_matches

def get_player_match_id(player_id, match_id):
	return "{}_{}".format(player_id, match_id)

async def get_match_data(player_api_id, player_id):

	## get two weeks ago, to the ealiest datetime on the first day
	two_weeeks_ago = datetime.combine(datetime.now()-timedelta(days=14), datetime_time.min)

	roster_data = Roster.filter(
		participants__player_id=player_id,
		match__api_id__icontains=player_api_id,
		match__created__gte=two_weeeks_ago
	)\
	.prefetch_related(
		'match',
		'match__map',
		'participants',
		'participants__player'
	)

	return roster_data

def player_placement_format(total_teams, placement):

	if total_teams and placement:
		if placement == 1:
			return f'{placement}/{total_teams}'
		else:
			return f'{placement}/{total_teams}'
	elif not total_teams and placement:
		if placement == 1:
			return f'{placement}/100'
		else:
			return f'{placement}/100'

async def parse_player_matches(match_json_list, player_id, platform_url):

	json_length = len(match_json_list)
	message = f'{json_length} matches to parse for {player_id}'

	match_count = 0

	total_time_taken = 0

	save = True

	maps = Map.all()
	players = Player.all()

	for match in match_json_list:

		match_count += 1

		match_id = match['data']['id']

		try:

			start_time = time.time()

			match_date = datetime.strptime(match['data']['attributes']['createdAt'].replace('Z', ''), "%Y-%m-%dT%H:%M:%S")

			match_map =  get_map_name(match['data']['attributes']['mapName'])
			match_map_reference = match['data']['attributes']['mapName']
			match_mode = correct_mode(match['data']['attributes']['gameMode'])
			match_custom = match['data']['attributes']['isCustomMatch']
			match_shard = match['data']['attributes']['shardId']
			match_type = match['data']['attributes']['matchType']

			match_url = match['data']['links']['self']
			match_url = match_url.replace('playbattlegrounds', 'pubg')

			match_participants =  [
				x for x in match['included']
				if x['type'] == 'participant'
			]

			this_map = maps.filter(reference__iexact=match_map_reference)
			map_exists = await this_map.exists()

			if not map_exists:
				match_map = Map(
					name=match_map,
					reference=match_map_reference
				)

				if save:
					await match_map.save()

				map_id = match_map.id
			else:
				this_map = await this_map.first()
				map_id = this_map.id

			this_match = Match(
				api_id=get_player_match_id(player_id, match_id),
				created=match_date,
				map_id=map_id,
				mode=match_mode,
				api_url=match_url,
				is_custom_match=match_custom,
				match_type=match_type,
				total_teams=len(match_participants)
			)

			if save:
				await this_match.save()

			current_player_parsed = [
				x for x in match_participants
				if 'attributes' in x
				and 'stats' in x['attributes']
				and x['attributes']['stats']['playerId'] == player_id
			]
			this_participant_api_id = current_player_parsed[0]['id']

			team_roster = [
				x for x in match['included']
				if x['type'] == 'roster'
				and 'relationships' in x
				and 'participants' in x['relationships']
				and any(
					z['id'] == this_participant_api_id for z in x['relationships']['participants']['data']
				)
			]

			roster_id = team_roster[0]['id']
			roster_placement = team_roster[0]['attributes']['stats']['rank']

			roster = Roster(
				placement=roster_placement,
				match_id=this_match.id,
				api_id=roster_id
			)

			if save:
				await roster.save()

			roster_id = roster.id

			roster_participant_ids = [
				x['id'] for x in team_roster[0]['relationships']['participants']['data']
			]

			roster_participants = [
				x for x in match_participants
				if x['id'] in roster_participant_ids
			]

			for participant in roster_participants:

				participant_api_id = participant['id']
				participant_kills = participant['attributes']['stats'].get('kills', None)
				participant_damage = participant['attributes']['stats'].get('damageDealt', None)
				participant_placement = participant['attributes']['stats'].get('winPlace', None)
				participant_name = participant['attributes']['stats'].get('name', None)
				participant_player_api_id =  participant['attributes']['stats'].get('playerId', None)
				knocks = participant['attributes']['stats'].get('DBNOs', None)
				ride_distance = participant['attributes']['stats'].get('rideDistance', None)
				swim_distance = participant['attributes']['stats'].get('swimDistance', None)
				walk_distance = participant['attributes']['stats'].get('walkDistance', None)

				if 'ai' in participant_player_api_id:
					participant_is_ai = True
				else:
					participant_is_ai = False

				player_queryset = players.filter(api_id=participant_player_api_id)
				player_exists = await player_queryset.exists()

				if not player_exists:
					participant_player_object = Player(
						api_id=participant_player_api_id,
						platform_url=platform_url,
						api_url=build_player_account_id_url(platform_url, player_id)
					)

					if save:
						await participant_player_object.save()
				else:
					participant_player_object = await player_queryset.first()

				participant_player_object_id = participant_player_object.id

				participant_object = Participant(
					api_id=participant_api_id,
					kills=participant_kills,
					player_name=participant_name,
					placement=participant_placement,
					damage=participant_damage,
					player_id=participant_player_object_id,
					is_ai=participant_is_ai,
					knocks=knocks,
					ride_distance=ride_distance,
					swim_distance=swim_distance,
					walk_distance=walk_distance,
				)

				if save:
					await participant_object.save()

				particpant_id = participant_object.id

				roster_participant = RosterParticipant(
					roster_id=roster_id,
					participant_id=particpant_id
				)

				if save:
					await roster_participant.save()

			seconds_taken = "{:0.4f}".format(time.time() - start_time)
			total_time_taken += time.time() - start_time
			message =  f"[{match_count}/{json_length}] ({match_id}) took {seconds_taken}(s)"

			if not LOG_SQL:
				logger.info(message)

		except:
			message = f'Threw the following error when trying to parse a match ({match_id}). {sys.exc_info()[1]}'

			if not LOG_SQL:
				logger.info(message)

	total_time_taken = "{:0.4f}".format(total_time_taken)
	message =  f"Took a total of {total_time_taken}(s)"

	if not LOG_SQL:
		logger.info(message)

async def get_player_matches(player_id, platform_url, matches):
	player_matches = await parse_player_object(player_id, platform_url, matches)
	await parse_player_matches(player_matches, player_id, platform_url)

async def retrieve_player_season_stats(player_id, platform, is_ranked=False):

	platform_url = build_url(platform)

	current_season = await Season.get(is_current=True, platform=platform.lower())
	current_player = await Player.filter(api_id=player_id, platform_url=platform_url).first()

	if current_season:
		season_url = build_season_url(
			base_url=platform_url,
			season_id=current_season.api_id,
			player_id=player_id,
			is_ranked=is_ranked
		)

		season_request = make_request(season_url)

		player_id = current_player.id
		season_id = current_season.id

		if season_request:

			attributes = season_request.get('data').get('attributes')
			game_mode_stats = attributes.get('gameModeStats') or attributes.get('rankedGameModeStats')

			## we should really only save the details that we do not have
			game_modes = [
				game_mode for game_mode in game_mode_stats if not(await PlayerSeasonStats.filter(
					player=current_player,
					season=current_season,
					mode=game_mode,
					is_ranked=is_ranked
				).exists())
			]

			for game_mode in game_modes:
				stats =  game_mode_stats.get(game_mode)
				assists = stats.get('assists', None)
				boosts = stats.get('boosts', None)
				dBNOs = stats.get('dBNOs', None)
				dailyKills = stats.get('dailyKills', None)
				dailyWins = stats.get('dailyWins', None)
				damageDealt = stats.get('damageDealt', None)
				days = stats.get('days', None)
				headshotKills = stats.get('headshotKills', None)
				heals = stats.get('heals', None)
				killPoints = stats.get('killPoints', None)
				kills = stats.get('kills', None)
				longestKill = stats.get('longestKill', None)
				longestTimeSurvived = stats.get('longestTimeSurvived', None)
				losses = stats.get('losses', None)
				maxKillStreaks = stats.get('maxKillStreaks', None)
				mostSurvivalTime = stats.get('mostSurvivalTime', None)
				rankPoints = stats.get('rankPoints', None)
				rankPointsTitle = stats.get('rankPointsTitle', None)
				revives = stats.get('revives', None)
				rideDistance = stats.get('rideDistance', None)
				roadKills = stats.get('roadKills', None)
				roundMostKills = stats.get('roundMostKills', None)
				roundsPlayed = stats.get('roundsPlayed', None)
				suicides = stats.get('suicides', None)
				swimDistance = stats.get('swimDistance', None)
				teamKills = stats.get('teamKills', None)
				timeSurvived = stats.get('timeSurvived', None)
				top10s = stats.get('top10s', None)
				vehicleDestroys = stats.get('vehicleDestroys', None)
				walkDistance = stats.get('walkDistance', None)
				weaponsAcquired = stats.get('weaponsAcquired', None)
				weeklyKills = stats.get('weeklyKills', None)
				weeklyWins = stats.get('weeklyWins', None)
				winPoints = stats.get('winPoints', None)
				wins = stats.get('wins', None)

				player_season_stats = PlayerSeasonStats(
					mode=game_mode,
					assists=assists,
					boosts=boosts,
					knocks=dBNOs,
					daily_kills=dailyKills,
					damage_dealt=damageDealt,
					days=days,
					daily_wins=dailyWins,
					headshot_kills=headshotKills,
					heals=heals,
					kill_points=killPoints,
					kills=kills,
					longest_kill=longestKill,
					longest_time_survived=longestTimeSurvived,
					losses=losses,
					max_kill_streaks=maxKillStreaks,
					most_survival_time=mostSurvivalTime,
					rank_points=rankPoints,
					rank_points_title=rankPointsTitle,
					revives=revives,
					ride_distance=rideDistance,
					road_kills=roadKills,
					round_most_kills=roundMostKills,
					rounds_played=roundsPlayed,
					suicides=suicides,
					swim_distance=swimDistance,
					team_kills=teamKills,
					time_survived=timeSurvived,
					top_10s=top10s,
					vehicle_destroys=vehicleDestroys,
					walk_distance=walkDistance,
					weapons_acquired=weaponsAcquired,
					weekly_kills=weeklyKills,
					weekly_wins=weeklyWins,
					win_points=winPoints,
					wins=wins,
					player_id=player_id,
					season_id=season_id,
					is_ranked=is_ranked
				)
				await player_season_stats.save()

async def get_match_telemetry_from_match(match_json, match, return_early=False):

	assets = [
		x for x in match_json['included']
		if x['type'] == 'asset'
	]

	for asset in assets:
		asset_id = asset.get('id', None)
		asset_attributes = asset.get('attributes', None)
		if asset_attributes:
			url = asset_attributes.get('URL', None)
			date_created = asset_attributes.get('createdAt', None)
			date_created = datetime.strptime(date_created.replace('Z', ''), "%Y-%m-%dT%H:%M:%S")
			if url:
				telemetry_data = make_request(url)

				if return_early:
					return telemetry_data
				else:
					await parse_match_telemetry(
						url=url,
						asset_id=asset_id,
						telemetry_data=telemetry_data,
						match=match,
						date_created=date_created,
						account_id=match.api_id.split('_')[0],
					)

async def create_leaderboard_for_match(match_json, telemetry, save=True):

	game_results_on_finished = [
		x for x in match_json
		if x['_T'] == 'LogMatchEnd'
	]

	telemetry_events = []

	game_results_on_finished_results = [
		x['gameResultOnFinished']['results'] for x in game_results_on_finished
	][0]

	characters = [
		x['characters'] for x in game_results_on_finished
	][0]

	victim_game_results = [
		x['victimGameResult'] for x in match_json
		if x['_T'] == 'LogPlayerKill'
	]

	log_player_take_damage_events = [
		x for x in match_json
		if x['_T'] == 'LogPlayerTakeDamage'
	]

	for x in game_results_on_finished_results:
		victim_game_results.append(x)

	teams = {}
	players = {}

	team_ids = []

	## build a list of rosters and their team members
	ais = 0
	non_ais = 0

	for character_entry in characters:

		character = character_entry.get('character')

		if character:

			team_id = character.get('teamId')
			team_ranking = character.get('ranking')
			player_name = character.get('name')
			player_acount_id = character.get('accountId')

			if 'ai' in player_acount_id:
				ais += 1
				is_ai = True
			else:
				non_ais += 1
				is_ai = False

			if team_id not in teams:

				team_ids.append(team_id)

				teams[team_id] = {
					'team_id': team_id,
					'roster_rank': team_ranking,
					'participant_objects':[
						{
							'player_name': player_name,
							'player_account_id': player_acount_id,
							'player_kills': 0,
							'damage_dealt': 0,
							'is_ai': is_ai
						}
					]
				}

			else:

				participant_details = {
					'player_name': player_name,
					'player_account_id': player_acount_id,
					'player_kills': 0,
					'damage_dealt': 0,
					'is_ai': is_ai
				}

				teams[team_id]['participant_objects'].append(participant_details)

	## this has kills for the team who are first place
	for victim_game_result in victim_game_results:
		team_id = victim_game_result['teamId']
		kills = victim_game_result['stats']['killCount']
		account_id = victim_game_result['accountId']

		for participant in teams[team_id]['participant_objects']:

			if participant['player_account_id'] == account_id:
				if 'ai' in account_id:
					participant['is_ai'] = True
				else:
					participant['is_ai'] = False

				participant['player_kills'] = kills

	for log_player_take_damage_event in log_player_take_damage_events:
		attacker = log_player_take_damage_event['attacker']

		if attacker:
			team_id = attacker['teamId']
			account_id = attacker['accountId']
			damage_dealt = log_player_take_damage_event['damage']

			for participant in teams[team_id]['participant_objects']:
				if participant['player_account_id'] == account_id:
					participant['damage_dealt'] += damage_dealt

	rosters = []

	for team_id in team_ids:
		team = teams[team_id]
		participant_objects = ''

		for x in team['participant_objects']:
			if not x['is_ai']:
				participant_objects += f" {x['player_name']}: {x['player_kills']} kill(s) | {round(x['damage_dealt'], 2)} damage
"
			else:
				participant_objects += f" {x['player_name']}: {x['player_kills']} kill(s) | {round(x['damage_dealt'], 2)} damage
"

		team['participant_objects'] = participant_objects
		rosters.append(team)

	if save:

		telemet_roster = TelemetryRoster(
			json=rosters,
			telemetry=telemetry
		)

		await telemet_roster.save()

		telemetry_events.append(
			TelemetryEvent(
				event_type='AICount',
				telemetry=telemetry,
				description=ais
			)
		)
		telemetry_events.append(
			TelemetryEvent(
				event_type='PlayerCount',
				telemetry=telemetry,
				description=non_ais
			)
		)
		await TelemetryEvent.bulk_create(telemetry_events)

	else:
		return rosters

async def parse_match_telemetry(url, asset_id, telemetry_data, date_created, match, account_id):

	match_id = match.id

	telemetry_check = Telemetry.filter(match_id=match_id, api_id=asset_id)
	this_player = await Player.filter(api_id=account_id).first()
	current_participant = await Participant.filter(player=this_player).order_by('id').first()
	player_name = current_participant.player_name
	kill_causes = ItemTypeLookup.all()

	telemetry_events = []

	save = True

	player_id = this_player.id

	append = telemetry_events.append

	if not await telemetry_check.exists():

		match_kills = 0
		dead = False
		won_match = False

		match_telemet = Telemetry(
			api_id=asset_id,
			api_url=url,
			created_at=date_created,
			match_id=match_id
		)

		if save:
	 		await match_telemet.save()

		await create_leaderboard_for_match(
			match_json=telemetry_data,
			telemetry=match_telemet
		)

		telem_id = match_telemet.id

		heal_item_ids = [
			'Item_Boost_PainKiller_C',
			'Item_Heal_FirstAid_C',
			'Item_Boost_EnergyDrink_C',
			'Item_Heal_Bandage_C',
			'Item_Heal_MedKit_C'
		]

		telem_to_capture = [
			'LogItemUse',
			'LogPlayerKill',
			'LogMatchEnd',
			'LogMatchStart',
		]

		log_player_events = [
			x for x in telemetry_data
			if x['_T'] in telem_to_capture
			and (
					(
						'killer' in x
						and x['killer']
						and 'accountId' in x['killer']
						and x['killer']['accountId'] == account_id
					)
				or
					(
						'victim' in x
						and x['victim']
						and 'accountId' in x['victim']
						and x['victim']['accountId'] == account_id
					)
				or
					(
						'item' in x
						and x['item']['itemId'] in heal_item_ids
						and 'character' in x
						and x['character']['accountId'] == account_id
					)
				or not (
						(
							'killer' in x
						)
					or
						(
							'victim' in x
						)
					or
						(
							'item' in x
						)
				)
			)
		]

		del telemetry_data

		if 'ai' in account_id:
			is_player_ai = True
		else:
			is_player_ai = False

		ais = 0

		for log_event in log_player_events:

			event_type = log_event['_T']
			event_timestamp = log_event['_D']

			if event_timestamp:
				event_timestamp = parse(event_timestamp)

			if event_type == 'LogPlayerKill':

				victim_name = log_event['victim']['name']
				victim_id = log_event['victim']['accountId']

				if victim_id == account_id:
					dead = True

				if 'ai' in victim_id:
					is_victim_ai = True
				else:
					is_victim_ai = False

				killer = log_event.get('killer')

				if killer:

					killer_name = log_event['killer']['name']
					killer_id = log_event['killer']['accountId']
					killer_x = log_event['killer']['location']['x']
					killer_y = log_event['killer']['location']['y']

					if 'victim' in log_event:
						victim_x = log_event['victim']['location']['x']
						victim_y = log_event['victim']['location']['y']
					else:
						victim_x = killer_x
						victim_y = killer_y

					if killer_id == account_id:
						match_kills += 1
						dead = False

					if 'ai' in killer_id:
						is_killer_ai = True
					else:
						is_killer_ai = False

				else:

					victim_x = log_event['victim']['location']['x']
					victim_y = log_event['victim']['location']['y']

					killer = log_event.get('killer')

					if killer:
						killer_x = log_event['killer']['location']['x']
						killer_y = log_event['killer']['location']['y']
					else:
						killer_x = victim_x
						killer_y = victim_y

					damage_type = log_event.get('damageTypeCategory')

					if damage_type:
						killer_name = log_event['damageTypeCategory']

				kill_location = log_event['damageReason']

				if kill_location not in ['None', 'NonSpecific']:
					kill_location = kill_location.title()
				else:
					kill_location = None

				kill_cause = log_event['damageCauserName']
				kill_cause = await kill_causes.filter(reference=kill_cause).first()
				kill_cause = kill_cause.name

				if kill_cause in ['Redzone', 'Bluezone']:
					if is_victim_ai:
						event_description = f' {victim_name} died inside the {kill_cause}'
					else:
						event_description = f' {victim_name} died inside the {kill_cause}'
				else:
					if is_killer_ai:
						if is_victim_ai:
							if killer_name == 'Damage_Drown':
								event_description = f' {victim_name} died by drowning'
							else:
								event_description = f' {killer_name} killed {victim_name} with a {kill_cause}'
						else:
							if killer_name == 'Damage_Drown':
								event_description = f' {victim_name} died by drowning'
							else:
								event_description = f' {killer_name} killed  {victim_name} with a {kill_cause}'
					else:
						if is_victim_ai:
							if killer_name == 'Damage_Drown':
								event_description = f' {victim_name} died by drowning'
							else:
								event_description = f' {killer_name} killed {victim_name} with a {kill_cause}'
						else:
							if killer_name == 'Damage_Drown':
								event_description = f' {victim_name} died by drowning'
							else:
								event_description = f' {killer_name} killed  {victim_name} with a {kill_cause}'

				append(TelemetryEvent(
					event_type=event_type,
					timestamp=event_timestamp,
					description=event_description,
					telemetry_id=telem_id,
					player_id=player_id,
					killer_x_cord=killer_x,
					killer_y_cord=killer_y,
					victim_x_cord=victim_x,
					victim_y_cord=victim_y
				))

				if match_kills:

					if dead:
						if is_player_ai:
							event_description = f' {player_name} died with {match_kills} kill(s)'
						else:
							event_description = f' {player_name} died with {match_kills} kill(s)'
					else:
						if is_player_ai:
							event_description = f' {player_name} now has {match_kills} kill(s)'
						else:
							event_description = f' {player_name} now has {match_kills} kill(s)'

					append(TelemetryEvent(
						event_type=event_type,
						timestamp=event_timestamp,
						description=event_description,
						telemetry_id=telem_id,
						player_id=player_id
					))

			if event_type == 'LogItemUse':

				item_id = log_event['item']['itemId']
				item_used = await kill_causes.filter(reference=item_id).first()
				item_used = item_used.name

				if is_player_ai:
					event_description = f' {player_name} used a {item_used}'
				else:
					event_description = f' {player_name} used a {item_used}'

				if item_id in ['Item_Boost_PainKiller_C', 'Item_Boost_EnergyDrink_C']:
					event_type = 'LogItemUseBoost'
				else:
					event_type = 'LogItemUseMed'

				append(TelemetryEvent(
					event_type=event_type,
					timestamp=event_timestamp,
					description=event_description,
					telemetry_id=telem_id,
					player_id=player_id
				))

			if event_type == 'LogMatchEnd':

				game_results = log_event['gameResultOnFinished']

				if game_results:

					won_match = any(
						x['accountId'] == account_id
						for x in game_results['results']
					)

				if won_match:
					event_description = f'Winner Winner Chicken Dinner!'
				else:
					if is_player_ai:
						event_description = f' {player_name} did not win this match. Better luck next time!'
					else:
						event_description = f' {player_name} did not win this match. Better luck next time!'

				append(TelemetryEvent(
					event_type=event_type,
					timestamp=event_timestamp,
					description=event_description,
					telemetry_id=telem_id,
					player_id=player_id
				))

			if event_type == 'LogMatchStart':

				event_description = 'Match started'

				append(TelemetryEvent(
					event_type=event_type,
					timestamp=event_timestamp,
					description=event_description,
					telemetry_id=telem_id,
					player_id=player_id
				))

		event_description = match_kills
		event_type = 'LogTotalMatchKills'

		append(TelemetryEvent(
			event_type=event_type,
			timestamp=event_timestamp,
			description=event_description,
			telemetry_id=telem_id,
			player_id=player_id
		))

		await TelemetryEvent.bulk_create(telemetry_events)


import pandas as pd
from statsmodels.tsa.stattools import adfuller
from statsmodels.tsa.seasonal import seasonal_decompose
import matplotlib.pyplot as plt

def calculate_return(adj_close_now, adj_close_before):
    return (adj_close_now - adj_close_before) / adj_close_before;

def intersection(a, b):
    return list(set(a) & set(b))

def get_bollinger_bands(series, window=20):
    deviation2 = 2 * pd.rolling_std(series, window, min_periods=window)
    mean = pd.rolling_mean(series, window=window)
    upper = mean + deviation2
    lower = mean - deviation2
    return pd.DataFrame({'UPPER': upper, 'LOWER': lower}, index=series.index)

def get_daily_returns(df):
    daily_returns = df.copy()
    daily_returns[1:] = (df[1:] / df[:-1].values) - 1
    daily_returns.ix[0, :] = 0
    return daily_returns

def test_stationarity(timeseries, window=252):

    #Determing rolling statistics
    rolmean = pd.rolling_mean(timeseries, window)
    rolstd = pd.rolling_std(timeseries, window)

    #Plot rolling statistics:
    orig = plt.plot(timeseries, color='blue',label='Original')
    mean = plt.plot(rolmean, color='red', label='Rolling Mean')
    std = plt.plot(rolstd, color='black', label = 'Rolling Std')

    idx = timeseries.reset_index()
    for i in range(idx.shape[0]/window):
        plt.axvline(idx.ix[i*window]['Date'], color='g', linestyle='--', linewidth=0.5)

    plt.legend(loc='best')
    plt.title('Rolling Mean & Standard Deviation')
    plt.show(block=False)

    #Perform Dickey-Fuller test:
    print('Results of Dickey-Fuller Test:')
    dftest = adfuller(timeseries, autolag='AIC')
    dfoutput = pd.Series(dftest[0:4], index=['Test Statistic','p-value','#Lags Used','Number of Observations Used'])
    for key,value in dftest[4].items():
        dfoutput['Critical Value (%s)'%key] = value
    print(dfoutput)

def decompose(series, freq):
    decomposition = seasonal_decompose(series, freq=freq)

    trend = decomposition.trend
    seasonal = decomposition.seasonal
    residual = decomposition.resid

    return (trend, seasonal, residual)

def plot_decomposition(series, trend, sesonal, residual):
    plt.subplot(411)
    plt.plot(series, label='Original')
    plt.legend(loc='best')
    plt.subplot(412)
    plt.plot(trend, label='Trend')
    plt.legend(loc='best')
    plt.subplot(413)
    plt.plot(sesonal,label='Seasonality')
    plt.legend(loc='best')
    plt.subplot(414)
    plt.plot(residual, label='Residuals')
    plt.legend(loc='best')
    plt.tight_layout()
    plt.show()


import numpy as np
import pandas as pd
import pytest

from giskard import slicing_function
from giskard.models._precooked import PrecookedModel

@slicing_function(name="random", row_level=False)
def random_slice(df: pd.DataFrame) -> pd.DataFrame:
    return df.sample(100)

@pytest.mark.parametrize(
    "model,dataset",
    [("german_credit_model", "german_credit_data")],
)
def test_precooked_model(model, dataset, request):
    model = request.getfixturevalue(model)
    dataset = request.getfixturevalue(dataset)
    precooked_model = PrecookedModel.from_model(model, dataset)
    data_slice = dataset.slice(random_slice())
    # assert np.all(data_slice.df.columns == dataset.df.columns)
    # assert np.all(precooked_model.predict(dataset).raw == model.predict(dataset).raw)

    assert np.all(precooked_model.predict(data_slice).raw == model.predict(data_slice).raw)


#sol using heapq runtime-O(nlogk) k=num of lists, space = O(n) creating new linkedlist
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
import heapq
class Solution:
    def mergeKLists(self, lists: List[ListNode]) -> ListNode:
        q=[]
        n = len(lists)
        for i,l in enumerate(lists):
            if l:
                heapq.heappush(q,(l.val,i, l))
        node =head = ListNode(None)
        while q:
            _,i,node.next = heapq.heappop(q)
            node = node.next
            if node.next:
                heapq.heappush(q, (node.next.val,i,  node.next)) #i added in case of ties. it val equals, it will cause error by comparing nodes
        return head.next


from visit_manager import NumberOfVisits
import json
import arrangement
import art
import realism

class SceneController(NumberOfVisits):

    def __init__(self, filename):
        super().__init__()
        self.name = filename
        self.go_again = True
        self.scene = self.get_scene()

    def __str__(self):
        """Function for polymorphism"""

        return self.scene

    def get_scene(self):
        """Return the scene to be shown"""

        with open(file=self.name, mode="r") as json_file:
            self.scene = json.load(json_file)["scene"]

        self.go_again = False

        return self.scene

    def show_scene(self):
        if self.scene == "Lsystem":
            arrangement.main()

        elif self.scene == "Environmental Art Graph":
            art.art()

        elif self.scene == "Outdoor Scene":
            realism.main()

if __name__ == "__main__":
    scene_controller = SceneController("scene.json")
    scene_controller.show_scene()


from PyQt6 import uic, QtTest
from PyQt6.QtWidgets import *
from PyQt6.QtCore import *
from PyQt6.QtGui import *
from game.speaker import Speaker
import game.aboutWindow as aboutWindow
import keyboard
import random

ICO_ICON = "images/main/miz.ico"
TITLE_ICON = "images/main/title.png"

S = [['.....','..00.','.00..','.....','.....'],['..0..','..00.','...0.','.....','.....']]
Z = [['.....','.00..','..00.','.....','.....'],['..0..','.00..','.0...','.....','.....']]
I = [['..0..','..0..','..0..','..0..','.....'],['.....','.....','0000.','.....','.....']]
O = [['.....','.00..','.00..','.....','.....']]
J = [['.....','.0...','.000.','.....','.....'],['.....','..00.','..0..','..0..','.....'],
     ['.....','.....','.000.','...0.','.....'],['.....','..0..','..0..','.00..','.....']]
L = [['.....','...0.','.000.','.....','.....'],['.....','..0..','..0..','..00.','.....'],
     ['.....','.....','.000.','.0...','.....'],['.....','.00..','..0..','..0..','.....']]
T = [['.....','..0..','.000.','.....','.....'],['.....','..0..','..00.','..0..','.....'],
     ['.....','.....','.000.','..0..','.....'],['.....','..0..','.00..','..0..','.....']]

shapes = [S, Z, I, O, J, L, T]
shape_colors = [1,2,3,4,5,6,7]
color_palettes = [
    [QColor(0,88,248),  QColor(60,188,252), QColor(0,88,248),   QColor(203,232,247),QColor(0,165,247),  QColor(73,91,205),  QColor(0,247,221)   ],#0
    [QColor(0,168,0),   QColor(110,173,110),QColor(184,248,24), QColor(200,239,200),QColor(0,236,0),    QColor(104,151,104),QColor(163,255,179) ],#1
    [QColor(216,0,204), QColor(248,120,248),QColor(204,0,183),  QColor(206,192,204),QColor(176,0,218),  QColor(214,104,227),QColor(218,0,169)   ],#2
    [QColor(0,88,248),  QColor(123,168,249),QColor(88,216,84),  QColor(9,216,3),    QColor(190,216,190),QColor(197,214,246),QColor(3,216,175)   ],#3
    [QColor(228,0,88),  QColor(88,248,152), QColor(228,63,127), QColor(3,242,99),   QColor(228,141,175),QColor(211,246,225),QColor(242,94,94)   ],#4
    [QColor(88,248,152),QColor(104,136,252),QColor(3,249,101),  QColor(12,63,250),  QColor(139,249,183),QColor(218,226,254),QColor(58,246,97)   ],#5
    [QColor(248,56,0),  QColor(124,124,124),QColor(246,99,56),  QColor(183,183,183),QColor(249,144,113),QColor(203,178,162),QColor(247,202,189) ],#6
    [QColor(104,68,252),QColor(168,0,32),   QColor(161,139,250),QColor(167,42,66),  QColor(199,186,250),QColor(166,79,96),  QColor(64,20,247)   ],#7
    [QColor(0,88,248),  QColor(248,56,0),   QColor(67,132,250), QColor(246,100,58), QColor(130,173,251),QColor(246,166,142),QColor(188,210,250) ],#8
    [QColor(248,56,0),  QColor(252,160,68), QColor(246,95,51),  QColor(250,185,121),QColor(246,140,109),QColor(246,217,188),QColor(250,191,174) ],#9
     ]

#=====TETRIS FUNCTIONS======================================================================

class Figure(object):
    def __init__(self, x, y, shape):
        self.x = x
        self.y = y
        self.shape = shape
        self.color = shape_colors[shapes.index(shape)]
        self.rotation = 0

def createField(locked_pos={}):
    board = [[0 for _ in range(15)] for _ in range(22)]

    for i in range(len(board)):
        for j in range(len(board[i])):
            if (j, i) in locked_pos:
                c = locked_pos[(j, i)]
                board[i][j] = c
    return board

def getShape():
    return Figure(7, 1, random.choice(shapes))

def convertShapeFormat(shape):
    positions = []
    format = shape.shape[shape.rotation % len(shape.shape)]
    for i, line in enumerate(format):
        row = list(line)
        for j, column in enumerate(row):
            if column == '0':
                positions.append((shape.x + j , shape.y + i))

    for i, pos in enumerate(positions):
        positions[i] = (pos[0]-2, pos[1]-1)
    return positions

def validSpace(shape, board):
    accepted_pos = [[(j, i) for j in range(15) if board[i][j] == 0] for i in range(22)]
    accepted_pos = [j for sub in accepted_pos for j in sub]

    formatted = convertShapeFormat(shape)

    for pos in formatted:
        if pos not in accepted_pos:
            if pos[1] > -1:
                return False
    return True

def checkLost(positions):
    for pos in positions:
        x, y = pos
        if y < 1:
            return True
    return False

def tryRotate(info):
    info.current_piece.rotation += 1
    if not (validSpace(info.current_piece, info.board)):
        info.current_piece.rotation -= 1
    else:
        Speaker.playsound(Speaker.obj(Speaker.generic_scroll_01), 0.3)

def tryMoveLeft(info):
    info.current_piece.x -= 1
    if not (validSpace(info.current_piece, info.board)):
        info.current_piece.x += 1
    else:
        Speaker.playsound(Speaker.obj(Speaker.generic_scroll_01), 0.3)

def tryMoveRight(info):
    info.current_piece.x += 1
    if not (validSpace(info.current_piece, info.board)):
        info.current_piece.x -= 1
    else:
        Speaker.playsound(Speaker.obj(Speaker.generic_scroll_01), 0.3)

def tryMoveDown(info):
    info.current_piece.y += 1
    if not (validSpace(info.current_piece, info.board)):
        info.current_piece.y -= 1
    else:
        Speaker.playsound(Speaker.obj(Speaker.generic_scroll_01), 0.3)

def clearRows(info, self):
    inc = 0
    for i in range(len(info.board)-1, -1, -1):
        row = info.board[i]
        if 0 not in row:
            inc += 1
            ind = i
            for j in range(len(row)):
                try:
                    del info.locked_positions[(j,i)]
                except:
                    continue
    if inc > 0:
        for key in sorted(list(info.locked_positions), key = lambda x: x[1])[::-1]:
            x, y = key
            if y < ind:
                newKey = (x, y + inc)
                info.locked_positions[newKey] = info.locked_positions.pop(key)
    score_map = {0:0, 1:60, 2:120, 3:360, 4:1800}
    if inc != 0:
        Speaker.playsound(Speaker.obj(Speaker.row_deleted), 0.2)
    prevLevel = checkLevel(info, True)
    info.lines += inc
    info.score += score_map[inc]*(info.level+1)
    checkLevel(info, False, prevLevel, self)
    self.levelText.setText("Level : " + str(info.level))
    self.scoreText.setText("Score : " + str(info.score))

def checkLevel(info, shouldReturn = False, prevLevel = 123, self = None):
    if shouldReturn:
        return info.level
    info.level = info.lines//10
    if prevLevel != 123:
        if prevLevel < info.level:
            playLevelSound(info.level, self)
    checkSpeed(info)

def checkSpeed(info):
    info.speed = info.level//2
    if info.level > 20:
        info.speed = 10

def playLevelSound(cur_lev, self):
    playTextAnimation(self)
    level = cur_lev%5
    if level == 0:
        Speaker.playsound(Speaker.obj(Speaker.lvl1), 0.4)
    if level == 1:
        Speaker.playsound(Speaker.obj(Speaker.lvl2), 0.4)
    if level == 2:
        Speaker.playsound(Speaker.obj(Speaker.lvl3), 0.4)
    if level == 3:
        Speaker.playsound(Speaker.obj(Speaker.lvl4), 0.4)
    if level == 4:
        Speaker.playsound(Speaker.obj(Speaker.lvl5), 0.4)

def playTextAnimation(self):
    self.animation = QPropertyAnimation(self.levelText, b'margin')
    self.animation.setStartValue(70)
    self.animation.setEndValue(0)
    self.animation.setDuration(1000)
    self.animation.setEasingCurve(QEasingCurve.Type.OutElastic)
    self.animation.start()

#=====QT CLASSES n WINDOWS=====================================================================================

class PlayWindow(QDialog):
    closed = pyqtSignal()
    def __init__(self, info):
        self.needsReset = 0
        super(PlayWindow, self).__init__()
        uic.loadUi("game/UI/PlayWindow.ui", self)
        self.setWindowTitle("PyQt6 Tetris (Game window)")
        self.setWindowIcon(QIcon(f"{ICO_ICON}"))
        Speaker.play_background_music()
        self.pause = 1
        #-----------------
        self.buttonExit = self.findChild(QPushButton, "ExitButton")
        self.tableWidget = self.findChild(QTableWidget, "PlayTable")
        self.figureWidget = self.findChild(QTableWidget, "FigureWindow")
        self.buttonPause = self.findChild(QPushButton, "PauseButton")
        self.timeText = self.findChild(QLabel, "TimeText")
        self.levelText = self.findChild(QLabel, "LevelText")
        self.scoreText = self.findChild(QLabel, "ScoreText")
        self.linesText = self.findChild(QLabel, "LinesText")
        #-----------------
        self.buttonPause.clicked.connect(self.PausePress)
        self.buttonPause.enterEvent = lambda e: Speaker.playsound(Speaker.obj(Speaker.menu_focus))
        self.buttonExit.clicked.connect(self.ExitB)
        self.buttonExit.enterEvent = lambda e: Speaker.playsound(Speaker.obj(Speaker.menu_focus))
        #-----------------
        self.tableWidget.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Stretch)
        self.tableWidget.verticalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Stretch)
        self.figureWidget.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Stretch)
        self.figureWidget.verticalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Stretch)
        self.gameTimer = QTimer()
        self.gameTimer.setInterval(25)
        self.gameTimer.timeout.connect(lambda: self.GameStateUpdate(info))
        self.time = 0
        self.buttonPause.setFocusPolicy(Qt.FocusPolicy.NoFocus)
        self.buttonExit.setFocusPolicy(Qt.FocusPolicy.NoFocus)
        self.wasd_space = [0,0,0,0,0]

    def keyPressEvent(self, event):
        if event.key() == Qt.Key.Key_W or event.key() == Qt.Key.Key_Up:
            self.wasd_space[0] = 1
        if event.key() == Qt.Key.Key_A or event.key() == Qt.Key.Key_Left:
            self.wasd_space[1] = 1
        if event.key() == Qt.Key.Key_S or event.key() == Qt.Key.Key_Down:
            self.wasd_space[2] = 1
        if event.key() == Qt.Key.Key_D or event.key() == Qt.Key.Key_Right:
            self.wasd_space[3] = 1
        if event.key() == Qt.Key.Key_Space:
            self.wasd_space[4] = 1

    def GameStateUpdate(self, info):
        if(self.needsReset == 1):
            self.time = 0
            self.pause = 0
            self.PauseGame()
            self.gameTimer.stop()
            info.reset()
            for y in range(20):
                for x in range(15):
                    self.tableWidget.setItem(y, x, QTableWidgetItem(None))
            for y in range(5):
                for x in range(5):
                    self.figureWidget.setItem(y, x, QTableWidgetItem(None))
            self.buttonPause.setEnabled(True)
            self.buttonPause.setStyleSheet("""
                QPushButton {
                background-color: rgba(255, 255, 255, 0);
                border: 0;
                color: rgb(150, 150, 150);
                }
                QPushButton:hover{
                background-color: rgba(255, 255, 255, 45);
                color: rgb(255,255,255);
                }
            """)
            self.buttonPause.setText("Start")
            self.scoreText.setText("Score : 0")
            self.levelText.setText("Level : 0")
            self.linesText.setText("Lines : 0")
            self.timeText.setText("Time passed : 0")
            self.needsReset = 0

        if(self.pause != 1):

            self.time += 1

            info.board = createField(info.locked_positions)

            if (self.time % (11-info.speed) == 0):
                info.current_piece.y += 1
                Speaker.playsound(Speaker.obj(Speaker.generic_scroll_01), 0.15)

            if not(validSpace(info.current_piece, info.board)) and info.current_piece.y > 0:
                info.current_piece.y -= 1
                info.change_piece = True

            #==checking=for=input========================================================================

            if self.wasd_space[0] == 1:
                tryRotate(info)
                self.wasd_space [0] = 0

            if self.wasd_space[1] == 1:
                tryMoveLeft(info)
                self.wasd_space[1] = 0

            if (keyboard.is_pressed('s') or keyboard.is_pressed('down') or self.wasd_space[2] == 1):
                if (self.time % 2 == 0) :
                    tryMoveDown(info)
                    self.wasd_space[2] = 0

            if self.wasd_space[3] == 1:
                tryMoveRight(info)
                self.wasd_space[3] = 0

            if self.wasd_space[4] == 1:
                for i in range(22):
                    info.current_piece.y += 1
                    if not(validSpace(info.current_piece, info.board)) and info.current_piece.y > 0:
                        info.current_piece.y -= 1
                        info.change_piece = True
                        break
                self.wasd_space[4] = 0

            if keyboard.is_pressed('p'):
                info.lines += 2
                info.score += 40
                self.time += 85

            #============================================================================================

            current_piece_blocks = convertShapeFormat(info.current_piece)

            for block in current_piece_blocks:
                x, y = block
                info.board[y][x] = info.current_piece.color

            if info.change_piece:
                Speaker.playsound(Speaker.obj(Speaker.block_fallen), 0.5)
                for block in current_piece_blocks:
                    info.locked_positions[block] = info.current_piece.color
                info.current_piece = info.next_piece
                info.next_piece = getShape()
                info.change_piece = False
                clearRows(info, self)

            if checkLost(info.locked_positions):
                self.timeText.setText("Game Over!")
                self.PauseGame()
                self.buttonPause.setEnabled(False)
                self.buttonPause.setStyleSheet("""
                    QPushButton {
                    background-color: rgba(255, 150, 150, 45);
                    border: 0;
                    color: rgb(20, 20, 20);
                    }
                    QPushButton:hover{
                    background-color: rgba(255, 150, 150, 65);
                    color: rgb(20,20,20);
                    }
                """)
                self.leadb = Leaderboard(info, self.time)
                self.leadb.closed.connect(self.leadbClosed)
                self.leadb.show()

            self.UpdateCell(info)
            self.UpdatePreview(info)
            self.timeText.setText("Time passed : " + str(self.time//20) + "s")
            self.LinesText.setText("Lines : " + str(info.lines))

    def UpdateCell(self, info):
        for y in range(20):
            for x in range(15):
                item = info.board[y+2][x]
                tableWidgetItem = QTableWidgetItem(str(item))
                self.tableWidget.setItem(y, x, tableWidgetItem)
                if(self.tableWidget.item(y,x).text() == '0'):
                    blockColor = QColor(0,0,0)
                else:
                    blockColor = color_palettes[info.level%10][(int(self.tableWidget.item(y,x).text()) - 1)]
                self.tableWidget.item(y, x).setBackground(blockColor)

    def UpdatePreview(self, info):
        for y in range(5):
            for x in range(5):
                self.figureWidget.setItem(y, x, QTableWidgetItem(str('.')))
        blockColor = color_palettes[info.level%10][(info.next_piece.color -1)]
        format = info.next_piece.shape[info.next_piece.rotation % len(info.next_piece.shape)]
        for i, line in enumerate(format):
            row = list(line)
            for j, column in enumerate(row):
                if column == '.':
                    self.figureWidget.item(i, j).setBackground(QColor(0,0,0))
                if column == '0':
                    self.figureWidget.item(i, j).setBackground(blockColor)

    def leadbClosed(self):
        self.leadb.hide()
        for y in range(20):
            for x in range(15):
                self.tableWidget.setItem(y, x, QTableWidgetItem(str('.')))
                self.tableWidget.item(y, x).setBackground(QColor(255,255,255))
                QtTest.QTest.qWait(1)
        self.close()

    def PauseGame(self):
        if(self.pause):
            self.buttonPause.setStyleSheet("""
                QPushButton {
                background-color: rgba(255, 255, 255, 45);
                color: rgb(255,255,255);
                border: 0;
                }
                QPushButton:hover{
                background-color: rgba(255, 255, 255, 65);
                color: rgb(255,255,255);
                }
            """)
            self.buttonPause.setText("Pause")
            self.pause = 0
            self.gameTimer.start()
        else:
            self.buttonPause.setStyleSheet("""
                QPushButton {
                background-color: rgba(255, 255, 255, 0);
                border: 0;
                color: rgb(150, 150, 150);
                }
                QPushButton:hover{
                background-color: rgba(255, 255, 255, 45);
                color: rgb(255,255,255);
                }
            """)
            self.buttonPause.setText("Start")
            self.pause = 1

    def PausePress(self):
        if self.pause:
            Speaker.playsound(Speaker.obj(Speaker.menu_accept))
        self.PauseGame()

    def ExitB(self):
        Speaker.playsound(Speaker.obj(Speaker.menu_back))
        self.close()

    def closeEvent(self, event):
        self.needsReset = 1
        self.closed.emit()
        QDialog.closeEvent(self, event)

class Leaderboard(QDialog):
    closed = pyqtSignal()
    def __init__(self, info, time):
        super(Leaderboard, self).__init__()
        uic.loadUi("game/UI/Leaderboard.ui", self)
        self.setWindowTitle("PyQt6 Tetris (Leaderboard)")
        self.setWindowIcon(QIcon(f"{ICO_ICON}"))
        self.buttonClose = self.findChild(QPushButton, "CloseButton")
        self.buttonClose.clicked.connect(self.CloseWindow)
        self.scoreText = self.findChild(QLabel, "ScoreText")
        self.timeText = self.findChild(QLabel, "TimeText")
        self.levelText = self.findChild(QLabel, "LevelText")
        #------------
        self.timeText.setText("Time : " + str(time//20) + "s")
        self.scoreText.setText("Score : " + str(info.score))
        self.levelText.setText("Level : " + str(info.level))
        Speaker.play_death()

    def CloseWindow(self):
        Speaker.stop_background_music()
        self.close()

    def closeEvent(self, event):
        Speaker.stop_background_music()  # Stop the background music
        self.closed.emit()
        QDialog.closeEvent(self, event)

class StartWindow(QDialog):
    closed = pyqtSignal()
    def __init__(self, info):
        super(StartWindow, self).__init__()
        uic.loadUi("game/UI/Title.ui", self)
        self.setWindowTitle("PyQt6 Tetris (Start window)")
        self.setWindowIcon(QIcon(f"{ICO_ICON}"))
        self.show()
        #------------
        self.info = info
        self.buttonStart = self.findChild(QPushButton, "StartButton")
        self.buttonAbout = self.findChild(QPushButton, "AboutButton")
        self.buttonExit = self.findChild(QPushButton, "ExitButton")
        #------------
        self.buttonStart.clicked.connect(self.StartB)
        self.buttonAbout.clicked.connect(self.AboutB)
        self.buttonExit.clicked.connect(self.ExitB)
        #------------
        self.buttonStart.enterEvent = lambda e: Speaker.playsound(Speaker.obj(Speaker.menu_focus))
        self.buttonAbout.enterEvent = lambda e: Speaker.playsound(Speaker.obj(Speaker.menu_focus))
        self.buttonExit.enterEvent = lambda e: Speaker.playsound(Speaker.obj(Speaker.menu_focus))
        #-----------------
        self.mainw = PlayWindow(info)
        self.mainw.closed.connect(self.showStart)

    def showStart(self):
        self.show()
        self.mainw.hide()
        self.smoothTransform(360, 480)

    def StartB(self):
        Speaker.playsound(Speaker.obj(Speaker.menu_accept))
        self.smoothTransform(640, 480)
        self.hide()
        self.mainw.show()

    def smoothTransform(self, width, height):
        old_h = self.height()
        old_w = self.width()
        end_x = round(self.x() + ((self.width() - width)/2))
        end_y = round(self.y() + ((self.height() - height)/2))
        tick_duration = 20
        prevH = 9999
        prevW = 9999
        x = self.x()
        y = self.y()
        i = 0
        while not (self.height() == height and self.width() == width):
            i+=1
            diff_h = (height - self.height())/9
            diff_w = (width - self.width())/9
            offset_w = old_w - self.width()
            offset_h = old_h - self.height()
            new_x = round(x + (offset_w/2))
            new_y = round(y + (offset_h/2))
            self.move(new_x, new_y)
            if(i%2==0):Speaker.scroll()
            if(prevW != diff_w or prevH != diff_h):
                prevW = diff_w
                prevH = diff_h
                self.setFixedHeight(round(self.height() + diff_h))
                self.setFixedWidth( round(self.width() + diff_w))
            else:
                self.move(end_x, end_y)
                self.setFixedHeight(height)
                self.setFixedWidth(width)
                break
            QtTest.QTest.qWait(tick_duration)

    def AboutB(self):
        Speaker.playsound(Speaker.obj(Speaker.menu_accept))
        self.msgBox = aboutWindow.TetrisAboutWindow()
        self.msgBox.show()

    def ExitB(self):
        Speaker.playsound(Speaker.obj(Speaker.menu_back))
        self.close()

    def closeEvent(self, event):
        Speaker.stop_background_music()
        self.closed.emit()
        QDialog.closeEvent(self, event)

class GameInfo():

    def __init__(self, locked_positions, board, change_piece, current_piece, next_piece, score, lines, level, speed):
        self.locked_positions = locked_positions
        self.board = board
        self.change_piece = change_piece
        self.current_piece = current_piece
        self.next_piece = next_piece
        self.score = score
        self.lines = lines
        self.level = level
        self.speed = speed

    def reset(self):
        self.locked_positions = {}
        self.board = createField(self.locked_positions)
        self.change_piece = False
        self.current_piece = getShape()
        self.next_piece = getShape()
        self.score = 0
        self.lines = 0
        self.level = 0
        self.speed = 0

#=================================##=================================#

def launchGame():
    locked_positions = {}
    board = createField(locked_positions)
    change_piece = False
    current_piece = getShape()
    next_piece = getShape()
    score = 0
    lines = 0
    level = 0
    speed = 0
    info = GameInfo(locked_positions, board, change_piece, current_piece, next_piece, score, lines, level, speed)
    UIWindow = StartWindow(info)
    return UIWindow

#=================================##=================================#


import time
import random
import apa

from point_region import PointRegion
from strand import Strand

# Strand Start: Where in the LED strip is the first LED
SS = 0

NUMBER_OF_LEDS = 186

STEP_TIME = 0.0
PAUSE_TIME = 0.0

# Color definitions: RGB tuples
DARK    = (0,0,0)
WHITE   = (32,32,32)
RED     = (16,0,0)
GREEN   = (0,32,0)
BLUE    = (0,0,32)
DARK_GREEN = (0,20,0)
DARK_BLUE = (0,0,10)
DARK_RED = (10,0,0)
YELLOW = (25,25,0)
LIGHT_YELLOW = (10,10,0)
PURPLE = (32,0,32)
DARK_PURPLE = (10,0,32)
BLUE_GREEN = (0,10,10)

# Strand point definitions
# Each is a tuple containing a tuple of LED numbers and a color for the tuple
STAR_SPD = ((14,15,44,45,74,75), YELLOW)

base_color = random.choice([DARK])

SPIN_MID_SPD = [
            ((76,16,43,73,13,46), base_color),
            ((77,17,42,72,12,47), base_color),
            ((78,18,41,71,11,48), base_color),
            ((79,19,40,70,10,49), base_color),
            ((80,20,39,69,9,50), base_color),
            ((81,21,38,68,8,51), base_color),
            ((82,22,37,67,7,52), base_color),
            ((83,23,36,66,6,53), base_color),
            ((84,24,35,65,5,54), base_color),
            ((85,25,34,64,4,55), base_color),
            ((86,26,33,63,3,56), base_color),
            ((87,27,32,62,2,57), base_color),
            ((88,28,31,61,1,58), base_color),
            ((89,29,30,60,0,59), base_color),
        ]

SPIN_BOT_SPD = [
            ((140, 100, 171, 129, 109, 161), base_color),
            ((141, 99,  172, 128, 110, 160), base_color),
            ((142, 98, 173, 127, 111, 159), base_color),
            ((143, 97, 174, 126, 112, 158), base_color),
            ((144, 96, 175, 125, 113, 157), base_color),
            ((145, 95, 176, 124, 114, 156), base_color),
            ((146, 94, 177, 123, 115, 155), base_color),
            ((147, 93, 178, 122, 116, 154), base_color),
            ((148, 92, 179, 121, 117, 153), base_color),

            ]

ALL_SPD = SPIN_MID_SPD + SPIN_BOT_SPD

# Constants used to index the tuples stored as Strand Range Definitions.
POINTS = 0
COLOR = 1

def add_strand_regions(definitions, regions, base_number):
    for region_num, strand_range_def in enumerate(definitions):
        regions.append(led_strand.add_point_region(base_number+region_num,
                                             "region"+str(base_number+region_num),
                                             strand_range_def[POINTS],
                                             strand_range_def[COLOR]))

# We will dynamically build lists of regions from the Strand Point Definitions
spin_branch_regions = []

try:
    # Create a Strand with all LEDs
    led_strand = Strand(NUMBER_OF_LEDS)

    # Create a region with all LEDs that is DARK, ie all LEDs are off.
    # Use this to turn all LEDs off as needed.
    region_dark = led_strand.add_region(10,
                                        "region10",
                                        range(0,NUMBER_OF_LEDS),
                                        DARK)

    star_region = led_strand.add_point_region(0,
                                             "region0",
                                             STAR_SPD[POINTS],
                                             STAR_SPD[COLOR])

    led_strand.set_region(star_region)

    add_strand_regions(ALL_SPD, spin_branch_regions, 400)

    for spin_region in spin_branch_regions:
        an_led = spin_region.get_led_num(0)
        an_led.r = 64
        an_led.g = 64
        an_led.b = 64
        an_led = spin_region.get_led_num(1)
        an_led.r = 64
        an_led.g = 0
        an_led.b = 0
        an_led = spin_region.get_led_num(2)
        an_led.r = 0
        an_led.g = 64
        an_led.b = 0
        an_led = spin_region.get_led_num(3)
        an_led.r = 0
        an_led.g = 0
        an_led.b = 64
        an_led = spin_region.get_led_num(4)
        an_led.r = 0
        an_led.g = 64
        an_led.b = 64
        an_led = spin_region.get_led_num(5)
        an_led.r = 64
        an_led.g = 0
        an_led.b = 64

    while True:

        for spin_region in spin_branch_regions:
            for idx in range(1):
                led_strand.set_region(spin_region)
                led_strand.write_leds()
                #spin_region.rotate("cw")
                time.sleep(STEP_TIME)
            spin_region.rotate("cw")

finally:
    # After a keyboard interrupt or anything else that shuts things down,
    # turn off the LED strand
    print("BYE!")
    led_strand.ledstrip.reset_leds()


import dataclasses
import re
import string

from typing import Match, Optional, Iterable

ROUTE_LINE = re.compile(
    r'^\s*'
    r'(?:-|\d+[.)]?)\s*'
    r'(?P<strikethrough>(?:~~)?)'
    r'(?P<place>.*?)'
    r'(?P=strikethrough)\s*'
    r'(?:\(.*?\)\s*)?'
    r'(?P<skipped>—\s*_?skipped(?::\s*"(?P<skip_reason>.*?)")?_?\s*)?'
    r'$',
    re.MULTILINE | re.IGNORECASE)

USER_ID_PATTERN = re.compile(
    r'<@!?(?P<id>\d+)>')

QUOTES_AND_WHITESPACE = '\'"“‟‘‛”’"❛❜❝❞' + string.whitespace

def clean_route(route: str) -> Iterable['RouteNode']:
    matches = ROUTE_LINE.finditer(route)
    nodes = (RouteNode.from_match(m) for m in matches)
    named_nodes = (n for n in nodes if n.name)
    yield from named_nodes

@dataclasses.dataclass
class RouteNode:
    name: str
    visited: bool
    skip_reason: Optional[str]

    @classmethod
    def from_match(cls, match: Match) -> 'RouteNode':
        place = match.group('place').strip(QUOTES_AND_WHITESPACE)
        visited = bool(match.group('strikethrough'))
        skipped = match.group('skipped') is not None
        skip_reason = match.group('skip_reason') or ('' if skipped else None)
        return cls(name=place, visited=visited, skip_reason=skip_reason)

def gen_user_ids(content: str) -> Iterable[int]:
    matches = USER_ID_PATTERN.finditer(content)
    id_strings = (m.group('id') for m in matches)
    ids = (int(i) for i in id_strings)
    yield from ids


# Solution for http://adventofcode.com/2016/day/19
from math import log, floor, ceil

# Part 1
def part1(n):
    k = floor(log(n) / log(2))
    return 2*(n - 2**k) + 1

# Part 2
def part2(n):
    k = ceil(log(n) / log(3))
    res = n - 3**(k-1)
    if res > 3**(k-1):
        res += res - 3**(k-1)
    return res

n = 3014387
print("Part 1", part1(n))  # 1834471
print("Part 2", part2(n))  # 1420064


from initializer import Initializer, Layer
from typing import Tuple
import numpy as np

class XavierNormal(Initializer):
    """Class for Xavier normal initializer.

    Xavier initalization is commonly used for initializing the weights of a
    layer that has the sigmoid or tanh activation function. The weights are
    initialized randomly from a normal distribution with the specified mean and
    standard deviation. The mean and standard deviation are calculated as
    follows:

    mean = 0
    std = sqrt(2 / (num_inputs + num_outputs))

    where num_inputs is the number of inputs to the layer and num_outputs is
    the number of outputs from the layer.
    """
    def __call__(self, layer: Layer) -> Tuple[np.ndarray, np.ndarray]:
        """Initialize the weights of a layer.

        Args:
            layer (Layer): Layer to be initialized.

        Returns:
            Tuple[np.ndarray, np.ndarray]: Tuple containing the weights and
                biases of the layer.
        """
        # Calculate the number of inputs and outputs.
        num_inputs = np.prod(layer.input_shape)
        num_outputs = np.prod(layer.output_shape)
        # Calculate the standard deviation.
        std = np.sqrt(2.0 / (num_inputs + num_outputs))
        # Calculate the initialized weights.
        weights = np.random.normal(0.0, std, (num_inputs, num_outputs))
        # Calculate the initialized bias.
        bias = np.random.normal(0.0, std, (1, num_outputs))
        # Return the initialized weights and bias.
        return weights, bias


import gc
import sys
sys.path.append(os.getenv("Analysis"))

import math
import pandas as pd

from Analysis import Plotting

import plotly.express as px
import plotly.figure_factory as ff

import hdbscan
from scipy.spatial import distance

#HDBScan with Andrews Curve Plot
def HDBScan(df, min_cluster_size = None, min_samples = None, epsilon = None, alpha = 1.0,
            single_cluster = False, outliers = None, new_column = "Cluster", offline = None,
            transform = None):

    """
    Min_cluster_size = Litteral
    min_samples = how conservative the model is.
    epsilon = how agressive similiar clusters merge, higher equals fewer clusters. Value based on data distance units, thus value could be 50 for 50 unit distance.
    alpha = another conservative value that you probably shouldn't need to messs with, but works on a tighter scale.'

    Note Outlier should be a cutoff value.

    Allow single cluster for anomoly detection / outlier detection.
    """

    assert all([pd.api.types.is_numeric_dtype(df[column]) for column in df.columns])

    if not min_cluster_size:
        min_cluster_size = int(len(df) * 0.1)

    if not min_samples:
        min_samples = int(min_cluster_size * .333)

    if not epsilon:
        distances = distance.cdist(df.values, df.values)
        mean = distances.mean()
        std  = distances.std()
        epsilon = float(min(mean, std))

    clusterer = hdbscan.HDBSCAN(
        min_cluster_size = min_cluster_size,
        min_samples = min_samples,
        cluster_selection_epsilon = epsilon,
        alpha = alpha,
        allow_single_cluster = single_cluster,
        core_dist_n_jobs = -1,
        gen_min_span_tree = True
    )
    clusterer.fit(df)

    tmp = df.copy()
    tmp[new_column] = clusterer.labels_
    tmp["{}_Probabilities".format(new_column)] = clusterer.probabilities_
    tmp['{}_outlier_prob'.format(new_column)]  = clusterer.outlier_scores_

    if not offline:
        Plotting.dfTable(tmp.head(50), title = "Results of HDBScan")
        Plotting.dfTable(pd.DataFrame(clusterer.cluster_persistence_), title = "Cluster Persistence")
        Plotting.Title("Density Based Cluster Validity Score", description = clusterer.relative_validity_)
    else:
        print("Cluster Persistence")
        print(pd.DataFrame(clusterer.cluster_persistence_))
        print()
        print("Density Based Cluster Validity Score: {}".format(clusterer.relative_validity_))

    if outliers:

        if not isinstance(outliers, float):
            outliers = 0.85

        tmp['{}_isoutlier'.format(new_column)] = tmp['{}_outlier_prob'.format(new_column)].apply(lambda x: 1 if x > outliers else 0)

        fig = ff.create_distplot([tmp['{}_outlier_prob'.format(new_column)].values], group_labels = ["Distribution"], bin_size = 0.1)
        fig.update_layout(title = 'Outlier Probability Distribution')
        Plotting.Plot(fig, offline = offline)

        if len(tmp['{}_isoutlier'.format(new_column)].unique()) > 1:
            fig = px.scatter_matrix(tmp,
                              dimensions = [column for column in df.columns],
                              color = '{}_isoutlier'.format(new_column))
            fig.update_layout(title = "Outlier Distribution")
            Plotting.Plot(fig, offline = offline)

    tmp1 = df.copy()
    tmp1['Cluster'] = clusterer.labels_
    Plotting.AndrewsCurve(tmp1, 'Cluster', offline = offline)
    del tmp1
    fig = px.scatter_matrix(tmp,
                      dimensions = [column for column in df.columns],
                      color = new_column)
    Plotting.Plot(fig, offline = offline)

    if transform:
        return tmp

    gc.collect()
    return


# -*- coding: utf-8 -*-
"""Backup tasks.
"""
import os
import tempfile
import importlib

from collections.abc import Callable
from copy import deepcopy
from shlex import quote as shell_quote
from subprocess import CalledProcessError
from subprocess import STDOUT

from . import app_utils
from .python_utils import cmd_utils
from .python_utils import exceptions
from .python_utils import file_utils
from .python_utils import misc_utils
from .python_utils import shell_utils

_xz_map = {
    "ext": ".xz",
    "env": "XZ_OPT"
}

_gzip_map = {
    "ext": ".gz",
    "env": "GZIP_OPT"
}

_bzip2_map = {
    "ext": ".bz2",
    "env": "BZIP2"
}

_tar_comp_map = {
    "--xz": _xz_map,
    "-J": _xz_map,
    "--gzip": _gzip_map,
    "-z": _gzip_map,
    "--bzip2": _bzip2_map,
    "-j": _bzip2_map
}

class BaseTask():
    """Base task class.

    Attributes
    ----------
    logger : object
        See <class :any:`LogSystem`>.
    """
    _cmd = None

    def __init__(self, task={}, settings={}, dry_run=False, logger=None):
        """Initialize.

        Parameters
        ----------
        task : dict, optional
            A task as defined in a tasks file.
        settings : dict, optional
            Settings as defined in a settings file or a tasks file.
        dry_run : bool, optional
            Do not perform file system changes.
        logger : object
            See <class :any:`LogSystem`>.
        """
        self._task = task
        self._settings = settings
        self._dry_run = dry_run
        self.logger = logger

        self._errors = []
        self._errors_count = 0
        self._warnings_count = 0

        self._validate_task()

    def report_called_process_errors(self):
        """Report called process errors.
        """
        if self._errors:
            self.logger.warning("**Errors have been found!**")

            for err in self._errors:
                self._errors_count += 1
                self.logger.error("**Command:** %s" % str(err.cmd))
                self.logger.error("**STDERR:**\n%s" % str(err.stderr))
                self.logger.error("**STDOUT:**\n%s" % str(err.stdout))

    def _validate_task(self):
        """Validate task.

        Raises
        ------
        exceptions.MissingCommand
            See :any:`exceptions.MissingCommand`.
        SystemExit
            Halt execution.
        """
        if self._cmd is not None and not cmd_utils.which(self._cmd):
            raise exceptions.MissingCommand(self._cmd)

        valid_items = []
        ignored_items = []

        for item in set(self._task.get("items")):
            item_path = file_utils.expand_path(item)

            if os.path.exists(item_path) and not os.path.islink(item_path):
                valid_items.append(item_path)
            else:
                self._warnings_count += 1
                ignored_items.append(item_path)

        if not valid_items:
            self.logger.error("**Empty items list! Can't proceed! Leaving...**")
            raise SystemExit(1)
        else:
            self._task["items"] = sorted(valid_items)

        # If there are ignored items, log them all at once.
        if ignored_items:
            self.logger.warning("**Invalid items found**")
            self.logger.warning("**They weren't added to the backup list:**\n%s" %
                                "\n".join(ignored_items))

    def start(self):
        """Main method to start the backup process.

        Returns
        -------
        tuple
            The errors and warnings count to be logged.
        """
        self._run_hook("pre")
        kwargs = self.run()
        self._run_hook("post", **kwargs if kwargs else {})
        self.report_called_process_errors()

        return (self._errors_count, self._warnings_count)

    def _run_hook(self, hook_type, **kwargs):
        """Run hook.

        A hook is a Python function defined inside a tasks file and is referenced in one of
        the ``pre_hook`` or ``post_hook`` task options.

        Parameters
        ----------
        hook_type : str
            One of "pre" or "post".
        **kwargs
            Some tasks can return a dictionary of options that can be passed to a ``post_hook``
            function.

        Raises
        ------
        SystemExit
            Halt execution if an exception is thrown when executing a hook.
        """
        hook_definition = self._task.get("%s_hook" % hook_type)

        if not hook_definition:
            return

        hook_module_name, hook_method = hook_definition.split(".")
        hook_module_path = os.path.join(app_utils.root_folder,
                                        "UserData",
                                        "hooks",
                                        hook_module_name + ".py")

        if os.path.exists(hook_module_path):
            self.logger.info("**Attempting to run %s-hook...**" % hook_type)

            spec = importlib.util.spec_from_file_location(hook_module_name, hook_module_path)
            module = importlib.util.module_from_spec(spec)
            spec.loader.exec_module(module)

            hook = getattr(module, hook_method)

            if isinstance(hook, Callable):
                task_copy = deepcopy(self._task)

                for key in ["pre_hook", "post_hook"]:
                    if key in task_copy:
                        del task_copy[key]

                try:
                    hook(task=task_copy, settings=self._settings,
                         dry_run=self._dry_run, logger=self.logger, **kwargs)
                except Exception as err:
                    self.logger.error(err)
                    raise SystemExit(1)
        else:
            self.logger.info("**%s-hook not configured.**" % hook_type.capitalize())

    def run(self):
        """Run a backup task.

        This method needs to be implemented in a sub-class of this super class.

        Raises
        ------
        exceptions.MethodNotImplemented
            See :any:`exceptions.MethodNotImplemented`.
        """
        raise exceptions.MethodNotImplemented("run")

class RsyncLocalTask(BaseTask):
    """Task to perform backups using the rsync command.
    """

    def __init__(self, **kwargs):
        """Initialize.

        Parameters
        ----------
        **kwargs
            See <class :any:`BaseTask`>.
        """
        self._cmd = "rsync"
        super().__init__(**kwargs)

    def run(self):
        """See <class :any:`BaseTask.run`>.
        """
        root_destination = self._task.get("destination", "")
        seps = os.sep + os.altsep if os.altsep else os.sep
        cmd = [self._cmd] + self._task.get("rsync_args", [])
        exclude_patterns = ["--exclude=%s" % shell_quote(ptrn) for ptrn in
                            self._settings.get("ignored_patterns", [])]
        cmd += exclude_patterns

        if self._dry_run:
            self.logger.log_dry_run(
                "**Destination folder will be created:**\n%s" %
                root_destination)
        else:
            os.makedirs(root_destination, exist_ok=True)

        for source_item in self._task.get("items"):
            if file_utils.is_real_dir(source_item):
                self.logger.info(shell_utils.get_cli_separator("-"), date=False)
                item_destination = os.path.join(
                    root_destination,
                    # Trick to join two absolute paths.
                    # Source: https://stackoverflow.com/a/50846104/4147432 <3
                    os.path.splitdrive(os.path.dirname(source_item))[1].lstrip(seps)
                )

                final_cmd = " ".join(cmd + [shell_quote(source_item),
                                            shell_quote(item_destination)])

                self.logger.info("**Mirroring:** %s" % source_item, date=False)

                if self._dry_run:
                    self.logger.log_dry_run("**Command that will be executed:**\n%s" % final_cmd)
                else:
                    try:
                        start_time = misc_utils.get_date_time()

                        # Forced to use shell=True. Otherwise, --exclude rsync arguments
                        # wouldn't freaking work due to shell expansions nonsense.
                        # I tried using the --from-files, but its logic is too "convoluted".
                        cmd_utils.run_cmd(final_cmd,
                                          stdout=None,
                                          stderr=STDOUT,
                                          shell=True,
                                          check=True)

                        finished_time = misc_utils.get_date_time()
                        elapsed_time = misc_utils.get_time_diff(start_time, finished_time)
                        self.logger.info("**Elapsed time:** %s" % elapsed_time, date=False)
                    except CalledProcessError as err:
                        self._errors.append(err)
                        continue
            else:
                self._warnings_count += 1
                self.logger.warning("**Omitted path. Not a directory:**")
                self.logger.warning(source_item)

class TarLocalTask(BaseTask):
    """Task to perform backups using the tar command.
    """

    def __init__(self, **kwargs):
        """Initialize.

        Parameters
        ----------
        **kwargs
            See <class :any:`BaseTask`>.
        """
        self._cmd = "tar"
        super().__init__(**kwargs)

    def __get_compression_data(self):
        """Get compression data.

        Scan ``tar_func_args`` option for tar compression arguments and, if found, return
        a dictionary with the extension that will be used to name the compressed archive
        and the environment variable used to define the compression level.

        Returns
        -------
        dict|False
            Compression data.
        """
        for arg in self._task.get("tar_func_args", []):
            if arg in _tar_comp_map:
                return {
                    "ext": _tar_comp_map[arg]["ext"],
                    "env": _tar_comp_map[arg]["env"]
                }

        return False

    def run(self):
        """See <class :any:`BaseTask.run`>.

        Returns
        -------
        dict
            Dictionary with data that will be passed to a ``post_hook``.
        """
        processed_list = app_utils.FilteredFilesList(
            self._task.get("items"), self._settings.get("ignored_patterns", []))
        full_list_of_files = "\n".join(processed_list.get_full_list_of_files())
        tar_env = cmd_utils.get_environment()
        compression_data = self.__get_compression_data()
        compression_level = self._task.get("tar_compression_level", "-7")
        archive_destination = self._task.get("destination", "")
        archive_file_name = "%s%s.tar" % \
            (self._task.get("destination_prefix", ""),
             misc_utils.micro_to_milli(misc_utils.get_date_time("filename")))
        archive_file_ext = compression_data["ext"] if compression_data else ""
        archive_path = os.path.join(archive_destination,
                                    archive_file_name + archive_file_ext)

        if self._dry_run:
            self.logger.log_dry_run(
                "**Destination folder will be created:**\n%s" %
                archive_destination)
        else:
            os.makedirs(archive_destination, exist_ok=True)

        if compression_data:
            tar_env[compression_data["env"]] = str(compression_level)

        with tempfile.NamedTemporaryFile(prefix="%s-" % self._task.get("type")) as tmp_file:
            tmp_file.write(str.encode(full_list_of_files))
            tmp_file.seek(0)

            cmd = [
                self._cmd,
                "--create",
            ] + self._task.get("tar_func_args", []) + [
                "--file",
                shell_quote(archive_path),
                "--files-from=%s" % tmp_file.name
            ] + self._task.get("tar_opt_args", [])

            if self._dry_run:
                self.logger.log_dry_run("**Command that will be executed:**\n%s" % " ".join(cmd))
            else:
                try:
                    cmd_utils.run_cmd(cmd,
                                      stdout=None,
                                      stderr=STDOUT,
                                      check=True,
                                      env=tar_env)
                except CalledProcessError as err:
                    self._errors.append(err)

        return {"archive_path": archive_path}

_tasks_map = {
    "base_task": BaseTask,
    "rsync_local": RsyncLocalTask,
    "tar_local": TarLocalTask
}

def factory(task_name="base_task"):
    """Task factory.

    Parameters
    ----------
    task_name : str, optional
        Task name.

    Returns
    -------
    object
        A task class.
    """
    return _tasks_map[task_name]

if __name__ == "__main__":
    pass


# https://www.interviewbit.com/problems/word-ladder-ii/
from collections import defaultdict

def similar_words(word):
    alphabet = 'abcdefghijklmnopqrstuvwxyz'
    return [''.join([word[:i], letter, word[i + 1:]]) for letter in alphabet for i in xrange(len(word))]

def find_all(start, prevs, end):
    res = []
    def recursive_search(start, arr):
        last = arr[-1]
        if last == start:
            res.append(arr[::-1])
            return

        for prev in prevs[last]:
            recursive_search(start, arr[::] + [prev])

    recursive_search(start, [end])
    res.sort()
    return res

def find_transformation(start, end, words):
    if start == end:
        return [[start]]

    frontier, steps, seen, words, found, prevs = [start], 1, {start}, set(words), False, defaultdict(list)
    while frontier:
        next_frontier, steps = [], steps + 1
        for word in frontier:
            for new_word in similar_words(word):
                if new_word == end:
                    prevs[new_word].append(word)
                    found = True
                elif new_word in words and new_word not in seen:
                    prevs[new_word].append(word)
                    seen.add(new_word)
                    next_frontier.append(new_word)

        if found:
            break

        frontier = next_frontier

    if not found:
        return []

    return find_all(start, prevs, end)

# =====================

from collections import deque
def find_transformation2(start, end, dictV):
    dictV = set(dictV)
    dictV.add(end)
    queue = deque([(start, 1, [start])])
    visited = set()
    min_length = float('inf')
    ladders = []
    while queue:
        word, length, ladder = queue.popleft()
        visited.add(word)
        if word == end:
            if length == min_length:
                ladders.append(ladder)
            elif length < min_length:
                min_length = length
                ladders = [ladder]
        for i in range(len(word)):
            for c in "abcdefghijklmnopqrstuvwxyz":
                new_word = word[:i] + c + word[i+1:]
                if new_word in dictV and new_word not in visited:
                    queue.append((new_word, length+1, ladder+[new_word]))
    return ladders


import turtle
__import__("turtle").__traceable__ = False

jack = turtle.Turtle()
def drawsquare(t, sz):
    for i in ['black', 'white', 'green', 'cyan']:
        t.color(i)
        t.forward(sz)
        t.left(90)
wn = turtle.Screen()
wn.bgcolor("indigo")
wn.title("Jack draws stuff")
size = 20
for i in range(10):
    drawsquare(jack, size)
    jack.color('indigo')
    jack.forward(size+50)
wn,mainloop()


#!/usr/bin/env python

import sqlite3
import os
import sys
import shutil

dbs = {
    "xfac" :     "hg19.xfac.1e-4",
    "uniprobe" : "hg19.uniprobe.1e-4",
    "jaspar" :   "hg19.jaspar.1e-4",
    "taipale" :  "hg19.taipale.1e-4"
}

paddings = [0]

cwd = os.getcwd()

def create_tables(dbs):
    for k in dbs.keys():
        for p in paddings:
            sqlite_fn = os.path.join(cwd, "%s_ids" % (k), "probe.db.%d.starch.reduced.mtx.sqlite" % (p))
            if os.path.exists(sqlite_fn):
                os.remove(sqlite_fn)
            table_name = "%s" % (k)
            # connect
            conn = sqlite3.connect(sqlite_fn)
            c = conn.cursor()
            # create
            c.execute('CREATE TABLE IF NOT EXISTS {tn} ({nf1} {ft1} PRIMARY KEY, {nf2} {ft2});'\
                      .format(tn=table_name, nf1='probe', ft1='TEXT', nf2='bitstring', ft2='TEXT'))
            # commit changes and close
            conn.commit()
            conn.close()

def import_reduced_binary_mtx(dbs):
    for k in dbs.keys():
        for p in paddings:
            sys.stderr.write("%s | %d\n" % (k, p))
            sqlite_fn = os.path.join(cwd, "%s_ids" % (k), "probe.db.%d.starch.reduced.mtx.sqlite" % (p))
            if not os.path.exists(sqlite_fn):
                raise SystemError("cannot find db [%s]\n" % (sqlite_fn))
            table_name = "%s" % (k)
            # connect
            conn = sqlite3.connect(sqlite_fn)
            c = conn.cursor()
            # import
            with open(os.path.join(cwd, "%s_ids" % (k), "probe.db.%d.starch.reduced.mtx" % (p)), "r") as pfh:
                for line in pfh:
                    (probe, bitstring) = line.rstrip().split('\t')
                    try:
                        c.execute("INSERT INTO {tn} ({pf}, {bf}) VALUES ('{pv}', '{bv}');".\
                                  format(tn=table_name, pf='probe', bf='bitstring', pv=probe, bv=bitstring))
                    except sqlite3.IntegrityError:
                        sys.stderr.write('ERROR: probe ID already exists in PRIMARY KEY column {}\n'.format('probe'))
            # cleanup
            conn.commit()
            conn.close()

def main():
    create_tables(dbs)
    import_reduced_binary_mtx(dbs)

if __name__ == "__main__":
    main()


import datetime
from odoo.addons.l10n_it_edi.tests.common import TestItEdi
from odoo.tests.common import tagged
from odoo import Command

@tagged('post_install_l10n', 'post_install', '-at_install')
class TestItEdiPa(TestItEdi):

    @classmethod
    def setUpClass(cls):
        super().setUpClass()

        cls.Move = cls.env['account.move'].with_company(cls.company)
        journal_code = cls.company_data_2['default_journal_sale'].code
        cls.split_payment_tax = cls.env['account.tax'].with_company(cls.company).search([('name', '=', '22% SP')])
        cls.split_payment_line_data = {
            'name': 'standard_line',
            'quantity': 1,
            'price_unit': 800.40,
            'tax_ids': [Command.set(cls.split_payment_tax.ids)],
        }

        cls.pa_partner_invoice_data = {
            'move_type': 'out_invoice',
            'invoice_date': datetime.date(2022, 3, 24),
            'invoice_date_due': datetime.date(2022, 3, 24),
            'partner_id': cls.italian_partner_b.id,
            'partner_bank_id': cls.test_bank.id,
            'invoice_line_ids': [
                Command.create(cls.split_payment_line_data),
            ],
            'l10n_it_origin_document_type': 'purchase_order',
            'l10n_it_origin_document_date': datetime.date(2022, 3, 23),
            'l10n_it_origin_document_name': f"{journal_code}/2022/0001",
            'l10n_it_cup': '0123456789',
            'l10n_it_cig': '0987654321'
        }
        cls.pa_partner_invoice = cls.Move.create(cls.pa_partner_invoice_data)
        cls.pa_partner_invoice_2 = cls.Move.create({
            **cls.pa_partner_invoice_data,
            'l10n_it_origin_document_type': False,
        })
        cls.pa_partner_invoice._post()

    def test_send_pa_partner(self):
        """ ImportoTotaleDocumento must include VAT
            ImportoPagamento must be without VAT
            EsigibilitaIva of the Split payment tax must be 'S'
            The orgin_document fields must appear in the XML.
            Use reference validator: https://fex-app.com/servizi/inizia
        """
        self._assert_export_invoice(self.pa_partner_invoice, 'split_payment.xml')


from abc import ABC
from retry import retry

import controller.array_action.errors as controller_errors
from controller.array_action.array_mediator_interface import ArrayMediator
from controller.array_action.config import FC_CONNECTIVITY_TYPE, ISCSI_CONNECTIVITY_TYPE
from controller.array_action.errors import NoConnectionAvailableException, UnsupportedConnectivityTypeError
from controller.common.csi_logger import get_stdout_logger
from controller.controller_server import utils

logger = get_stdout_logger()

class ArrayMediatorAbstract(ArrayMediator, ABC):

    @retry(NoConnectionAvailableException, tries=11, delay=1)
    def map_volume_by_initiators(self, vol_id, initiators):
        host_name, connectivity_types = self.get_host_by_host_identifiers(initiators)

        logger.debug(
            "hostname : {}, connectivity_types  : {}".format(host_name,
                                                             connectivity_types))

        connectivity_type = utils.choose_connectivity_type(connectivity_types)

        if FC_CONNECTIVITY_TYPE == connectivity_type:
            array_initiators = self.get_array_fc_wwns(host_name)
        elif ISCSI_CONNECTIVITY_TYPE == connectivity_type:
            array_initiators = self.get_iscsi_targets_by_iqn()
        else:
            raise UnsupportedConnectivityTypeError(connectivity_type)

        mappings = self.get_volume_mappings(vol_id)
        if len(mappings) >= 1:
            logger.debug(
                "{0} mappings have been found for volume. the mappings are: {1}".format(len(mappings), mappings))
            if len(mappings) == 1:
                mapping = list(mappings)[0]
                if mapping == host_name:
                    logger.debug("idempotent case - volume is already mapped to host.")
                    return mappings[mapping], connectivity_type, array_initiators
            raise controller_errors.VolumeMappedToMultipleHostsError(mappings)

        logger.debug(
            "no mappings were found for volume. mapping vol : {0} to host : {1}".format(
                vol_id, host_name))

        try:
            lun = self.map_volume(vol_id, host_name)
            logger.debug("lun : {}".format(lun))
        except controller_errors.LunAlreadyInUseError as ex:
            logger.warning(
                "Lun was already in use. re-trying the operation. {0}".format(
                    ex))
            for i in range(self.max_lun_retries - 1):
                try:
                    lun = self.map_volume(vol_id, host_name)
                    break
                except controller_errors.LunAlreadyInUseError as inner_ex:
                    logger.warning(
                        "re-trying map volume. try #{0}. {1}".format(i,
                                                                     inner_ex))
            else:  # will get here only if the for statement is false.
                raise ex

        return lun, connectivity_type, array_initiators

    @retry(NoConnectionAvailableException, tries=11, delay=1)
    def unmap_volume_by_initiators(self, vol_id, initiators):
        host_name, _ = self.get_host_by_host_identifiers(initiators)

        self.unmap_volume(vol_id, host_name)