import os.path
import random
from Gift import Gift
import pygame
from Gift import GiftType
from Ship import BulletType
from Ship import Ship
from enum import Enum

MAX_SPEED = 30
MAX_BULLET_VELOCITY = 25
MIN_BULLET_RATIO = 10
COIN_IMAGE = pygame.image.load(os.path.join('Assets', 'coin.png'))
COIN_IMAGE = pygame.transform.scale(COIN_IMAGE, (40, 40))
pygame.mixer.init()
pygame.mixer.Channel(1).set_volume(0.1)
pygame.mixer.Channel(0).set_volume(0.2)
pygame.mixer.Channel(4).set_volume(0.1)
pygame.mixer.Channel(1).play(pygame.mixer.Sound('Assets/main_sound.mp3'))

# pygame.mixer.music.load('Assets/chicken_sound.mp3')

def collide(rect1, rect2):
    rect1_left = rect1.x
    rect1_top = rect1.y
    rect1_right = rect1.x + rect1.width
    rect1_bottom = rect1.y + rect1.height
    rect2_left = rect2.x
    rect2_top = rect2.y
    rect2_right = rect2.x + rect2.width
    rect2_bottom = rect2.y + rect2.height

    if (rect2_bottom > rect1_top > rect2_top or rect2_bottom > rect1_bottom > rect2_top) and \
            (rect2_left < rect1_left < rect2_right or rect2_left < rect1_right < rect2_right):
        return 1
    return 0

class MapSideType(Enum):
    LEFT = 1
    TOP = 2
    RIGHT = 3
    BOTTOM = 4

class GamePhase(Enum):
    ENEMIES1 = 1
    ENEMIES2 = 2
    BOSS = 3
    BONUS = 4

class World:
    def __init__(self, height, width, display):
        self.height = height
        self.width = width
        self.enemies = []
        self.gifts = []
        self.boss = None
        self.display = display
        self.gift_width = 20
        self.gifts_height = 20
        self.ship_width = 46
        self.ship_height = 46
        self.gift_height = 20
        self.gift_width = 20
        self.star_width = 2
        self.star_height = 2
        self.star_color = (255, 255, 255)
        self.ship_x_value = (width - self.ship_width) // 2
        self.ship_y_value = height - self.ship_height - 10
        self.background_color = (8, 17, 53)
        self.background_stars = []
        self.ship_color = (255, 255, 255)
        self.enemy_color = (255, 0, 100)
        self.ship = Ship(self.ship_x_value, self.ship_y_value, self.ship_height, self.ship_width, -1, self.ship_color, self)
        self.ship.bullet_type = BulletType.ONE
        self.ship.bullet_height = 10
        self.ship.bullet_width = 10
        self.ship.bullet_ratio = 25
        self.ship.bullet_velocity = 10
        self.enemy_velocity = 3
        self.coins = []
        self.coin_height = 31
        self.coin_width = 31
        self.game_phase = GamePhase.ENEMIES1
        self.enemy_wave = 30
        self.bonus_wave = 50
        self.boss_health_points = 50

    def collisions(self):
        boss_rect = pygame.Rect(self.boss.x, self.boss.y, self.boss.width, self.boss.height)
        ship_rect = pygame.Rect(self.ship.x, self.ship.y, self.ship.width, self.ship.height)
        boss = self.boss
        i = 0
        while i < len(self.enemies):
            enemy = self.enemies[i]
            if enemy.is_alive:
                enemy_rect = pygame.Rect(enemy.x, enemy.y, enemy.width, enemy.height)
                if collide(enemy_rect, ship_rect):
                    # pygame.mixer.music.play()
                    pygame.mixer.Channel(0).play(pygame.mixer.Sound('Assets/chicken_sound.mp3'))
                    enemy.is_alive = False
                    self.ship.points += enemy.award
                    self.ship.color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
                    self.ship.health_points -= 1
            i += 1

        i, j = 0, 0
        while i < len(self.ship.bullets):
            j = 0
            bullet = self.ship.bullets[i]
            while j < len(self.enemies):
                enemy = self.enemies[j]
                if enemy.is_alive:
                    if collide(bullet.body, pygame.Rect(enemy.x, enemy.y, enemy.width, enemy.height)):
                        # pygame.mixer.music.play()
                        pygame.mixer.Channel(0).play(pygame.mixer.Sound('Assets/chicken_sound.mp3'))
                        del self.ship.bullets[i]
                        enemy.is_alive = False
                        self.ship.points += enemy.award
                        # enemies[j].color = (1, 1, 1)
                        i -= 1
                        j -= 1
                        break
                j += 1
            if (self.game_phase == GamePhase.BOSS):
                if boss.is_alive and collide(bullet.body, boss_rect):
                    self.boss.health_points -= 1
                    print(self.boss.health_points)
                    if self.boss.health_points <= 0:
                        self.boss.is_alive = False
                    del self.ship.bullets[i]
                    i -= 1
            i += 1

        i = 0
        for enemy in self.enemies:
            while i < len(enemy.bullets):
                bullet = enemy.bullets[i]
                # bullet.color = (255, 255, 255)
                if collide(bullet.body, ship_rect):
                    del enemy.bullets[i]
                    pygame.mixer.Channel(3).play(pygame.mixer.Sound('Assets/splash_sound.mp3'))
                    self.ship.color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
                    self.ship.health_points -= 1
                    i -= 1
                i += 1
            i = 0
        if (self.game_phase == GamePhase.BOSS):
            if boss.is_alive and collide(boss_rect, ship_rect):
                boss.health_points -= 1
                self.ship.health_points -= 1
                if boss.health_points <= 0:
                    boss.is_alive = False
                if self.ship.health_points <= 0:
                    self.ship.is_alive = False

        i = 0
        while i < len(boss.bullets):
            bullet = boss.bullets[i]
            if collide(bullet.body, ship_rect):
                del boss.bullets[i]
                pygame.mixer.Channel(3).play(pygame.mixer.Sound('Assets/splash_sound.mp3'))
                self.ship.color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
                self.ship.health_points -= 1
                i -= 1
            i += 1
        i = 0

        while i < len(self.gifts):
            if collide(self.gifts[i].body, ship_rect):
                pygame.mixer.Channel(4).play(pygame.mixer.Sound('Assets/gift_sound.mp3'))
                self.upgrade_ship(self.gifts[i])
                del self.gifts[i]
                i -= 1
            i += 1

        if (self.game_phase == GamePhase.BONUS):
            i = 0
            while i < len(self.coins):
                if collide(self.coins[i][0], ship_rect):
                    pygame.mixer.Channel(4).play(pygame.mixer.Sound('Assets/gift_sound.mp3'))
                    self.ship.points += 100
                    del self.coins[i]
                    i -= 1
                i += 1

    def upgrade_ship(self, gift):
        print(gift.gift_type)
        if gift.gift_type == GiftType.SPEED and self.ship.x_velocity < MAX_SPEED:
            self.ship.x_velocity += gift.value
            self.ship.y_velocity += gift.value

        elif gift.gift_type == GiftType.BULLET_VELOCITY and self.ship.bullet_velocity < MAX_BULLET_VELOCITY:
            self.ship.bullet_velocity += gift.value

        elif gift.gift_type == GiftType.BULLET_COLOR:
            self.ship.bullet_color = gift.value

        elif gift.gift_type == GiftType.BULLET_RATIO and self.ship.bullet_ratio > MIN_BULLET_RATIO:
            self.ship.bullet_ratio -= gift.value

        elif gift.gift_type == GiftType.WEAPON_UPGRADE:
            self.ship.upgrade_bullet_type()

    def spawn_gift(self):

        value = 1
        # gift_type = random.randint(1, 5)
        gift_type = random.choice(list(GiftType))
        img = ''
        if gift_type == GiftType.SPEED:
            value = random.randint(1, 3)
            img = 'gift_speed.webp'
        elif gift_type == GiftType.BULLET_VELOCITY:
            value = random.randint(1, 5)
            img = 'gift_bullet_velocity.webp'
        elif gift_type == GiftType.BULLET_COLOR:
            value = (random.randint(1, 255), random.randint(1, 255), random.randint(1, 255))
            img = 'gift_color.webp'
        elif gift_type == GiftType.BULLET_RATIO:
            value = random.randint(1, 5)
            img = 'gift_bullet_ratio.webp'
        elif gift_type == GiftType.WEAPON_UPGRADE:
            value = 1
            img = 'gift_weapon.webp'

        x = random.randint(0, self.width - self.gift_width)
        velocity = random.randint(2, 8)
        gift_image = pygame.image.load(os.path.join('Assets', img))
        gift_image = pygame.transform.scale(gift_image, (35, 35))
        gift = Gift(x, self.gift_height, self.gift_height, self.gift_width, velocity, value, gift_type, self,
                    gift_image)
        self.gifts.append(gift)

    def spawn_random_enemy(self):
        while True:
            tmp_x = random.randint(0, self.width - self.ship_width)
            tmp_y = random.randint(0, self.height // 2)
            flag = True
            for enemy in self.enemies:
                if collide(enemy, pygame.Rect(tmp_x, tmp_y, self.ship_width, self.ship_height)):
                    flag = False
                    break
            if flag:
                new_enemy = Ship(tmp_x, tmp_y, self.ship_height, self.ship_width, 1, self.enemy_color, self)
                # new_enemy.bullet_velocity = 5
                # new_enemy.bullet_ratio = self.
                new_enemy.bullet_color = (255, 100, 0)
                self.enemies.append(new_enemy)
                break

    def spawn_enemy1(self):
        enemy = Ship(10, 10, self.ship_height, self.ship_width, 1, self.enemy_color, self)
        enemy.change_bullet_color((255, 100, 0))
        enemy.bullet_ratio = random.randint(200, 250)
        self.enemies.append(enemy)

    def create_star(self):
        new_star = pygame.Rect(random.randint(0, self.width), -self.height, self.star_width, self.star_height)
        self.background_stars.append([new_star, random.randint(2, 12)])

    def create_coin(self):
        new_coin = pygame.Rect(random.randint(0, self.width), -self.height, self.coin_width, self.coin_height)
        self.coins.append([new_coin, random.randint(2, 7)])

    def move_stars(self):
        tmp = []
        for star in self.background_stars:
            star[0].y += star[1]
            if star[0].y <= self.height:
                tmp.append(star)
                pygame.draw.rect(self.display.window, self.star_color, star[0])

        self.background_stars = tmp[:]

    def move_coins(self):
        tmp = []
        for coin in self.coins:
            coin[0].y += coin[1]
            if coin[0].y <= self.height:
                tmp.append(coin)
                # pygame.draw.rect(self.display.window, (0,255,0), coin[0])
                self.display.window.blit(COIN_IMAGE, (coin[0].x - 5, coin[0].y - 5))
        self.coins = tmp[:]

    def move_gifts(self):
        tmp = []
        for gift in self.gifts:
            if gift.move():
                tmp.append(gift)
                # pygame.draw.rect(self.display.window, gift.color, gift.body)
                self.display.window.blit(gift.img, (gift.body.x - 9, gift.body.y - 9))
        self.gifts = tmp[:]

    def move_enemy1(self, enemy):
        if (enemy.y // 150) % 2 == 0:
            if enemy.x > self.display.width - 2 * enemy.width:
                enemy.x_velocity = 0
                enemy.y_velocity = self.enemy_velocity
            else:
                enemy.x_velocity = self.enemy_velocity
                enemy.y_velocity = 0
        else:
            if enemy.x < 2 * enemy.width:
                enemy.x_velocity = 0
                enemy.y_velocity = self.enemy_velocity
            else:
                enemy.x_velocity = -self.enemy_velocity
                enemy.y_velocity = 0
        enemy.x += enemy.x_velocity
        enemy.y += enemy.y_velocity
        if enemy.y >= self.display.height:
            enemy.is_alive = False

    def spawn_enemy2(self):

        map_side = random.choice(list(MapSideType))
        while map_side == MapSideType.BOTTOM:
            map_side = random.choice(list(MapSideType))
        x, y = 0, 0
        x_velocity, y_velocity = random.randint(1, 5), random.randint(0, 3)
        if map_side == MapSideType.LEFT:
            x = -self.ship_width
            y = random.randint(0, 0.25 * self.height)
        elif map_side == MapSideType.TOP:
            x = random.randint(-self.ship_width, self.width + self.ship_width)
            y = -self.ship_height
            x_velocity = random.randint(-5, 5)
            y_velocity = random.randint(1, 3)
        elif map_side == MapSideType.RIGHT:
            x = self.width
            y = random.randint(0, 0.25 * self.height)
            x_velocity = -x_velocity

        enemy = Ship(x, y, self.ship_height, self.ship_width, 1, self.enemy_color, self)
        enemy.change_bullet_color((255, 100, 0))
        enemy.bullet_ratio = random.randint(200, 250)
        enemy.x_velocity = x_velocity
        enemy.y_velocity = y_velocity
        self.enemies.append(enemy)

    def move_enemy2(self, enemy):
        enemy.x += enemy.x_velocity
        enemy.y += enemy.y_velocity
        if enemy.y >= self.height or enemy.y < -enemy.height or enemy.x > self.width or enemy.x < -enemy.width:
            enemy.is_alive = False

    def move_enemies1(self):
        for enemy in self.enemies:
            self.move_enemy1(enemy)

    def move_enemies2(self):
        for enemy in self.enemies:
            self.move_enemy2(enemy)

    def create_boss(self):
        width = 150
        height = 120
        self.boss = Ship((self.width - width) // 2, height, height, width, 1, (255, 0, 0), self)
        self.boss.award = 1000
        self.boss.health_points = self.boss_health_points
        self.boss.bullet_type = BulletType.THREE_WIDE
        self.boss.bullet_velocity = 1
        self.boss.bullet_ratio = 80

    def change_boss_velocity(self, map_side):
        x_velocity, y_velocity = random.randint(1, 5), random.randint(0, 3)
        if map_side == MapSideType.BOTTOM:
            y_velocity = -y_velocity
        elif map_side == MapSideType.TOP:
            x_velocity = random.randint(-5, 5)
            y_velocity = random.randint(1, 3)
        elif map_side == MapSideType.RIGHT:
            y = random.randint(0, 0.25 * self.height)
            x_velocity = -x_velocity
        self.boss.x_velocity = x_velocity
        self.boss.y_velocity = y_velocity

    def move_boss(self):
        boss = self.boss
        boss.x += boss.x_velocity
        boss.y += boss.y_velocity
        if boss.y >= self.height - 3 * boss.height:
            self.change_boss_velocity(MapSideType.BOTTOM)
        elif boss.y < 40:
            self.change_boss_velocity(MapSideType.TOP)
        elif boss.x > self.width - boss.width - 40:
            self.change_boss_velocity(MapSideType.RIGHT)
        elif boss.x < 40:
            self.change_boss_velocity(MapSideType.LEFT)

    def change_phase(self):
        phase = self.game_phase
        if phase == GamePhase.ENEMIES1:
            self.game_phase = GamePhase.ENEMIES2
        elif phase == GamePhase.ENEMIES2:
            self.game_phase = GamePhase.BOSS
        elif phase == GamePhase.BOSS:
            self.game_phase = GamePhase.BONUS
        elif phase == GamePhase.BONUS:
            self.game_phase = GamePhase.ENEMIES1


#! python3

'''
Overview: pubCrawl.py is a python script designed to "scrape" the webpage of a provided URL for "actionable items"(URLs, redirects, resource references, etc).
'''

#*IMPORT BLOCK BEGIN*
from bs4 import BeautifulSoup as bs
from selenium import webdriver
from selenium.webdriver.firefox.options import Options as firefoxOptions
from urllib.parse import urlparse
import datetime
import json
import os.path
import random
import requests
import re
import string
import sys
#*IMPORT BLOCK END*

#*FUNCTION BLOCK BEGIN*
#**STUB BLOCK BEGIN**
#***FUTURE VERSION BLOCK BEGIN***

#***FUTURE VERSION BLOCK END***

#**STUB BLOCK END**

def userInputHandler(systemInput,defaultURL = "https://www.instagram.com/",testMode = False,argValue=" "):
	'''
	Function for accepting & parsing user input from the console line.  Will return a default value(defaultURL) if no value is given by the user.
	:param systemInput: Input Data
	:type systemInput: str
	:param defaultURL: URL provided to the function to indicate the URL that is to be returned if no value is supplied by the user during initialization of the program
	:type defaultURL: str
	:param testMode: Testing value; value to inform the function whether it is operating in a function testing scenario or not.  Default is set to "False" to help assure that the function operates with the assumption of a live environment unless instructed otherwise.
	:type testMode: boolean
	:param argValue: Testing value; value to be provided when testing the function that provides a value in place of "sys.argv[1]" which will not be supplied when running unit tests from test_pubcrawl.py
	:type argValue: str
	:return: str
	'''

	try:
		if(len(systemInput) <= 1):
			cleanedURL = defaultURL
		else:
			if testMode == False:
				argValue = sys.argv[1]
			else:
				argValue = argValue
			providedURL = argValue
			urlCleaner = pubCrawl(providedURL)
			cleanedURL = urlCleaner.userInputParser(providedURL)
		return(cleanedURL)
	except Exception as e:
		logger = pubCrawl(systemInput)
		logger.logHandler(e, 'userInputHandler', 'ERROR')

class pubCrawl:
	'''
	Class designed scrape target site/URL's code for visible URLs.  It will then take the results & write them to a text file for review.
	'''
	#*STUB BLOCK BEGIN**
	#**FUTURE VERSION BLOCK BEGIN***
	def recursiveDepthManager(self,intDepth = 1):
		'''
		#!STUB(Future Feature)
		Function for managing & orchestrating the recursive function of user defined depths of scraping on found resources.
		:param intDepth: value that defines how far down the program should look for URLs within the URLs that it does find. Will be adjusted & implemented in later version.
		:type intDepth: int
		'''
		if(intDepth == 1):
			pass
		else:
			pass

	def siteRepCheck(self):
		'''
		#!STUB(Future Feature)
		Function for integrating API calls on malicious site checkers(Brightcloud, Virustotal, etc) to automatically check provided URLs for malicious activity/poor community reputation.
		'''
		pass

	#**FUTURE VERSION BLOCK END***
	#*STUB BLOCK END**

	#Lambda designed to read str pool of data & output it as a dictionary.  Expected output: dict
	dataPoolSorter = lambda self, data : json.loads(data)

	#Lambda designed to provide ease of operating system filepath construction.  Expected output: str
	pathCombiner = lambda self, firstFileValue, secondFileValue : os.path.join(firstFileValue, secondFileValue)

	def __init__(self, inputURL,scanDepth = 0, testMode = False):
		'''
		Function for initializing the pubCrawl class when it is called.
		:param inputURL: URL submitted for scraping by the program
		:type inputURL: str
		:param testMode: value to inform the class if it is operating under testing conditions or not
		:type testMode: boolean
		'''
		self.inputURL = inputURL
		self.scanDepth = scanDepth
		self.testMode = testMode

	def userInputParser(self,providedURL):
		'''
		Function for attempting to clean up user's input & parsing it properly for future connection/handling.
		:param providedURL: URL passed to the function to be evaluated & parsed to assure that the working URL is constructed in a predictable, more absolute way
		:type providedURL: str
		:return: str
		'''

		print(f"userInputParser's providedURL: {providedURL}")
		try:
			parsedURL = urlparse(providedURL)
			if parsedURL[1] != '':	#Check to determine if the value in parsedURL's netloc(parsedURL[1]) is populated.
				checkedURL = self.urlNetLocCreator(parsedURL)
			else:
				checkedURL = self.schemeIterator(parsedURL)

			return checkedURL

		except Exception as e:
			self.logHandler(e, 'userInputParser', 'ERROR')

	def schemeIterator(self,providedRelativeURL, schemeOptions = ['https','http']):
		'''
		Function for attempting to regulate & massage user inputted URLs for better functionality, primarily while working with the requests library.
		:param providedRelativeURL: Value to define the URL that will have different schemes applied to it to assure that it will lead to a functional URL.
		:type providedRelativeURL: str
		:param schemeOptions: list of options to be iterated through to be applied to the providedRelativeURL if no schemes have already been applied to providedRelativeURL when it was submitted.
		:type schemeOptions: list of strings that define URL schemes to be tested. This will be in order to assure that the URL provided will resolve properly.
		:return str:
		'''

		try:
			for scheme in schemeOptions:
				urlToCheck = f"{scheme}://{providedRelativeURL[2]}/" #Line that reconstructs the value in providedURL's path to add the current tested scheme to form "{SCHEME}://{PATH}"({http}://{scanme.nmap.org}" excluding curley braces)
				try: #Block to test the relative provided URL with the applied scheme
					request = requests.get(urlToCheck)
					checkedURL = urlToCheck
					break
				except:
					continue

			return checkedURL

		except Exception as e:
			self.logHandler(e, 'schemeIterator', 'ERROR')

	def urlNetLocCreator(self, providedURL):
		'''
		Function for taking strings provided by calling function, evaluating, & processing it so that netlocations(netloc) can be seperated more easily from paths(path).
		:param providedURL: Value to define the URL that will be evaluated & rebuilt so it more cleanly provides an absolute URL instead of a relative URL.
		:type providedURL: str
		:return: str
		'''

		try:
			checkedURL = f"{providedURL[0]}://{providedURL[1]}/" #Line that reconstructs the provided URL to include only the URL's scheme(parsedURL[0]) & it's netloc(parsedURL[1]) in form "{SCHEME}://{NETLOC}/"("{http}://{scanme.nmap.org}/" excluding curley braces)
			return checkedURL

		except Exception as e:
			self.logHandler(e, 'urlNetLocCreator', 'ERROR')

	def scrapeDynamicSite(self,inputURL):
		'''
		Function designed to gather & return URLs from websites that require activation before they can be properly scraped for URLs.
		:param inputURL: URL of the target to be scraped
		:type inputURL: str
		:return: list
		'''
		try:
			dynamicURLList = self.dynamicSiteGrabber(self.dynamicConnectorFirefox(),inputURL)
			return(dynamicURLList)
		except Exception as e:
			logHandlerReturn = self.logHandler(e, 'scrapeDynamicSite', 'ERROR')
			testResolve = self.testHandler(logHandlerReturn,e)

	def scrapeStaticSite(self,inputURL):
		'''
		Function designed to gather & return URLs from websites that are static & have HTML available to be scraped without dynamic code activitation.
		:param inputURL: URL of the target to be scraped
		:type inputURL: str
		:return: list
		'''
		try:
			targetResponse = self.siteConnector(inputURL)
			siteHTML = bs(targetResponse, 'html.parser')
			staticURLList = self.staticSiteScraper(siteHTML)
			return staticURLList
		except Exception as e:
			logHandlerReturn = self.logHandler(e, "scrapeStaticSite", 'ERROR')
			testResolve = self.testHandler(logHandlerReturn,e)

	def logHandler(self, loggingData, functionSource, logType = 'INFORMATION',writeLocation1 = "output",writeLocation2 = "logs"):
		'''
		Function for gathering error information & recording it to a central location.
		:param loggingData: Exception data provided by the calling function that is to be recorded.
		:type loggingData: str
		:param functionSource: Name of the function that is calling logHandler() so it can be recorded for logs.
		:type functionSource: str
		:param logType: Exception type provided by calling function to be recorded.
		:type logType: str
		:param writeLocation1: String indicating the parent folder of where logHandler() should write its log results
		:type writeLocation1: str
		:param writeLocation2: String indicating the child folder of where logHandler() should write its log results
		:type writeLocation2: str
		'''
		testMode = self.testMode
		if testMode == False:
			loggingData = str(loggingData)
			functionSource = str(functionSource)
			logType = str(logType)
			logTime = datetime.datetime.now()
			logTime = str(logTime)
			fileName = logType + '_LOGS' + '.txt'
			logFilePath = self.pathCombiner(writeLocation1,writeLocation2)
			fullFilePath = self.pathCombiner(logFilePath,fileName)
			openMode = 'a+'
			with open(fullFilePath, openMode) as writeFile:
				writeFile.write(logTime +  " (" + functionSource + "): " + loggingData + ";\n")
			if logType != "TESTING":
				print(f"During runtime {functionSource} met a logable event type {logType}")
				print(f"Log written to: {fullFilePath}")
			return None
		else:
			return loggingData

	def fileNamer(self):
		'''
		Feature for generating a unique filename for each of the pubCrawl outputs.
		:return: str in format ADJECTIVENOUN####
		'''
		adjectiveFilePath = self.pathCombiner("fileNames","adjectives.txt")
		adjectiveDataPool = self.fileReader(adjectiveFilePath)
		adjectives = self.dataPoolSorter(adjectiveDataPool)
		nounFilePath = self.pathCombiner("fileNames","nouns.txt")
		nounDataPool = self.fileReader(nounFilePath)
		nouns = self.dataPoolSorter(nounDataPool)

		try:
			adjectiveKey = random.choice(list(adjectives))
			adjectivePool = adjectives[adjectiveKey]
			adjectiveSelection = random.choice(list(adjectivePool)).capitalize()
			nounKey = random.choice(list(nouns))
			nounPool = nouns[nounKey]
			nounSelection = random.choice(list(nounPool)).capitalize()
			randomDigits = ''.join(random.choice(string.digits) for _ in range(4))
			fileName = adjectiveSelection + nounSelection + randomDigits
			return(fileName)
		except Exception as e:
			logHandlerReturn = self.logHandler(e, "fileNamer", 'ERROR')
			testResolve = self.testHandler(logHandlerReturn,e)

	def fileReader(self, fullFileName):
		'''
		Function for reading exterior files & returning their content to requestor.
		:param fullFileName: file location of data to be read & returned
		:type fullFileName: str
		:return: file data in str format
		'''
		try:
			with open(fullFileName) as file:
				data = file.read()
			return data
		except Exception as e:
			logHandlerReturn = self.logHandler(e, 'fileReader', 'ERROR')
			testResolve = self.testHandler(logHandlerReturn,e)

	def dataParser(self, targetSite, providedData, providedFileName, parentFolder = "output", fileType = ".txt"):
		'''
		Function for parsing the data that is found from the staticSiteScraper & saving it to a data file.
		:param targetSite: Value to indicate what site is being scraped for URLs.
		:type targetSite: str
		:param providedData: Data provided by calling function that will be written to the file
		:type providedData: str
		:param providedFileName: File name that is provided by calling function that the data will be written to.
		:type providedFileName: str
		:param parentFolder: Value that indicates what folder data should be written to.
		:type parentFolder: str
		:param fileType: the filetype of the file that the data will be written to.
		:type fileType: str
		'''
		mode = 'w'
		try:
			fileName = providedFileName + fileType
			fullFilePath = self.pathCombiner(parentFolder,fileName)
			with open(fullFilePath, mode) as fileWriter:
				fileWriter.write(f'URL Scraped: {targetSite}\n')
				for link in providedData:
					fileWriter.write(f'{link}\n')
		except Exception as e:
			logHandlerReturn = self.logHandler(e, 'dataParser', 'ERROR')
			testResolve = self.testHandler(logHandlerReturn,e)
			return(testResolve)

	def staticSiteScraper(self, providedHTML):
		'''
		Function for scraping the URL provided by the user for usable information that is stored in the HTML(via hrefs).
		:param providedHTML: HTML provided by the calling function that will be searched for available URLs
		:type providedHTML: str
		:return: modified list
		'''
		urlList = []
		try:
			for link in providedHTML.find_all(attrs={'href': re.compile("^https?://")}):
				#print("\'href\': " + link.get('href'))
				urlList.append("%s" % link.get('href'))
			return(urlList)
		except Exception as e:
			self.logHandler(e, 'staticSiteScraper', 'ERROR')

	def siteConnector(self, workingURL):
		'''
		Function for negotiating & connecting to user supplied URL.
		:param workingURL: URL provided by calling function that will be connected to by siteConnector()
		:type workingURL: str
		:return: str
		'''
		try:
			response = requests.get(workingURL)
			return(response.text)
		except Exception as e:
			logHandlerReturn = self.logHandler(e, "siteConnector", 'ERROR')
			testResolve = self.testHandler(logHandlerReturn,e)

	def dynamicConnectorFirefox(self):
		'''
		Function for creating a Selenium web browser driver in Firefox
		:return: selenium.webdriver.firefox.webdriver.WebDriver
		'''
		try:
			options = firefoxOptions()
			options.add_argument("--headless") #This line is to assure that a browser window does not open on the user's screen.  This line can be commented out if you want to open n number of browser windows where n is equal to the number of URLs being scraped.
			driver = webdriver.Firefox(options=options)
			return driver
		except Exception as e:
			logHandlerReturn = self.logHandler(e, 'dynamicConnectorFirefox', 'ERROR')
			testResolve = self.testHandler(logHandlerReturn,e)

	def dynamicSiteGrabber(self,providedDriver,providedURL,searchingElement1 = 'tag name',searchingElement2 = 'a',retrievingAttribute = 'href'):
		'''
		Function for running & grabbing HTML elements from a site after it has been summoned.  Function then searches for elements on the page to be returned.
		:param providedDriver: Driver that has been generated via selenium for calling on the website
		:type providedDriver: selenium.webdriver.firefox.webdriver.WebDriver
		:param providedURL: Provided URL that will be summoned & scraped for information
		:type providedURL: str
		:param searchingElement1: The type of element that is to be searched for in resulting HTML once the site has been summoned.
		:type searchingElement1: str
		:param searchingElement2: Subtype of element that is to be searched for in resulting HTML when the site has been summoned.
		:type searchingElement2: str
		:param retrievingAttribute: Type of element that will be scanned for & retrieved to be returned to the calling function
		:type retrievingAttribute: str
		:return: list
		'''
		try:
			foundURLs = []
			driver = providedDriver
			driver.get(providedURL)
			allLinks = driver.find_elements(searchingElement1,searchingElement2)
			for elem in allLinks:
				foundURLs.append(elem.get_attribute(retrievingAttribute))
			return foundURLs
		except Exception as e:
			logHandlerReturn = self.logHandler(e, 'dynamicSiteGrabber', 'ERROR')
			testResolve = self.testHandler(logHandlerReturn,e)

	def testHandler(self,logHandlerValue,exceptionValue):
		'''
		Function for assisting the class to determine what steps to complete based on the values it recieves
		:param logHandlerValue: Value that may be passed to the fuction that was supplied by logHandler() if the class is running in testMode
		:type logHandlerValue: str
		:param exceptionValue: Value that is supplied to the function & returned if it is determined that the logHandlerValue contains nothing(None)
		:type exceptionValue: TypeError
		:return: TypeError
		'''
		if logHandlerValue == None:
			sys.exit("Exiting")
		else:
			return exceptionValue

	def scrapeSite(self,targetType='HYBRIDSITE'):
		'''
		Function for activating & operating the pubCrawl class in the anticipated order & manner if the user is planning on performing both static & dynamic scraping.
		:param targetType: Value that indicates the type of site that is going to be scraped.  DYNAMICSITE indicates a site that should be scraped for only URLs that arrive after scripts are allowed to load.  STATICSITE indicates a site that should only be scraped for URLS before scripts are allowed to load.  HYBRIDSITE indicates a site that should be scraped for URLS both before & after scripts are allowed to load.
		:type targetType: str
		'''
		workingURL = self.inputURL
		targetType = targetType.upper()
		depthCounter = 0
		urlList = []
		while(depthCounter <= self.scanDepth):
			print(depthCounter)
			depthCounter += 1
		print(f"URL to be scraped: {workingURL}")
		print(f"Depth Set: {str(self.scanDepth)}")
		if(targetType == 'HYBRIDSITE'):
			staticURLList = self.scrapeStaticSite(workingURL)
			dynamicURLList = self.scrapeDynamicSite(workingURL)
			uniqueURLSet = set.union(set(staticURLList),set(dynamicURLList))
		elif(targetType == 'DYNAMICSITE'):
			uniqueURLList = self.scrapeDynamicSite(workingURL)
			uniqueURLSet = set(uniqueURLList)
		elif(targetType == 'STATICSITE'):
			uniqueURLList = self.scrapeStaticSite(workingURL)
			uniqueURLSet = set(uniqueURLList)
		else:
			sys.exit(f'{targetType} is not a valid targetType value. Try again with one of the following:\n\n\tHYBRIDSITE\n\tDYNAMICSITE\n\tSTATICSITE\n')

		for url in uniqueURLSet:
			urlList.append(url)

		fileName = self.fileNamer()
		self.dataParser(workingURL,uniqueURLSet, fileName)
		print(f"Results stored to {fileName}")
		print(urlList)

def main():
	'''
	Function for the primary orchestration & running of the code
	'''

	workingURL = userInputHandler(sys.argv)
	pub_crawl = pubCrawl(workingURL)
	pub_crawl.scrapeSite()

#*FUNCTION BLOCK END*

if __name__ == '__main__':
	main()


import numpy as np
import matplotlib.pyplot as plt

def get_points(mean, std=.15, npoints=10):
  x = np.random.normal(mean[0], std, npoints)
  y = np.random.normal(mean[1], std, npoints)
  return np.stack([x,y]).T

def att_mean(points, ref, sigma=.15):
  ref = np.array(ref)[None]
  dists = points - ref
  dists = np.sum(dists**2, axis=1)
  w = np.exp(-dists/sigma/2)
  w = w/np.sum(w)
  att_mean = points * w[:, None]
  return att_mean.sum(axis=0)

p0 = [-1, .5]
p1 = [.5, 0]
p2 = [0, .5]

a = [0, 1]
b = [1, 0]
c = [-1, 0]
points_cluster = [get_points(x) for x in [a,b,c]]
points = np.concatenate(points_cluster, axis=0)

mean = np.mean(points, axis=0)
att0 = att_mean(points, p0)
att1 = att_mean(points, p1)
att2 = att_mean(points, p2)

plt.plot(points_cluster[0][:, 0], points_cluster[0][:, 1], '.', markersize=20, color='purple')
plt.plot(points_cluster[1][:, 0], points_cluster[1][:, 1], '.', markersize=20, color='green')
plt.plot(points_cluster[2][:, 0], points_cluster[2][:, 1], '.', markersize=20, color='pink')

markers = ['h', 's', 's', 's', 'o', 'o', 'o']
colors = ['black', 'red', 'blue', 'green', 'red', 'blue', 'green']
for i, p in enumerate([mean, att0, att1, att2, p0, p1, p2]):
  plt.plot(p[0], p[1], markers[i], color=colors[i], markersize=20)

plt.gca().axes.get_yaxis().set_visible(False)
plt.gca().axes.get_xaxis().set_visible(False)
plt.show()
#plt.xlim(0, 1.8)


from django.http import HttpResponseRedirect, HttpResponse
from django.shortcuts import render
from .forms import DocumentForm
from .models import Document, ConstructionMaterial

import pandas as pd
from sqlalchemy import create_engine

def upload_csv(request):
    """
    Загрузка файла в модель Document.
    """
    if request.method == 'POST':
        form = DocumentForm(request.POST, request.FILES)
        if form.is_valid():
            form.save()
            return HttpResponseRedirect("/success/")
    else:
        form = DocumentForm()
    return render(request, 'main_app/upload.html', {'form': form})

def parsing_request(request):
    """
    Вывод шаблона с полями для ввода запроса на парсинг.
    """
    last_file = Document.objects.last()
    fields = [f.verbose_name for f in ConstructionMaterial._meta.get_fields()]
    return render(request, 'main_app/parser.html', {'document': last_file, 'fields': fields[1:]})

def parsing_result(request):
    """
    Получение запрашиваемой в парсинге информации, чтение последнего загруженного .csv файла через pandas.
    """
    searching_field = request.POST.getlist("searching_field")
    fields = [f.verbose_name for f in ConstructionMaterial._meta.get_fields()]
    fields = fields[1:]
    current_search = {fields[i]: searching_field[i] for i in range(len(fields)) if searching_field[i] != ''}

    def search_df(length, **kwargs):
        """
        Поиск побитового соответствия запрашиваемого поля в dataframe pandas.
        """
        latest_csv = Document.objects.latest('file')
        df = pd.read_csv(latest_csv.file.path, sep=';', engine='python', quoting=3, error_bad_lines=False)
        if length == 0:
            res = "Необходимо выбрать хотя бы одно поле для поиска"
        else:
            for key, value in kwargs.items():
                res = df[df[key].astype(str).str.contains(value, na=False)]  # Доработать множественный поиск
        return res

    global result
    result = search_df(len(current_search), **current_search)
    result_html = result.to_html()

    return render(request, 'main_app/parser_result.html', {'result_html': result_html, 'result': result})

def save_to_db(request):
    """
    Сохранение информации которую удалось спарсить в модель ConstructionMaterial.
    """
    if request.method == 'GET':
        # result = request.GET["result"]
        print(type(result))
        engine = create_engine('sqlite:///db.sqlite3')
        result.to_sql(ConstructionMaterial._meta.db_table, if_exists='append', con=engine, index=False)
        context = {"success": "Данная выборка успешно сохранена"}
    # return HttpResponse("Данная выборка успешно сохранена")
    return render(request, 'main_app/parser_result.html', context)


from sklearn import metrics
from sklearn.ensemble import RandomForestClassifier
from sklearn.cross_validation import train_test_split
from sklearn import datasets

#Load the iris flower data set
iris = datasets.load_iris()
X, y = iris.data, iris.target

#spit data
X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=0.7)

#Insert a Bayesian model into the data
classification_model = RandomForestClassifier()
classification_model.fit(X_train, y_train)
print(classification_model)

#Produce estimations
forecast = X_test
estimate = classification_model.predict(y_test)

#Summarize the fit of the model
print(metrics.classification_report(forecast, estimate))
print(metrics.confusion_matrix(forecast, estimate))


from fastapi import APIRouter
from fastapi import Depends
from app.routers.depends import common, jobs
from app.models.jobs import Jobs

router_jobs = APIRouter()

@router_jobs.post("/submit/{job_id}",
                  dependencies=[Depends(common.verify_key)],
                  name='提交作业')
async def submit_jobs(job_id: str = None, b_jobs: Jobs = Depends(jobs.post_body)):
    print(job_id)
    print(b_jobs)
    return {"Hello": "World, jobs"}

@router_jobs.post("/submit1",
                  dependencies=[Depends(common.verify_key), Depends(common.paging_parameters)],
                  name='提交作业1')
async def submit_jobs1(paging: dict = None):
    print(paging)
    return {"Hello": "World, jobs"}

@router_jobs.post("/submit2", name='提交作业2')
async def submit_jobs2(dep_query_file: str = Depends(jobs.query_or_cookie_extractor)):
    print('ad_dep =', dep_query_file)
    return {"Hello": "World, jobs"}

@router_jobs.post("/submi3",
                  dependencies=[Depends(common.verify_key), Depends(jobs.checker)],
                  name='提交作业3')
async def submit_jobs3(paging: dict = None):
    print(paging)
    return {"Hello": "World, jobs"}


import copy
import math
from typing import List

def get_positive_odd_integer() -> None:
    """
    Get an odd, positive integer from standard input
    Upon invalid user input (negative, 1 or even numbers), re-prompt
    """

    def is_valid_integer(integer):
        return integer % 2 != 0 and integer >= 3

    first_input = int(input("Enter an odd, positive integer larger than 1: "))

    if is_valid_integer(first_input):
        return first_input
    else:
        print("Incorrect. Please adhere to the required format.")
        return get_positive_odd_integer()

def create_matrix(magic_order: int) -> List[List[int]]:
    outer_list = [[0 for _ in range(magic_order)] for _ in range(magic_order)]
    return outer_list

def pretty_print_matrix(matrix: List[List[int]]) -> None:
    for row in range(len(matrix)):
        for col in range(len(matrix)):
            print(str(matrix[row][col]) + "\t", end="")
        print()

def create_magic_square(matrix: List[List[int]]) -> List[List[int]]:
    """
    Takes in an n by n matrix
    """
    matrix_copy = copy.deepcopy(matrix)
    row_length = len(matrix)

    current_index = 1
    row, col = 0, math.floor(len(matrix) / 2)

    while current_index <= math.pow(current_index, 2):
        matrix_copy[row][col] = current_index
        current_index += 1

        wrap_row, wrap_col = (row - 1) % row_length, (col + 1) % row_length

        if matrix_copy[wrap_row][wrap_col]:
            row += 1
            if row >= row_length:
                break
        else:
            row, col = wrap_row, wrap_col

    return matrix_copy

def main() -> None:
    magic_order = get_positive_odd_integer()
    empty_matrix = create_matrix(magic_order)

    magic_square = create_magic_square(empty_matrix)

    print(f"The magic square of {magic_order} is: ")
    pretty_print_matrix(magic_square)

if __name__ == "__main__":
    main()


from typing import List

class Solution:
    def movesToStamp(self, stamp: str, target: str) -> List[int]:

        tarr = [a for a in target]
        star = 0
        visited = [False]*len(target)
        res = []

        def canReplace(str, p, stamp):
            for i in range(len(stamp)):
                if str[p + i] != '*' and str[p + i] != stamp[i]:
                    return False
            return True

        def doReplace(tarr, p, len):
            c = 0
            for i in range(len):
                if tarr[p+i] != '*':
                    tarr[p+i] = '*'
                    c += 1
            return c

        while star < len(target):
            replaced = False

            for i in range(len(target) - len(stamp) + 1):
                if visited[i] == False and canReplace(tarr, i, stamp):
                    star += doReplace(tarr, i, len(stamp))
                    replaced = True
                    visited[i] = True
                    res.append(i)

                if star == len(target):
                    break

            if replaced == False:
                return []

        res = res[::-1]

        return res

stamp = "abca"
target = "aabcaca"
s = Solution()
print(s.movesToStamp(stamp, target))


from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.keys import Keys
import time

options = webdriver.ChromeOptions()
options.add_argument("--headless")  # Run the browser in headless mode (no UI)

def scrpe(data):
    driver = webdriver.Chrome(options=options)
    driver.get('https://www.google.com/')
    driver.maximize_window()

    search_box = driver.find_element(by='xpath', value='//textarea[@class="gLFyf"]')
    search_box.send_keys(data)
    search_box.send_keys(Keys.ENTER)
    time.sleep(7)

    scroll_height = 1000
    num_scrolls = 2

    for _ in range(num_scrolls):
        driver.execute_script(f'window.scrollBy(0, {scroll_height});')

    desc_elements = driver.find_elements(by='xpath', value='//div[@class="VwiC3b yXK7lf MUxGbd yDYNvb lyLwlc lEBKkf"]//span')
    descriptions = [des_element.text for des_element in desc_elements]

    time.sleep(5)

    link_elements = driver.find_elements(by='xpath', value='//div[@class="Z26q7c UK95Uc jGGQ5e"]//a')
    links = [link_element.get_attribute('href') for link_element in link_elements]

    data_list = []

    for des, link in zip(descriptions, links):
        data_dict = {
            'description': des,
            'link': link
        }
        data_list.append(data_dict)

    driver.quit()
    return data_list


"""Views for cart"""
from django.shortcuts import render, redirect, reverse

def view_cart(request):
    """Render all cart contents"""
    return render(request, 'cart.html')

def add_to_cart(request, id):
    """Add new feature with amount to pay to cart"""
    amount = int(request.POST.get('amount'))
    cart = request.session.get('cart', {})
    cart[id] = cart.get(id, amount)
    request.session['cart'] = cart
    return redirect(reverse('view_cart'))

def adjust_amount(request, id):
    """Adjust amount to pay"""
    amount = int(request.POST.get('amount'))
    cart = request.session.get('cart', {})
    cart[id] = cart.get(id, amount)
    cart[id] = amount
    request.session['cart'] = cart
    return redirect(reverse('view_cart'))


# Customer Segmentation with RFM Analysis

# TASK 1. Data Understanding

# Import necessarily libraries
import pandas as pd
import datetime as dt

# Set pandas options to display all columns and rows, float format, and comment out display width
pd.set_option('display.max_columns', None)
pd.set_option('display.max_rows', None)
pd.set_option('display.float_format', lambda x: '%.2f' % x)

# 1. Read the flo_data_20K.csv file and create a copy of the dataframe
df_ = pd.read_csv(r"CRM_Analytics\flo_data_20k.csv")
df = df_.copy()
df.head()

# 2. Analyze the data set including the first 10 observations, variable names, dimensions, descriptive statistics,
# missing values, and variable types
df.head(10)
df.columns
df.shape
df.describe().T
df.isnull().sum()
df.info()

# 3. Create new variables to show the total number of orders and the total customer value for each customer
# who shops from both online and offline platforms
df["order_num_total"] = df["order_num_total_ever_online"] + df["order_num_total_ever_offline"]
df["customer_value_total"] = df["customer_value_total_ever_offline"] + df["customer_value_total_ever_online"]

# 4. Analyze variable types and convert date columns to datetime
date_columns = df.columns[df.columns.str.contains("date")]
df[date_columns] = df[date_columns].apply(pd.to_datetime)
df.info()

# 5. Analyze the distribution of the number of customers, total number of items purchased, and total customer
# value in each order channel
df.groupby("order_channel").agg({"master_id":"count",
                                 "order_num_total":"sum",
                                 "customer_value_total":"sum"})

# 6. Sort and display the top 10 customers who bring the highest customer value
df.sort_values("customer_value_total", ascending=False)[:10]

# 7. Sort and display the top 10 customers who have the highest number of orders
df.sort_values("order_num_total", ascending=False)[:10]

# 8. Function to prepare the data
def data_prep(dataframe):
    # Create new variables to show the total number of orders and the total customer value for each customer
    # who shops from both online and offline platforms
    dataframe["order_num_total"] = dataframe["order_num_total_ever_online"] + dataframe["order_num_total_ever_offline"]
    dataframe["customer_value_total"] = dataframe["customer_value_total_ever_offline"] + dataframe["customer_value_total_ever_online"]
    # Convert date columns to datetime
    date_columns = dataframe.columns[dataframe.columns.str.contains("date")]
    dataframe[date_columns] = dataframe[date_columns].apply(pd.to_datetime)
    return df

# TASK 2: Calculate RFM Metrics

# Define the analysis date as 2 days after the date of the latest order in the dataset
latest_order_date = df["last_order_date"].max()
analysis_date = latest_order_date + dt.timedelta(days=2) # add 2 days to the latest order date

# Create a new dataframe to store customer_id, recency, frequency, and monetary values for RFM analysis
rfm = pd.DataFrame()
rfm["customer_id"] = df["master_id"] # Add customer IDs from original dataframe
rfm["recency"] = (analysis_date - df["last_order_date"]).astype('timedelta64[D]') # Calculate recency as the number of days between the analysis date and the date of the customer's last order
rfm["frequency"] = df["order_num_total"] # Add total number of orders for each customer from the original dataframe
rfm["monetary"] = df["customer_value_total"] # Add total monetary value of orders for each customer from the original dataframe

# Print the first five rows of the RFM dataframe to check that it has been created correctly
print(rfm.head())

# TASK 3: Calculate RF and RFM Scores

# Convert the recency, frequency, and monetary metrics into scores from 1 to 5 using qcut, and save them as separate columns in the rfm dataframe
rfm["recency_score"] = pd.qcut(rfm['recency'], 5, labels=[5, 4, 3, 2, 1]) # Divide recency values into 5 equal-sized bins and assign labels of 5 (highest) to 1 (lowest) to each bin
rfm["frequency_score"] = pd.qcut(rfm['frequency'].rank(method="first"), 5, labels=[1, 2, 3, 4, 5]) # Divide frequency values into 5 equal-sized bins based on their rank, and assign labels of 1 (lowest) to 5 (highest) to each bin
rfm["monetary_score"] = pd.qcut(rfm['monetary'], 5, labels=[1, 2, 3, 4, 5]) # Divide monetary values into 5 equal-sized bins and assign labels of 1 (lowest) to 5 (highest) to each bin

# Combine the recency_score and frequency_score columns into a single column called RF_SCORE
rfm["RF_SCORE"] = (rfm['recency_score'].astype(str) + rfm['frequency_score'].astype(str)) # Concatenate the recency_score and frequency_score columns into a single column

# Combine the recency_score, frequency_score, and monetary_score columns into a single column called RFM_SCORE
rfm["RFM_SCORE"] = (rfm['recency_score'].astype(str) + rfm['frequency_score'].astype(str) + rfm['monetary_score'].astype(str)) # Concatenate all three score columns into a single column

# TASK 4: Define RF Scores as Segments
# Define segments for more meaningful interpretation of RFM scores and convert RF_SCORE to segments using seg_map
seg_map = {
    r'[1-2][1-2]': 'hibernating', # Customers who have low recency, frequency, and monetary value
    r'[1-2][3-4]': 'at_Risk', # Customers who have low recency but higher frequency and monetary value
    r'[1-2]5': 'cant_loose', # Customers who have high monetary value but low recency and frequency
    r'3[1-2]': 'about_to_sleep', # Customers who have low recency and frequency but higher monetary value
    r'33': 'need_attention', # Customers who have low recency, frequency, and monetary value
    r'[3-4][4-5]': 'loyal_customers', # Customers who have high recency and frequency but lower monetary value
    r'41': 'promising', # Customers who have high recency, low frequency, and monetary value
    r'51': 'new_customers', # Customers who have high frequency but low recency and monetary value
    r'[4-5][2-3]': 'potential_loyalists', # Customers who have high recency and monetary value but lower frequency
    r'5[4-5]': 'champions' # Customers who have high recency, frequency, and monetary value
}

# Replace RF_SCORE with corresponding segment using seg_map and add a new 'segment' column to the rfm DataFrame
rfm['segment'] = rfm['RF_SCORE'].replace(seg_map, regex=True)

# TASK 5. Action Time!
# 1. Analyze the recency, frequency, and monetary averages of the segments.
# Group by the "segment", and aggregate the means and counts of the "recency", "frequency", and "monetary" columns.
rfm[["segment", "recency", "frequency", "monetary"]].groupby("segment").agg(["mean", "count"])

# 2. Using RFM analysis, find the customers in the relevant profile for 2 cases and save their customer IDs to CSV files.

# a. FLO is introducing a new women's shoe brand. The prices of the brand's products are above the general customer preferences. Therefore, it is desired to communicate
# with the customers in the relevant profile who will be interested in the promotion and sales of the brand. These customers are planned to be loyal and
# those who shop from the women's category. Save the customer IDs to a CSV file named "new_brand_target_customer_ids.csv".
target_segments_customer_ids = rfm[rfm["segment"].isin(["champions","loyal_customers"])]["customer_id"]
cust_ids = df[(df["master_id"].isin(target_segments_customer_ids)) &(df["interested_in_categories_12"].str.contains("KADIN"))]["master_id"]
cust_ids.to_csv("new_brand_target_customer_ids.csv", index=False)
print(cust_ids.shape)

# b. A discount of nearly 40% is planned for men's and children's products. Previously good customers who are interested in the discounted categories but have not shopped for a long time, and new customers are wanted to be targeted specifically. Save the customer IDs to a CSV file named "discount_target_customer_ids.csv".
target_segments_customer_ids = rfm[rfm["segment"].isin(["cant_loose","hibernating","new_customers"])]["customer_id"]
cust_ids = df[(df["master_id"].isin(target_segments_customer_ids)) & ((df["interested_in_categories_12"].str.contains("ERKEK"))|(df["interested_in_categories_12"].str.contains("COCUK")))]["master_id"]
cust_ids.to_csv("discount_target_customer_ids.csv", index=False)
print(cust_ids.shape)

# BONUS. Functionalize all steps

def create_rfm(dataframe):
    # Data Preparation
    # Create a new column for the total number of orders
    dataframe["order_num_total"] = dataframe["order_num_total_ever_online"] + dataframe["order_num_total_ever_offline"]
    # Create a new column for the total customer value
    dataframe["customer_value_total"] = dataframe["customer_value_total_ever_offline"] + dataframe["customer_value_total_ever_online"]
    # Get the columns that contain dates and convert them to datetime format
    date_columns = dataframe.columns[dataframe.columns.str.contains("date")]
    dataframe[date_columns] = dataframe[date_columns].apply(pd.to_datetime)

    # Calculate RFM Metrics
    # Determine the latest order date and set the analysis date to the next day
    latest_order_date = dataframe["last_order_date"].max()
    analysis_date = latest_order_date + dt.timedelta(days=1)
    # Create a new empty dataframe to store RFM values
    rfm = pd.DataFrame()
    # Add customer ID, recency, frequency, and monetary columns to the RFM dataframe
    rfm["customer_id"] = dataframe["master_id"]
    rfm["recency"] = (analysis_date - dataframe["last_order_date"]).astype('timedelta64[D]')
    rfm["frequency"] = dataframe["order_num_total"]
    rfm["monetary"] = dataframe["customer_value_total"]

    # Calculate RF and RFM Scores
    # Divide recency, frequency, and monetary into 5 equal-sized groups and assign scores
    rfm["recency_score"] = pd.qcut(rfm['recency'], 5, labels=[5, 4, 3, 2, 1])
    rfm["frequency_score"] = pd.qcut(rfm['frequency'].rank(method="first"), 5, labels=[1, 2, 3, 4, 5])
    rfm["monetary_score"] = pd.qcut(rfm['monetary'], 5, labels=[1, 2, 3, 4, 5])
    # Combine recency and frequency scores to create RF Score
    rfm["RF_SCORE"] = (rfm['recency_score'].astype(str) + rfm['frequency_score'].astype(str))
    # Combine RF Score and monetary score to create RFM Score
    rfm["RFM_SCORE"] = (rfm['recency_score'].astype(str) + rfm['frequency_score'].astype(str) + rfm['monetary_score'].astype(str))

    # Map RF Scores to segment names
    seg_map = {
        r'[1-2][1-2]': 'hibernating',
        r'[1-2][3-4]': 'at_Risk',
        r'[1-2]5': 'cant_loose',
        r'3[1-2]': 'about_to_sleep',
        r'33': 'need_attention',
        r'[3-4][4-5]': 'loyal_customers',
        r'41': 'promising',
        r'51': 'new_customers',
        r'[4-5][2-3]': 'potential_loyalists',
        r'5[4-5]': 'champions'
    }
    # Map RF Scores to segment names and add the segment column to the RFM dataframe
    rfm['segment'] = rfm['RF_SCORE'].replace(seg_map, regex=True)

    # Return the RFM dataframe
    return rfm[["customer_id", "recency","frequency","monetary","RF_SCORE","RFM_SCORE","segment"]]

rfm_df = create_rfm(df)


import csv
from collections import Counter

with open("HeightWeight.csv") as f:
    reader = csv.reader(f)
    file_data = list(reader)

file_data.pop(0)

newData = []

for i in range(len(file_data)):
    number = file_data[i][1]
    newData.append(float(number))

def mean():
    n = len(newData)
    total = 0

    for x in newData:
        total = total + x

    mean = total/n
    print("The mean is " + str(mean))

def median():
    newData.sort()

    n = len(newData)

    if n%2 == 0:
        median1 = float(newData[n//2])
        median2 = float(newData[n//2 -1])

        median = (median1 + median2) / 2
    else:
        median = newData[n//2]

    print("The median is" + str(median))

def mode():
    data = Counter(newData)

    mode_data_range = {
        "75 - 85": 0,
        "85 - 95": 0,
        "95 - 105": 0,
        "105 - 115": 0,
        "115 - 125": 0,
        "125 - 135": 0,
        "135 - 145": 0,
        "145 - 155": 0,
        "155 - 165": 0,
        "165 - 175": 0,
    }

    for height,occurence in data.items():
        if 75 < float(height) < 85:
            mode_data_range["75 - 85"] += occurence
        elif 85 < float(height) < 95:
            mode_data_range["85 - 95"] += occurence
        elif 95 < float(height) < 105:
            mode_data_range["95 - 105"] += occurence
        elif 105 < float(height) < 115:
            mode_data_range["105 - 115"] += occurence
        elif 115 < float(height) < 125:
            mode_data_range["115 - 125"] += occurence
        elif 125 < float(height) < 135:
            mode_data_range["125 - 135"] += occurence
        elif 135 < float(height) < 145:
            mode_data_range["135 - 145"] += occurence
        elif 145 < float(height) < 155:
            mode_data_range["145 - 155"] += occurence
        elif 155 < float(height) < 165:
            mode_data_range["155 - 165"] += occurence
        elif 165 < float(height) < 175:
            mode_data_range["165 - 175"] += occurence

    mode_range = 0
    mode_occurence = 0

    for range, occurence in mode_data_range.items():
        if occurence > mode_occurence:
            mode_range, mode_occurence = [int(range.split("-")[0]), int(range.split("-")[1])], occurence
    mode = float((mode_range[0] + mode_range[1])/2)
    print(f"mode is {mode: 2f}")

def main():
    mean()
    median()
    mode()

main()


import json
import operator
import pickle

def most_common():
    f = open('shapecolour.p', 'rb')

    unpickle = pickle.load(f)
    counter = {}
    for pair in unpickle:
        key = pair['colour'] + ' ' + pair['shape']
        counter[key] = counter.get(key, 0) + 1

    m_common = max(counter.keys(), key=(lambda x:counter[x]))
    m_common = m_common.split(' ')
    return {
        "colour": m_common[0],
        "shape": m_common[1]
    }

def process():
    m_common = most_common()
    result = {}
    result["mostCommon"] = m_common
    result["rawData"] = [pickle.load(open('shapecolour.p', 'rb'))]

    f = open('processed.json', 'w')
    json.dump(result, f)

process()


import os
import boto3
from boto3.dynamodb.conditions import Key
import uuid
import json
import time
import arrow,datetime
import random
import urllib.parse

#ここでデータベースを作る必要がなくなったのでデータは無い

#DynamoDBへの接続を取得する。
def _get_database():
    endpoint = os.environ.get("DB_ENDPOINT")
    if endpoint:
        return boto3.resource("dynamodb",endpoint_url=endpoint)
        print("endpointあります！！")
    else:
        return boto3.resource("dynamodb")

def _get_ramdom_number(size,done_list):
    output_number = 0
    limit_sign = 0
    for num in range(size):
        if len(done_list) == size:
            print("すべて成し遂げました！")
            break
        random_number =random.randint(1,size)
        if not str(random_number in done_list):
            output_number = random_number
            print(done_list)
            break
    return output_number

def get_all_tables():
    table = _get_database().Table(os.environ['DB_TABLE_NAME'])
    response = table.scan()
    print(response['Items'])
    return response['Items']

def get_all_tablename():
    table = _get_database().Table(os.environ['DB_TABLE_NAME'])
    response = table.scan()
    response_item = response['Items']
    output_list = []
    for part in response_item:
        name_tid_list={"table_name":part['table_name'],"tid":part['tid']}
        print(name_tid_list,type(name_tid_list))
        output_list.append(name_tid_list)
    output_list.append({"table_length":len(response_item)})

    return output_list

def _get_all_tid():
    table = _get_database().Table(os.environ['DB_TABLE_NAME'])
    response = table.scan()
    response_item = response['Items']
    output_list = []
    for part in response_item:
        output_list.append(int(part['tid']))

    return output_list

def get_table_detail(name_or_tid):
    #at first, this will search the table by name
    table = _get_database().Table(os.environ['DB_TABLE_NAME'])
    fe=Key("table_name").eq(name_or_tid);
    response = table.scan(
            FilterExpression=fe
            )
    if response['Items'] == []:
        fe2 = Key("tid").eq(name_or_tid);
        response = table.scan(
                FilterExpression = fe2
                )
        return response['Items']
    else:
        return response['Items']

def create_table(name,length,graphrow,setting):
    if "tags" in setting.keys():
        tags = setting['tags']
    else:
        tags = []
    table = _get_database().Table(os.environ['DB_TABLE_NAME'])
    put_table={}
    try:
        name = urllib.parse.unquote(name)
    except:
        print("デコードできませんでした")
    tid_list = _get_all_tid()
    print("今存在するtidは以下です\n",tid_list)
    x = 1
    num = 1
    while(x == 1):
        if not num in tid_list:
            tid = num
            break
        else:
            num += 1
    print("今回使うtidは以下です\n",num)
    put_table['tid'] = str(tid)
    put_table['len'] = int(length)
    put_table['done'] = []
    put_table['alldone_times'] = 0
    put_table['table_name'] = name
    put_table['detail'] = []
    put_table['graphrow'] = graphrow
    put_table['row_desplay'] = []
    put_table['tags'] = tags
    table.put_item(Item=put_table)
    return put_table

#todoを消す
def delete_table(tid):
    table = _get_database().Table(os.environ['DB_TABLE_NAME'])

    #データを削除する
    result = table.delete_item(
            Key={
                'tid':tid,
                },
            ReturnValues='ALL_OLD'
            )
    return result['Attributes']

def recoad_progress_to_table(name,tid):
    table = _get_database().Table(os.environ['DB_TABLE_NAME'])
    fe = Key("tid").eq(tid);
    response = table.scan(
            FilterExpression = fe
            )
    response_item = response['Items']
    exist_check = 0
    for table_list in response_item:
        if table_list['tid'] == str(tid):
            response_item = table_list
            exist_check += 1
    if exist_check == 0:
        print("選択したtidが存在しません")
        return "tidalart"
    if exist_check >1 :
        print("おかしい計算がされています")
    #入力するものが数字か特定の文字かを判断します。
    print(type(response_item['len']))
    try:
        ident = int(response_item['len'])
        if 0 < int(name) and int(name) <= response_item['len']:
            pass
        else:
            print("指定外の数字です")
            return("error")
    except:
        print("入力するものが文字です")
        #まだ未実装#
    #これはリスト型
    response_detail = response_item['detail']
    detail_times_list = []
    for detail_part in response_detail:
        if name == detail_part['name']:
            detail_times_list.append(detail_part['times'])
    print(detail_times_list)
    #print(max(detail_times_list))
    append_detail_dic = {}
    #detail部分
    done_times = 0
    if detail_times_list == []:
        print("今回が初めてです")
        append_detail_dic['date'] = str(arrow.now().to("Asia/Tokyo"))
        append_detail_dic['name'] = name
        append_detail_dic['times'] = 1
        done_times = 0
        response_item["detail"].append(append_detail_dic)
    #    print(response_detail)
    else:
        maxtimes = 1 + max(detail_times_list)
        append_detail_dic['date'] = str(arrow.now().to("Asia/Tokyo"))
        append_detail_dic['name'] = name
        append_detail_dic['times'] = maxtimes
        done_times = maxtimes - 1
        response_item["detail"].append(append_detail_dic)
    #    print(response_detail)
    #ここからdone部分
    if response_item['alldone_times'] == done_times:
        response_item['done'].append(name)
    if len(response_item['done']) == response_item['len']:
        response_item['alldone_times'] += 1
        done_append_list=[]
        for part in response_item['detail']:
            if part['times'] == response_item['alldone_times'] + 1:
                done_append_list.append(str(part['name']))
        response_item['done'] = done_append_list
    #print(response_item)
    table.put_item(Item=response_item)
    return response_item

def remove_progress_to_table(name,tid):
    table = _get_database().Table(os.environ['DB_TABLE_NAME'])
    fe = Key("tid").eq(tid);
    response = table.scan(
            FilterExpression = fe
            )
    response_item = response['Items']
    exist_check = 0
    for table_list in response_item:
        if table_list['tid'] == str(tid):
            response_item = table_list
            exist_check += 1
    if exist_check == 0:
        print("選択したtidが存在しません")
        return "tidalart"
    if exist_check >1 :
        print("おかしい計算がされています")
    #入力するものが数字か特定の文字かを判断します。
    print(type(response_item['len']))
    try:
        ident = int(response_item['len'])
        if 0 < int(name) and int(name) <= response_item['len']:
            pass
        else:
            print("指定外の数字です")
            return("error")
    except:
        print("入力するものが文字です")
        #まだ未実装#
    #これはリスト型
    response_detail = response_item['detail']
    detail_times_list = []
    for detail_part in response_detail:
        if name == detail_part['name']:
            detail_times_list.append(detail_part['times'])
    print(detail_times_list)
    #print(max(detail_times_list))
    append_detail_dic = {}
    #detail部分
    done_times = 0
    if detail_times_list == []:
        print("今回が初めてですなので削除するものがありません")
        return('error')
    else:
        for part in response_item['detail']:
            #print(part)
            print(max(detail_times_list),part['times'],name,part['name'])
            if max(detail_times_list) == part['times'] and name == part['name']:
                print(part)
                remove_done = part['times']
                remove_done_name = part['name']
                response_item["detail"].remove(part)
        if int(remove_done) == response_item["alldone_times"] + 1:
            print(remove_done,response_item['done'])
            response_item['done'].remove(str(remove_done_name))
        elif int(remove_done) == response_item["alldone_times"]:
            done_list = []
            response_item['alldone_times'] -= 1
            try:
                ident = int(response_item['len'])
            except:
                print("入力するものが文字です")
            for num in range(ident):
                if not str(num+1) == remove_done_name:
                    done_list.append(str(num+1))
            response_item['done'] = done_list

    #print(response_item)
    table.put_item(Item=response_item)
    return response_item

def get_random_parameter(tid):
    detail = get_table_detail(tid)
    #print(detail[0])
    #print(detail[0]['len'])
    #print(detail[0]['done'])
    return _get_ramdom_number(int(detail[0]['len']),detail[0]['done'])

def change_setting_table(tid,data):
    print(data['name'])
    if not data['name'] == "":
        updatepass_name = 1
    else:
        updatepass_name = 0

    table = _get_database().Table(os.environ['DB_TABLE_NAME'])
    if updatepass_name == 1:
        responce = table.update_item(
            ExpressionAttributeValues={
                ':n':data['name'],
                ':r':{
                    'L':data['rowdesplay'],
                    },
                ':t':data['tags'],
                },
            Key ={
                'tid':str(tid)
                },
            ReturnValues="ALL_NEW",
            TableName=os.environ['DB_TABLE_NAME'],
            UpdateExpression="SET table_name=:n,row_desplay=:r,tags=:t",
            )
    else:
        response = table.update_item(
            ExpressionAttributeValues={
                ':r':{
                    'L':data['rowdesplay'],
                    },
                ':t':data['tags'],
                },
            Key ={
                'tid':str(tid)
                },
            ReturnValues="ALL_NEW",
            TableName=os.environ['DB_TABLE_NAME'],
            UpdateExpression="SET row_desplay=:r,tags=:t",
            )
    return(response['Attributes'])

if __name__ == "__main__":

    data = [1,4,7]
    print(_get_ramdom_number(10,data))


"""Font handler by Joseph Lansdowne.

The Fonts class in this module can serve as a font cache, but the real point of
this is to render multi-line text with alignment and shadow and stuff.

Release: 5.

Licensed under the GNU General Public License, version 3; if this was not
included, you can find it here:
    http://www.gnu.org/licenses/gpl-3.0.txt

"""

from os.path import isfile, abspath, sep as path_sep, join as join_path

import pygame

class Fonts (dict):
    """Collection of pygame.font.Font instances.

    CONSTRUCTOR

Fonts(*font_dirs)

font_dirs: directories to find fonts - so you can just pass the font's filename
           when adding a font.

Use the dict interface to register fonts:

    fonts['some name'] = (filename, size[, bold = False])

where the arguments are as taken by pygame.font.Font.  All directories in
font_dirs are searched for the filename, unless it contains a path separator.
If so, or if the search yields no results, it is used as the whole path
(absolute or relative).

Retrieving the font again yields a pygame.font.Font instance.  Assigning two
different names to the same set of arguments makes the same instance available
under both without loading the file twice.

    METHODS

render

    ATTRIBUTES

font_dirs: as given.  You may alter this list directly.

"""

    def __init__ (self, *font_dirs):
        self.font_dirs = list(font_dirs)
        self._fonts_by_args = {}

    def __setitem__ (self, name, data):
        # standardise data so we can be sure whether we already have it
        if len(data) == 2:
            fn, size = data
            bold = False
        else:
            fn, size, bold = data
        size = int(size)
        bold = bool(bold)
        # find font file
        orig_fn, fn = fn, None
        temp_fn = None
        if path_sep not in orig_fn:
            # search registered dirs
            for d in self.font_dirs:
                temp_fn = join_path(d, orig_fn)
                if isfile(temp_fn):
                    fn = temp_fn
                    break
        if fn is None:
            # wasn't in any registered dirs
            fn = orig_fn
        fn = abspath(fn)
        # load this font if we haven't already
        data = (fn, size, bold)
        font = self._fonts_by_args.get(data, None)
        if font is None:
            font = pygame.font.Font(fn, size, bold = bold)
            self._fonts_by_args[data] = font
            # store
            dict.__setitem__(self, name, font)

    def render (self, font, text, colour, shadow = None, width = None,
                just = 0, minimise = False, line_spacing = 0, aa = True,
                bg = None, pad = (0, 0, 0, 0)):
        """Render text from a font.

render(font, text, colour[, shadow][, width], just = 0, minimise = False,
       line_spacing = 0, aa = True[, bg], pad = (0, 0, 0, 0))
    -> (surface, num_lines)

font: name of a registered font.
text: text to render.
colour: (R, G, B) tuple.
shadow: to draw a drop-shadow: (colour, offset) tuple, where offset is (x, y).
width: maximum width of returned surface (wrap text).  ValueError is raised if
       any words are too long to fit in this width.
just: if the text has multiple lines, justify: 0 = left, 1 = centre, 2 = right.
minimise: if width is set, treat it as a minimum instead of absolute width
          (that is, shrink the surface after, if possible).
line_spacing: space between lines, in pixels.
aa: whether to anti-alias the text.
bg: background colour; defaults to alpha.
pad: (left, top, right, bottom) padding in pixels.  Can also be one number for
     all sides or (left_and_right, top_and_bottom).  This treats shadow as part
     of the text.

surface: pygame.Surface containing the rendered text.
num_lines: final number of lines of text.

Newline characters split the text into lines (along with anything else caught
by str.splitlines), as does the width restriction.

"""
        font = self[font]
        lines = []
        if shadow is None:
            offset = (0, 0)
        else:
            shadow_colour, offset = shadow
        if isinstance(pad, int):
            pad = (pad, pad, pad, pad)
        elif len(pad) == 2:
            pad = tuple(pad)
            pad = pad + pad
        else:
            pad = tuple(pad)
        if width is not None:
            width -= pad[0] + pad[2]

        # split into lines
        text = text.splitlines()
        if width is None:
            width = max(font.size(line)[0] for line in text)
            lines = text
            minimise = True
        else:
            for line in text:
                if font.size(line)[0] > width:
                    # wrap
                    words = line.split(' ')
                    # check if any words won't fit
                    for word in words:
                        if font.size(word)[0] >= width:
                            e = '\'{0}\' doesn\'t fit on one line'.format(word)
                            raise ValueError(e)
                    # build line
                    build = ''
                    for word in words:
                        temp = build + ' ' if build else build
                        temp += word
                        if font.size(temp)[0] < width:
                            build = temp
                        else:
                            lines.append(build)
                            build = word
                    lines.append(build)
                else:
                    lines.append(line)
        if minimise:
            width = max(font.size(line)[0] for line in lines)

        # simple case: just one line and no shadow or padding and bg is opaque
        # or fully transparent bg and want to minimise
        if len(lines) == 1 and pad == (0, 0, 0, 0) and shadow is None \
           and (bg is None or len(bg) == 3 or bg[3] in (0, 255)) and minimise:
            if bg is None:
                sfc = font.render(lines[0], True, colour)
            else:
                sfc = font.render(lines[0], True, colour, bg)
            return (sfc, 1)
        # else create surface to blit all the lines to
        size = font.get_height()
        h = (line_spacing + size) * (len(lines) - 1) + font.size(lines[-1])[1]
        sfc = pygame.Surface((width + abs(offset[0]) + pad[0] + pad[2],
                             h + abs(offset[1]) + pad[1] + pad[3]))
        # to get transparency, need to be blitting to a converted surface
        sfc = sfc.convert_alpha()
        sfc.fill((0, 0, 0, 0) if bg is None else bg)
        # render and blit text
        todo = []
        if shadow is not None:
            todo.append((shadow_colour, 1))
        todo.append((colour, -1))
        n_lines = 0
        for colour, mul in todo:
            o = (max(mul * offset[0] + pad[0], 0),
                 max(mul * offset[1] + pad[1], 0))
            h = 0
            for line in lines:
                if line:
                    n_lines += 1
                    s = font.render(line, aa, colour)
                    if just == 2:
                        sfc.blit(s, (width - s.get_width() + o[0], h + o[1]))
                    elif just == 1:
                        sfc.blit(s, ((width - s.get_width()) / 2 + o[0],
                                     h + o[1]))
                    else:
                        sfc.blit(s, (o[0], h + o[1]))
                h += size + line_spacing
        return (sfc, n_lines)


import os

import nltk
from nltk.tokenize import sent_tokenize, word_tokenize
from bs4 import BeautifulSoup
nltk.download('punkt')

def calculate_ari(text):
    words = word_tokenize(text)
    sentences = sent_tokenize(text)

    num_characters = sum(len(word) for word in words)
    num_words = len(words)
    num_sentences = len(sentences)

    ari = 4.71 * (num_characters / num_words) + 0.5 * (num_words / num_sentences) - 21.43
    return ari

def calculate_fres(text):
    words = word_tokenize(text)
    sentences = sent_tokenize(text)

    num_words = len(words)
    num_sentences = len(sentences)

    fres = 206.835 - 1.015 * (num_words / num_sentences) - 84.6 * (sum(len(word) for word in words) / num_words)
    return fres

def calculate_lix(text):
    words = word_tokenize(text)
    sentences = sent_tokenize(text)

    num_long_words = len([word for word in words if len(word) > 6])
    num_words = len(words)
    num_sentences = len(sentences)

    lix = (num_words / num_sentences) + (num_long_words * 100 / num_words)
    return lix

def calculate_metrics(text):
    words = word_tokenize(text)
    sentences = sent_tokenize(text)

    num_words = len(words)
    num_sentences = len(sentences)
    num_characters = sum(len(word) for word in words)
    num_syllables = sum([syllable_count(word) for word in words])

    avg_syllables_per_word = num_syllables / num_words
    avg_words_per_sentence = num_words / num_sentences
    avg_letters_per_word = num_characters / num_words

    reading_time = num_words / 200  # Assuming average reading speed of 200 words per minute
    speaking_time = num_words / 150  # Assuming average speaking speed of 150 words per minute

    return avg_syllables_per_word, avg_words_per_sentence, avg_letters_per_word, num_sentences, num_words, reading_time, speaking_time

def syllable_count(word):
    vowels = "aeiouAEIOU"
    count = 0
    in_word = False

    for char in word:
        if char in vowels:
            if not in_word:
                count += 1
                in_word = True
        else:
            in_word = False

    if word.endswith("e"):
        count -= 1

    return count if count > 0 else 1

def list_files(directory):
    files = []
    for filename in os.listdir(directory):
        filepath = os.path.join(directory, filename)
        if os.path.isfile(filepath):
            files.append(filepath)
    return files

if __name__ == "__main__":
    folder_path = "./pp_example/"
    files_in_folder = list_files(folder_path)
    for file in files_in_folder:
        if file == "./pp_example/.DS_Store":
            continue

        with open(file, "r", encoding="utf-8") as file:
            html_content = file.read()

        soup = BeautifulSoup(html_content, "html.parser")
        text = soup.get_text()

        ari = calculate_ari(text)
        fres = calculate_fres(text)
        lix = calculate_lix(text)
        avg_syllables_per_word, avg_words_per_sentence, avg_letters_per_word, num_sentences, num_words, reading_time, speaking_time = calculate_metrics(
            text)
        print("availability metrics: ")
        print("ARI:", ari)
        print("FRES:", fres)
        print("LIX:", lix)
        print("Average Syllables per Word:", avg_syllables_per_word)
        print("Average Words per Sentence:", avg_words_per_sentence)
        print("Average Letters per Word:", avg_letters_per_word)
        print("Sentence Count:", num_sentences)
        print("Word Count:", num_words)
        print("Reading Time (minutes):", reading_time)
        print("Speaking Time (minutes):", speaking_time)
        print("---------------------------")


# https://leetcode.com/problems/number-of-islands/
# TC: O(M*N)
# SC: O(M*N)

class Solution:
    from collections import deque

    def _is_valid(self, grid, r, c):
        if r < 0 or c < 0 or r > len(grid) - 1 or c > len(grid[0]) - 1:
            return False
        return True

    def dfs(self, grid, x, y):
        DIRECTIONS = [(1, 0), (-1, 0), (0, 1), (0, -1)]
        grid[x][y] = '0'
        for d in DIRECTIONS:
            new_x = x + d[0]
            new_y = y + d[1]
            if self._is_valid(grid, new_x,
                              new_y) and grid[new_x][new_y] == '1':
                self.dfs(grid, new_x, new_y)

    def bfs(self, grid, x, y):
        DIRECTIONS = [(1, 0), (-1, 0), (0, 1), (0, -1)]
        grid[x][y] = '0'
        queue = collections.deque()
        queue.append((x, y))

        while queue:
            x, y = queue.popleft()
            for d in DIRECTIONS:
                new_x = x + d[0]
                new_y = y + d[1]
                if self._is_valid(grid, new_x,
                                  new_y) and grid[new_x][new_y] == '1':
                    queue.append((new_x, new_y))

    def numIslands(self, grid: List[List[str]]) -> int:

        if not grid or not grid[0]:
            return 0

        r = len(grid)
        c = len(grid[0])
        count = 0

        for x in range(r):
            for y in range(c):
                if grid[x][y] == '1':
                    count += 1
                    self.dfs(grid, x, y)
        return count


# Explore Coriolis Argo oxygen data
# Plot temporal and spatial distributions of this data

import xarray as xr
import glob

argo_dir = '/home/hourstonh/Documents/climatology/oxy_clim/Coriolis_Argo/'
argo_files = glob.glob(argo_dir + '*.nc')

argo = argo_files[0]

argodat = xr.open_dataset(argo)

# Some key variable names:
# LATITUDE, LONGITUDE, DOXY_ADJUSTED, N_PROF (number of profiles), JULD (Julian day in UTC)
# JULD same as JULD_LOCATION ??

# JULD in numpy.datetime64 format

# Plot spatial distribution

argodat.close()


from django.conf.urls import url

from portelaGerenciador.core.views import home, cadastro_usuario, sucesso_usuario,cadastro_produtos,sucesso_produto

urlpatterns = [
    url(r'^$', home, name='home'),
    url(r'^cadastro-usuario/$', cadastro_usuario, name='cadastrousuario'),
    url(r'^sucesso-usuario/$', sucesso_usuario, name='usuariosucesso'),
    url(r'^cadastro-produto/$',cadastro_produtos, name='cadastroproduto'),
    url(r'^sucesso-produto/$',sucesso_produto, name='sucessoproduto'),
]


from __future__ import print_function

import argparse
import sys

import pkg_resources
import six

from .download import download

distribution = pkg_resources.get_distribution('gdown')

class _ShowVersionAction(argparse.Action):
    def __init__(self, *args, **kwargs):
        kwargs['nargs'] = 0
        self.version = kwargs.pop('version')
        super(self.__class__, self).__init__(*args, **kwargs)

    def __call__(self, parser, namespace, values, option_string=None):
        print(
            'gdown {ver} at {pos}'.format(
                ver=self.version, pos=distribution.location
            )
        )
        parser.exit()

def main():
    parser = argparse.ArgumentParser(
        formatter_class=argparse.ArgumentDefaultsHelpFormatter
    )
    parser.add_argument(
        '-V',
        '--version',
        version=distribution.version,
        action=_ShowVersionAction,
        help='display version',
    )
    parser.add_argument(
        'url_or_id', help='url or file id (with --id) to download file from'
    )
    parser.add_argument('-O', '--output', help='output filename')
    parser.add_argument(
        '-q', '--quiet', action='store_true', help='suppress standard output'
    )
    parser.add_argument(
        '--id',
        action='store_true',
        help='flag to specify file id instead of url',
    )
    args = parser.parse_args()

    if args.output == '-':
        if six.PY3:
            args.output = sys.stdout.buffer
        else:
            args.output = sys.stdout

    if args.id:
        url = 'https://drive.google.com/uc?id={id}'.format(id=args.url_or_id)
    else:
        url = args.url_or_id

    download(url=url, output=args.output, quiet=args.quiet)

if __name__ == '__main__':
    main()


'''
Smallish example for what appears to be a pygraphviz runtime error
'''

from pygraphviz import AGraph

# this works well:

dd = AGraph(name = "test", compound = "true", directed = "true", newrank = "true")
dd.add_node("a", shape = "point")
dd.add_node("b", shape = "point")
dd.add_subgraph(["a", "b"], name = 'cluster_a_b')

# but a similar thing on an inheriting class raises an error upon the subgraph call:

class D(AGraph):

    def __init__(self):
        super().__init__(name = "test", compound = "true", directed = "true", newrank = "true")
        # alternative, same outcome:
        # AGraph.__init__(self, name = "test", compound = "true", directed = "true", newrank = "true")
        self.add_node("a", shape = "point")
        self.add_node("b", shape = "point")
        self.add_subgraph(["a", "b"], name = 'cluster_a_b')
        self.add_node("c", shape = "point")
        self.add_edge("c",'cluster_a_b')
        self.add_node("d", shape = "point")
d = D()


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

# arguments define
import argparse

# load torch
import torchvision

# other utilities
# import matplotlib.pyplot as plt
from sklearn import svm
from sklearn.metrics import confusion_matrix
#%% Load the training data
def MNIST_DATASET_TRAIN(downloads, train_amount):
    # Load dataset
    training_data = torchvision.datasets.MNIST(
              root = './mnist/',
              train = True,
              transform = torchvision.transforms.ToTensor(),
              download = downloads
              )

    #Convert Training data to numpy
    train_data = training_data.train_data.numpy()[:train_amount]
    train_label = training_data.train_labels.numpy()[:train_amount]

    # Print training data size
    print('Training data size: ',train_data.shape)
    print('Training data label size:',train_label.shape)
    # plt.imshow(train_data[0])
    # plt.show()

    train_data = train_data/255.0

    return train_data, train_label

#%% Load the test data
def MNIST_DATASET_TEST(downloads, test_amount):
    # Load dataset
    testing_data = torchvision.datasets.MNIST(
              root = './mnist/',
              train = False,
              transform = torchvision.transforms.ToTensor(),
              download = downloads
              )

    # Convert Testing data to numpy
    test_data = testing_data.test_data.numpy()[:test_amount]
    test_label = testing_data.test_labels.numpy()[:test_amount]

    # Print training data size
    print('test data size: ',test_data.shape)
    print('test data label size:',test_label.shape)
    # plt.imshow(test_data[0])
    # plt.show()

    test_data = test_data/255.0

    return test_data, test_label

#%% Main function for MNIST dataset
if __name__=='__main__':

    # Training Arguments Settings
    parser = argparse.ArgumentParser(description='Saak')
    parser.add_argument('--download_MNIST', default=True, metavar='DL',
                        help='Download MNIST (default: True)')
    parser.add_argument('--train_amount', type=int, default=60000,
                        help='Amount of training samples')
    parser.add_argument('--test_amount', type=int, default=2000,
                        help='Amount of testing samples')
    args = parser.parse_args()

    # Print Arguments
    print('\n----------Argument Values-----------')
    for name, value in vars(args).items():
        print('%s: %s' % (str(name), str(value)))
    print('------------------------------------\n')

    # Load Training Data & Testing Data
    train_data, train_label = MNIST_DATASET_TRAIN(args.download_MNIST, args.train_amount)
    test_data, test_label = MNIST_DATASET_TEST(args.download_MNIST, args.test_amount)

    training_features = train_data.reshape(args.train_amount,-1)
    test_features = test_data.reshape(args.test_amount,-1)

    # Training SVM
    print('------Training and testing SVM------')
    clf = svm.SVC(C=5, gamma=0.05,max_iter=10)
    clf.fit(training_features, train_label)

    #Test on test data
    test_result = clf.predict(test_features)
    precision = sum(test_result == test_label)/test_label.shape[0]
    print('Test precision: ', precision)

    #Test on Training data
    train_result = clf.predict(training_features)
    precision = sum(train_result == train_label)/train_label.shape[0]
    print('Training precision: ', precision)

    #Show the confusion matrix
    matrix = confusion_matrix(test_label, test_result)


import logging
import os

from metrics import Metric

class MediaMetric(Metric):
  """MediaMetric class injects and calls JS responsible for recording metrics.

  Default media metrics are collected for every media element in the page,
  such as decoded_frame_count, dropped_frame_count, decoded_video_bytes, and
  decoded_audio_bytes.
  """

  def __init__(self, tab):
    super(MediaMetric, self).__init__()
    with open(os.path.join(os.path.dirname(__file__), 'media.js')) as f:
      js = f.read()
      tab.ExecuteJavaScript(js)
    self._results = None
    self._skip_basic_metrics = False

  def Start(self, page, tab):
    """Create the media metrics for all media elements in the document."""
    if hasattr(page, 'skip_basic_metrics'):
      self._skip_basic_metrics = page.skip_basic_metrics
    tab.ExecuteJavaScript('window.__createMediaMetricsForDocument()')

  def Stop(self, page, tab):
    self._results = tab.EvaluateJavaScript('window.__getAllMetrics()')

  def AddResults(self, tab, results):
    """Reports all recorded metrics as Telemetry perf results."""
    trace_names = []
    for media_metric in self._results:
      trace_names.append(self._AddResultsForMediaElement(media_metric, results))

    return '_'.join(trace_names) or tab.url

  def _AddResultsForMediaElement(self, media_metric, results):
    """Reports metrics for one media element.

    Media metrics contain an ID identifying the media element and values:
    media_metric = {
      'id': 'video_1',
      'metrics': {
          'time_to_play': 120,
          'decoded_bytes': 13233,
          ...
      }
    }
    """
    def AddOneResult(metric, unit):
      metrics = media_metric['metrics']
      for m in metrics:
        if m.startswith(metric):
          special_label = m[len(metric):]
          if isinstance(metrics[m], list):
            values = [float(v) for v in metrics[m]]
          else:
            values = float(metrics[m])
          results.Add(trace + special_label, unit, values,
                      chart_name=metric, data_type='default')

    trace = media_metric['id']
    if not trace:
      logging.error('Metrics ID is missing in results.')
      return

    if not self._skip_basic_metrics:
      AddOneResult('buffering_time', 'ms')
      AddOneResult('decoded_audio_bytes', 'bytes')
      AddOneResult('decoded_video_bytes', 'bytes')
      AddOneResult('decoded_frame_count', 'frames')
      AddOneResult('dropped_frame_count', 'frames')
      AddOneResult('time_to_play', 'ms')

    AddOneResult('avg_loop_time', 'ms')
    AddOneResult('seek', 'ms')
    return trace


import xml.etree.ElementTree as ET
import pandas as pd
import subprocess

def get_qualified_name(project):
    """
    Returns the qualified suffix name for the project
    :param project: the project
    :return: the qualified name
    """
    d = {'cassandra': 'org.apache.cassandra',
         'dagger': 'dagger',
         'guava': 'com.google.common',
         'ivy': 'org.apache.ivy',
         'lang': 'org.apache.commons.lang',
         'math': 'org.apache.commons.math',
         'time': 'org.joda.time'}
    return d[project]

def parse_xml(path='metrics/jdepend/dagger.xml', project='dagger'):
    """
    Parses the xml resulting from the jdepend xml output and output the result as a frame;
    deletes also the intermediate xml
    :param path: the path for the xml file
    :param project: the project under analysis
    :return: a pandas DataFrame
    """

    tree = ET.parse(path)
    root = tree.getroot()
    packages = root.find('Packages')

    # columns of the final dataframe
    project_list = []
    Ca_list = []
    Ce_list = []
    A_list = []
    I_list = []
    D_list = []
    V_list = []
    class_list = []

    for package in packages:

        if package.attrib['name'] and package.attrib['name'].startswith(get_qualified_name(project)):
            classes = [x.text.strip() for x in package.iter('Class')]
            l = len(classes)
            project_list.extend([project] * l)
            class_list.extend(classes)

            Ca_list.extend([float(next(package.iter('Ca')).text)] * l)
            Ce_list.extend([float(next(package.iter('Ce')).text)] * l)
            A_list.extend([float(next(package.iter('A')).text)] * l)
            I_list.extend([float(next(package.iter('I')).text)] * l)
            D_list.extend([float(next(package.iter('D')).text)] * l)
            V_list.extend([float(next(package.iter('V')).text)] * l)
    output = pd.DataFrame({'project': project_list,
                           'class': class_list,
                           'Ca': Ca_list,
                           'Ce': Ce_list,
                           'A': A_list,
                           'I': I_list,
                           'D': D_list,
                           'V': V_list})
    output.to_csv('metrics/jdepend/{}.csv'.format(project), index=False)
    command = 'rm -rf metrics/jdepend/{}.xml'.format(project)
    delete = subprocess.run(command.split(), stderr=subprocess.PIPE)
    if delete.returncode != 0:
        print('error')

if __name__ == '__main__':
    parse_xml()


from django.shortcuts import render
from django.views import View
from SysProj.settings import DATABASES
from Products.models import Product
# Create your views here.

class ViewAllDB(View):
    def get(self, request, slug):
        db = request.user.databases
        return render(request, 'alldb.html', context ={"data": [ key for key in db ] })

class CreateProduct(View):
    def get(self, request, slug):
        return render(request, 'create_product.html' , context = {"db":slug})

    def post(self, request, slug):
        data = request.POST
        user_id = request.user.id
        print('user_id',user_id)
        Product.objects.using(slug).create( name = data['name'], price = data['price'], unit = data['unit'])
        return render(request, 'create_product.html' , context = {"db":slug})

class AllProduct(View):
    def get(self, request):
        db1 = Product.objects.using('database1').all()
        db2 = Product.objects.using('database2').all()
        db3 = Product.objects.using('database3').all()
        db4 = Product.objects.using('database4').all()
        db5 = Product.objects.using('database5').all()

        data = {
            "database1":db1,
            "database2":db2,
            "database3":db3,
            "database4":db4,
            "database5":db5,
        }

        return render(request, 'all_product.html', context = data)