#hangman by alexandra sergueeva and joshua yu
import random
import time
import pickle
from datetime import datetime
add_library("minim")
def setup():
    global minim, click, bloop, scoreadded, gamemode, font, playerName, wrongGuesses, hangmanImage, guessLimit, keyboard, listGuessed, listWrongGuesses, points, hintChosen, highscoreDict
    gamemode = "startMenu"
    font = loadFont("BradleyHandITC-48.vlw")
    words = getFileInfo("hangmanwords.txt")
    hangmanImage = {0:loadImage("hangman.png"), 1:loadImage("hangman1.png"), 2:loadImage("hangman2.png"), 3:loadImage("hangman3.png"), 4:loadImage("hangman4.png"), 5:loadImage("hangman5.png"), 6:loadImage("hangman6.png")}
    keyboard = [[['q', True],['w', True],['e', True],['r', True],['t', True],['y', True],['u', True],['i', True],['o', True],['p', True]],
                [['a', True],['s', True],['d', True],['f', True],['g', True],['h', True],['j', True],['k', True],['l', True]],
                [['z', True],['x', True],['c', True],['v', True],['b', True],['n', True],['m', True]]]
    wordGenerator()
    listGuessed = []
    listWrongGuesses = []
    ###print(words)
    size(800,600)
    playerName = []
    wrongGuesses = 0
    guessLimit = 6
    points = 0
    highscoreDict = {}
    hintChosen = False
    scoreadded = False
    add_library('minim')
    minim = Minim(this)
    click = minim.loadFile("click.wav")
    bloop = minim.loadFile("correct.wav")

def draw():
    imageMode(CENTER)
    if gamemode == "startMenu":
        startMenu()
    if gamemode == "playerMenu":
        playerMenu()
    if gamemode == "scoresMenu":
        scoresMenu()
    if gamemode == "helpMenu":
        helpMenu()
    if gamemode == "inGame":
        game()
        keyboardDisplay()
        hintDisplay()
    if gamemode == "end":
        endScreen()
    if gamemode == "won":
        wonScreen()

def getFileInfo(fileName):
    global fileInfo
    file = open(fileName)
    fileInfo = []
    fileText = file.readlines()
    for x in fileText:
        x = x.strip()
        x = x.split(",")
        fileInfo.append(x)
    return fileInfo

def startMenu(): #start menu
    background(255)
    textFont(font)
    textAlign(CENTER)
    textSize(48)
    fill(0)
    text("hangman", width/2, height/5)
    if width/2-50 <= mouseX <= width/2+50 and 200 <= mouseY <= 270:
        fill(225)
        text("play", width/2, (height/5)*2)
        fill(0)
    else:
        fill(0)
        text("play", width/2, (height/5)*2)
    if width/2-50 <= mouseX <= width/2+50 and 325 <= mouseY <= 380:
        fill(225)
        text("help", width/2, (height/5)*3)
        fill(0)
    else:
        fill(0)
        text("help", width/2, (height/5)*3)
    if width/2-60 <= mouseX <= width/2+60 and 460 <= mouseY <= 485:
        fill(225)
        text("scores", width/2, (height/5)*4)
        fill(0)
    else:
        fill(0)
        text("scores", width/2, (height/5)*4)

def playerMenu(): #where you select number of players and input names
    global minLimit
    background(255)
    minLimit = 3
    textFont(font)
    textAlign(CENTER)
    textSize(48)
    fill(0)
    text("type your name", width/2, height/3)
    text(getPlayerName(), width/2, height/2)
    if width/2-60 <= mouseX <= width/2+60 and 460 <= mouseY <= 485 or len(playerName) < minLimit:
        fill(225)
        text("begin", width/2, (height/5)*4)
        fill(0)
    else:
        fill(0)
        text("begin", width/2, (height/5)*4)
    if 35 <= mouseX <= 64 and 24 <= mouseY <= 48:
        fill(225)
        text("<", 50, 50)
        fill(0)
    else:
        fill(0)
        text("<", 50, 50)

def getPlayerName():
    global curKey, playerName
    curKey = ""
    return str("".join(playerName))

def helpMenu(): #help menu
    global helpmode, prevGamemode
    imageMode(CORNER)
    background(255)
    textFont(font)
    textAlign(CENTER)
    textSize(48)
    fill(0)
    if helpmode == "help1":
        help = loadImage("help1.jpg")
        image(help,0,60,830,450)
        stroke(255,0,0)
        line(539,209,575,209)
        stroke(0)
        if 700 <= mouseX <= 735 and 510 <= mouseY <= 540:
            fill(225)
            text(">", 720, 540)
            fill(0)
        else:
            fill(0)
            text(">", 720, 540)
    if helpmode == "help2":
        help = loadImage("help2.jpg")
        image(help,0,80,830,400)
    if 35 <= mouseX <= 64 and 24 <= mouseY <= 48:
        fill(225)
        text("<", 50, 50)
        fill(0)
    else:
        fill(0)
        text("<", 50, 50)

def scoresMenu(): #scores menu
    global highscoreDict, points, playerName, fromhighscore, tohighscore
    filename = 'highscores'
    infile = open(filename, 'rb')
    try:
        highscoreDict = pickle.load(infile)
    except EOFError:
        highscoreDict = {}
    infile.close()
    background(255)
    fill(0)
    textSize(30)
    textAlign(CENTER)
    text("Scores", width/5, 50)
    text("Name", (width/5)*2, 50)
    text("Date", (width/5)*3, 50)
    text("Time", (width/5)*4, 50)
    itemlist = highscoreDict.items()
    sortlist(itemlist)
    if len(itemlist) > 10:
        numloop = 10
    else:
        numloop = len(itemlist)
    for x in range(numloop):
        text(str(itemlist[x][1]), width/5, 100+50*x)
        namedatetimelist = itemlist[x][0].split(", ")
        text(str(namedatetimelist[0]), (width/5)*2, 100+50*x)
        text(str(namedatetimelist[1]), (width/5)*3, 100+50*x)
        text(str(namedatetimelist[2]), (width/5)*4, 100+50*x)
    textSize(48)
    if 35 <= mouseX <= 64 and 24 <= mouseY <= 48:
        fill(225)
        text("<", 50, 50)
        fill(0)
    else:
        fill(0)
        text("<", 50, 50)

def sortlist(itemlist):
    for z in range(len(itemlist)-1, 0, -1): #sorting by score
        for y in range(z):
            if itemlist[y][1] < itemlist[y+1][1]:
                itemlist[y], itemlist[y+1] = itemlist[y+1], itemlist[y]
    for z in range(len(itemlist)-1, 0, -1): #sorting by datetime
        for y in range(z):
            if itemlist[y][1] == itemlist[y+1][1]:
                datetimeobj1 = datetime.strptime(itemlist[y][0].split(", ", 1)[1], "%m/%d/%Y, %H:%M:%S")
                datetimeobj2 = datetime.strptime(itemlist[y+1][0].split(", ", 1)[1], "%m/%d/%Y, %H:%M:%S")
                if datetimeobj1 > datetimeobj2:
                    itemlist[y], itemlist[y+1] = itemlist[y+1], itemlist[y]
    return itemlist

def game():
    global wrongGuesses, fileInfo, guessWord, displayWord, gamemode
    background(255)
    textFont(font)
    textAlign(CENTER)
    textSize(48)
    fill(0)
    image(hangmanImage[wrongGuesses], width/5, height/4, 300, 300)
    strokeWeight(15)
    line(0, 300, width, 300)
    strokeWeight(1)
    for letter in range(len(displayWord)): #put correctly guessed letters on screen
        if displayWord[letter] != "_":
            text(displayWord[letter], (550-(len(displayWord)*40/2)+(40*letter)), 250)
        line((535-(len(displayWord)*40/2)+(40*letter)),250,(565-(len(displayWord)*40/2)+(40*letter)),250)
    textAlign(LEFT, BOTTOM)
    textSize(25)
    text("Points: "+str(points), 20, height-20)
    if wrongGuesses == guessLimit:
        gamemode = "end"
    textAlign(RIGHT, BOTTOM)
    if 735 <= mouseX <= 780 and 555 <= mouseY <= 575:
        fill(225)
    text("help",width-20, height-20)
    if 660 <= mouseX <= 720 and 555 <= mouseY <= 575:
        fill(225)
    else:
        fill(0)
    text("Menu",width-80, height-20)

def wordGenerator(): #get random guess word from list
    global displayWord, guessWord, worth, wordHint, gamemode
    if len(fileInfo) != 0:
        randWord = random.randint(0,len(fileInfo) - 1)
        guessWord = fileInfo[randWord][0]
        wordHint = fileInfo[randWord][1]
        displayWord = ["_"]*len(guessWord)
        fileInfo.pop(randWord) #pop word from list when it is guessed correctly
    elif len(fileInfo) == 0: #if there are no words left in the list, player won
        gamemode = "won"

def hintDisplay():  #if player clicks for hint, display hint
    textFont(font)
    textAlign(CENTER, CENTER)
    textSize(32)
    fill(0)
    if hintChosen:
        text("Click for Hint", 550, 30)
        text(wordHint, 550, 60)
    elif 450 <= mouseX <= 650 and 15 <= mouseY <= 50:
        fill(225)
        text("Click for Hint", 550, 30)
    else:
        fill(0)
        text("Click for Hint", 550, 30)

def keyboardDisplay(): #display letters
    global keyboard
    textFont(font)
    textAlign(CENTER, CENTER)
    textSize(48)
    for x in range(len(keyboard)): #displays the keyboard
        for y in range(len(keyboard[x])):
            if keyboard[x][y][1] == False:
                fill(225)
            else:
                fill(0)
            text(keyboard[x][y][0],40+width/2-((80*len(keyboard[x]))/2)+(80*y), 350+(80*x))

def endScreen():
    global guessWord, scoreadded
    textFont(font)
    textAlign(CENTER, CENTER)
    textSize(48)
    background(255)
    fill(0)
    text("The word was " + guessWord, width/2, height/5)
    text(getPlayerName()+", you lost!", width/2, (height/5)*2)
    text("Final Score: "+str(points), width/2, (height/5)*3)
    if 250 <= mouseX <= 550 and 470 <= mouseY <= 510:
        fill(225)
    text("Click to restart", width/2, (height/5)*4)
    if scoreadded == False:
        addscore()
        scoreadded = True

def wonScreen():
    global playerName, highscoreDict, points, scoreadded
    textFont(font)
    textAlign(CENTER, CENTER)
    textSize(48)
    background(255)
    fill(0)
    text(getPlayerName()+ ", you won!", width/2, height/3)
    text("Final Score: "+str(points), width/2, height/2)
    if 250 <= mouseX <= 550 and 390 <= mouseY <= 420:
        fill(225)
    text("Click to restart", width/2, height/3*2)
    if scoreadded == False:
        addscore()
        scoreadded = True

def addscore():
    global highscoreDict, points, playerName, fromhighscore, tohighscore
    filename = 'highscores'
    ##print("before open")
    infile = open(filename, 'rb')
    ##print("before load")
    try:
        highscoreDict = pickle.load(infile)
    except EOFError:
        highscoreDict = {}
    infile.close()
    itemlist = highscoreDict.items()
    if len(highscoreDict) < 10 or points > sortlist(itemlist)[9][1]:
        outfile = open(filename, 'wb')
        highscoreDict[str("".join(playerName))+", "+str(datetime.now().strftime("%m/%d/%Y, %H:%M:%S"))] = points
        if len(highscoreDict) > 10:
            itemlist = highscoreDict.items()
            del highscoreDict[sortlist(itemlist)[9][0]]
        pickle.dump(highscoreDict, outfile)
        outfile.close()

def restart(): #restarts the game
    global points, gamemode, playerName, wrongGuesses, keyboard, listWrongGuesses, listGuessed, hintChosen, scoreadded
    gamemode = "startMenu"
    playerName = []
    getFileInfo("hangmanwords.txt")
    wordGenerator()
    keyboard = [[['q', True],['w', True],['e', True],['r', True],['t', True],['y', True],['u', True],['i', True],['o', True],['p', True]],
    [['a', True],['s', True],['d', True],['f', True],['g', True],['h', True],['j', True],['k', True],['l', True]],
    [['z', True],['x', True],['c', True],['v', True],['b', True],['n', True],['m', True]]]
    wrongGuesses = 0
    listWrongGuesses = []
    listGuessed = []
    hintChosen = False
    scoreadded = False
    points = 0

def mousePressed():
    global helpmode, prevGamemode, gamemode, minLimit, wrongGuesses, keyboard, guessWord, listWrongGuesses, points, listGuessed, hintChosen, playerName, bloop, click
    #center coords of each letter on screen
    keyCoords = [[40, 350], [120, 350], [200, 350], [280, 350], [360, 350], [440, 350], [520, 350], [600, 350], [680, 350], [760, 350], [80, 430], [160, 430], [240, 430], [320, 430], [400, 430], [480, 430], [560, 430], [640, 430], [720, 430], [160, 510], [240, 510], [320, 510], [400, 510], [480, 510], [560, 510], [640, 510]]
    print(mouseX, mouseY)
    if gamemode == "startMenu" and mouseButton == LEFT:

        #if "start" clicked
        if width/2-50 <= mouseX <= width/2+50 and 200 <= mouseY <= 270:
            gamemode = "playerMenu"
            click = minim.loadFile("click.wav")
            click.play()

        #if "help" clicked
        if width/2-50 <= mouseX <= width/2+50 and 325 <= mouseY <= 380:
            gamemode = "helpMenu"
            helpmode = "help1"
            prevGamemode = "startMenu"
            print(gamemode)

        #if "scores clicked"
        if width/2-60 <= mouseX <= width/2+60 and 460 <= mouseY <= 485:
            gamemode = "scoresMenu"
            print("scores")

    if gamemode == "helpMenu":
        if 35 <= mouseX <= 64 and 24 <= mouseY <= 48:
            if helpmode == "help1":
                gamemode = prevGamemode
            if helpmode == "help2":
                gamemode = "helpMenu"
                helpmode = "help1"
        #if next arrow clicked in help menu
        if 700 <= mouseX <= 735 and 510 <= mouseY <= 540:
            helpmode = "help2"

    #if "begin" clicked
    if gamemode == "playerMenu" and mouseButton == LEFT:
        if width/2-60 <= mouseX <= width/2+60 and 460 <= mouseY <= 485:
            if len(playerName) >= minLimit:
                gamemode = "inGame"
                time.sleep(0.5)
        #if back arrow clicked
        if 35 <= mouseX <= 64 and 24 <= mouseY <= 48:
            gamemode = "startMenu"
            playerName = []

    if gamemode == "scoresMenu":
        if 35 <= mouseX <= 64 and 24 <= mouseY <= 48:
            gamemode = "startMenu"
    if gamemode == "inGame" and mouseButton == LEFT:
        for x in range(len(keyCoords)):
            if keyCoords[x][0]-20 <= mouseX <= keyCoords[x][0]+20 and keyCoords[x][1]-20 <= mouseY <= keyCoords[x][1]+20:
                if 0 <= x <= 9:
                    row = 0
                    column = x
                if 10 <= x <= 18:
                    row = 1
                    column = x - 10
                if 19 <= x <= 25:
                    row = 2
                    column = x - 19
                ###print("key "+str(x)+" clicked")
                if keyboard[row][column][1] == True:  #if letter clicked, turn it off and add letter to listGuessed[]
                    keyboard[row][column][1] = False
                    listGuessed.append(keyboard[row][column][0])
                for y in range(len(listGuessed)):
                    if listGuessed[y] in guessWord: #if clicked letter is in the guess word, add letter to displayWord[]
                        for z in range(guessWord.count(listGuessed[y])):
                            ###print("correct letter guessed")
                            displayWord[[i for i, n in enumerate(list(guessWord)) if n == listGuessed[y]][z]] = listGuessed[y]
                        if ''.join(displayWord) == guessWord: #if letters in displayWord[] match the guessWord, add points and generate new word, and reset keyboard, num wrong guesses, and list of guessed letters
                            bloop = minim.loadFile("correct.wav")
                            bloop.play()
                            if hintChosen == True:
                                points += int(round(len(guessWord)/2))
                            else:
                                points += len(guessWord)
                            wordGenerator()
                            keyboard = [[['q', True],['w', True],['e', True],['r', True],['t', True],['y', True],['u', True],['i', True],['o', True],['p', True]],
                            [['a', True],['s', True],['d', True],['f', True],['g', True],['h', True],['j', True],['k', True],['l', True]],
                            [['z', True],['x', True],['c', True],['v', True],['b', True],['n', True],['m', True]]]
                            wrongGuesses = 0
                            listWrongGuesses = []
                            listGuessed = []
                            hintChosen = False
                    elif listGuessed[y] not in listWrongGuesses:
                        listWrongGuesses.append(listGuessed[y])
                        wrongGuesses = len(listWrongGuesses)
                        ###print("wrong guesses: "+str(wrongGuesses))
                ###print(listGuessed)
                ###print(keyboard)
                ###print(displayWord)
            #if "click for hint" clicked
            if 450 <= mouseX <= 650 and 15 <= mouseY <= 50:
                hintChosen = True
        #if "help" in game mode clicked
        if 735 <= mouseX <= 780 and 555 <= mouseY <= 575:
            gamemode = "helpMenu"
            helpmode = "help1"
            prevGamemode = "inGame"
        #if "menu" in game mode clicked
        if 660 <= mouseX <= 720 and 555 <= mouseY <= 575:
            gamemode = "startMenu"
    if gamemode == "end":
        if 250 <= mouseX <= 550 and 470 <= mouseY <= 510: #if "click to restart" clicked
            restart()
    if gamemode == "won":
        if 250 <= mouseX <= 550 and 390 <= mouseY <= 420:
            restart()

def keyPressed():  #player name
    global gamemode, playerName, guessLimit
    nameLimit = 20
    validKeys = "abcdefghijklmnopqrstuvwxyz1234567890 "
    if gamemode == "playerMenu":
        if keyCode != SHIFT and key in validKeys and len(playerName) < nameLimit:
            curKey = key
            playerName.append(curKey)
            ###print(curKey)
        if key == BACKSPACE and len(playerName) > 0:
            playerName.pop()


"""Configure py.test default values and functionality."""
import os
import pprint
import sys
import unittest.mock
from pathlib import Path
from runpy import run_path

import fakeredis
import pytest
from mongobox import MongoBox
from mongoengine import connect, disconnect
from rq import SimpleWorker

import upload_rest_api.app as app_module
from upload_rest_api.jobs.utils import get_job_queue
from upload_rest_api.lock import ProjectLockManager
from upload_rest_api.models.project import Project
from upload_rest_api.models.token import Token
from upload_rest_api.models.user import User

# Prefer modules from source directory rather than from site-python
sys.path.insert(
    0, os.path.join(os.path.abspath(os.path.dirname(__file__)), '..')
)

def pytest_addoption(parser):
    """Add custom flag for printing all queries done during the test."""
    parser.addoption("--log-queries", action="store_true",
                     help=("Print a list of MongoDB queries performed during "
                           "the test."))

@pytest.yield_fixture(scope="function")
def upload_tmpdir(tmpdir):
    """Temporary directory for uploads."""
    tmpdir.mkdir("upload")
    yield Path(tmpdir.join("upload"))

@pytest.yield_fixture(scope="function", autouse=True)
def mock_config(monkeypatch, upload_tmpdir, test_mongo):
    """Mock the generic configuration located in
    `upload_rest_api.config` that is accessible whether Flask is active
    or not.
    """
    projects_path = upload_tmpdir / "projects"
    temp_upload_path = upload_tmpdir / "tmp"
    temp_tus_path = upload_tmpdir / "tus"
    trash_path = upload_tmpdir / "trash"

    projects_path.mkdir()
    temp_upload_path.mkdir()
    temp_tus_path.mkdir()
    trash_path.mkdir()

    mock_config_ = run_path("include/etc/upload_rest_api.conf")

    from upload_rest_api.config import CONFIG

    # Copy the values from "include/etc/upload_rest_api.conf"
    for key, value in mock_config_.items():
        if not key[0].isupper():
            # Skip Python built-ins
            continue

        monkeypatch.setitem(CONFIG, key, value)

    monkeypatch.setitem(CONFIG, "UPLOAD_BASE_PATH", str(upload_tmpdir))
    monkeypatch.setitem(CONFIG, "UPLOAD_PROJECTS_PATH", str(projects_path))
    monkeypatch.setitem(CONFIG, "UPLOAD_TMP_PATH", str(temp_upload_path))
    monkeypatch.setitem(CONFIG, "UPLOAD_TRASH_PATH", str(trash_path))

    # Use lower lock TTL and timeout to prevent tests from hanging for a
    # long time in case of a bug
    monkeypatch.setitem(CONFIG, "UPLOAD_LOCK_TTL", 30)
    monkeypatch.setitem(CONFIG, "UPLOAD_LOCK_TIMEOUT", 1)

    monkeypatch.setitem(CONFIG, "TUS_API_SPOOL_PATH", str(temp_tus_path))

    monkeypatch.setitem(CONFIG, "MONGO_HOST", test_mongo.address[0])
    monkeypatch.setitem(CONFIG, "MONGO_PORT", test_mongo.address[1])

    yield CONFIG

@pytest.fixture(autouse=True)
def patch_hashing_iters(monkeypatch):
    """Run tests with only 2000 hashing iters to avoid CPU
    bottlenecking.
    """
    monkeypatch.setattr("upload_rest_api.models.user.ITERATIONS", 2000)

@pytest.yield_fixture(autouse=True, scope="session")
def test_mongo():
    """
    Initialize MongoDB test instance and return MongoDB client instance for
    the database
    """
    box = MongoBox()
    box.start()

    client = box.client()
    client.PORT = box.port

    yield client

    box.stop()

@pytest.yield_fixture(scope="function", autouse=True)
def mongoengine(test_mongo):
    """
    Connect MongoEngine to the MongoBox instance
    """
    disconnect()
    connect(
        host=f"mongodb://{test_mongo.address[0]}:{test_mongo.address[1]}/upload",
        tz_aware=True
    )
    yield
    test_mongo.drop_database("upload")

@pytest.yield_fixture(autouse=True)
def db_logging_fx(test_mongo, request):
    """
    Optionally print list of database queries made during a test.

    If --log-queries flag is provided to pytest, all the database
    queries made during the test and it's setup are printed to stdout.
    """
    test_mongo.upload.command("profile", 2)
    yield

    if request.config.getoption("--log-queries"):
        queries = []

        for entry in test_mongo.upload.system.profile.find({}):
            try:
                queries.append(
                    entry["command"]
                )
            except KeyError:
                pass

        print()
        print(f"{len(queries)} QUERIES were sent")
        print()
        pprint.pprint(queries, indent=4)

@pytest.yield_fixture(scope="function", autouse=True)
def mock_redis(monkeypatch):
    """Patch job queue to use a mock Redis."""
    conn = fakeredis.FakeStrictRedis()

    monkeypatch.setattr(
        "upload_rest_api.redis.Redis",
        lambda *args, **kwargs: conn
    )

    yield conn

    # Ensure no dangling locks remain at the end of the test run.
    # Tests that leave dangling locks (eg. background jobs that are
    # deliberately left unfinished) should make them explicit by
    # removing the locks manually.
    for key in conn.keys("upload-rest-api:locks:*"):
        assert conn.hlen(key) == 0, \
            f"Locks were not released: {conn.hkeys(key)}"

    # fakeredis versions prior to v1.0 are not isolated and use a
    # singleton, making a manual flush necessary
    conn.flushall()

@pytest.fixture(scope="function")
def background_job_runner(test_auth):
    """Convenience fixture to complete background jobs based on the task
    API response received by the client.
    """
    def wrapper(
            test_client, queue_name, response=None, task_id=None,
            expect_success=True):
        """Find the RQ job corresponding to the background task and
        finish it. Either 'response' or 'task_id' needs to be provided to
        run the job.

        :param test_client: Flask test client
        :param str queue_name: Queue name containing the job
        :param response: Response returned to the client that contains
                         a polling URL and the task ID
        :param entry: Task ID
        :param bool expect_success: Whether to test for task success.
                                    Default is True.

        :returns: Return the task status HTTP response after the job
                  has been finished
        """
        # Get the task ID from the polling URL from the response or database
        # entry provided to the client
        assert response or task_id

        if response:
            polling_url = response.json["polling_url"]
            task_id = polling_url.split("/")[-1]

        polling_url = f"/v1/tasks/{task_id}"

        # Ensure the task can be found in the correct queue and complete
        # it
        queue = get_job_queue(queue_name)
        assert task_id in queue.job_ids

        job = queue.fetch_job(task_id)

        SimpleWorker([queue], connection=queue.connection).execute_job(
            job=job, queue=queue
        )

        # Check that the task API reports the task as having finished
        response = test_client.get(polling_url, headers=test_auth)

        assert response.json["status"] != "pending"

        if expect_success:
            assert response.json["status"] == "done"

        return response

    return wrapper

@pytest.yield_fixture(scope="function")
def app(test_mongo, mock_config, monkeypatch):
    """Create temporary upload directory and app, which uses it.

    Temp dirs are cleaned after use.

    :returns: flask.Flask instance
    """
    # Patch app to use default configuration file instead of global
    # configuration file (/etc/upload_rest_api.conf)
    def _mock_configure_app(app):
        """Update Flask app to use the same configuration parameters as
        `upload_rest_api.config.CONFIG`.
        """
        app.config.from_pyfile("../include/etc/upload_rest_api.conf")
    monkeypatch.setattr(app_module, "configure_app", _mock_configure_app)

    flask_app = app_module.create_app()

    # Initialize database.
    test_mongo.drop_database("upload")
    Project.create(identifier="test_project", quota=1000000)
    Project.create(identifier="test_project2", quota=12345678)

    monkeypatch.setattr("pymongo.MongoClient", lambda *args: test_mongo)

    flask_app.config["TESTING"] = True
    flask_app.config["UPLOAD_PROJECTS_PATH"] \
        = mock_config["UPLOAD_PROJECTS_PATH"]
    flask_app.config["UPLOAD_TMP_PATH"] = mock_config["UPLOAD_TMP_PATH"]
    flask_app.config["TUS_API_SPOOL_PATH"] = mock_config["TUS_API_SPOOL_PATH"]

    yield flask_app

@pytest.yield_fixture(scope="function")
def test_client(app):
    """
    Flask test client fixture
    """
    with app.test_client() as test_client_:
        yield test_client_

@pytest.fixture(scope="function")
def user(test_mongo):
    """Initialize and return User instance with db connection through
    Mongobox.
    """
    return User.create(username="test_user")

@pytest.fixture(scope="function")
def project():
    """Initialize and return a project dict
    """
    project = Project.create(identifier="test_project")
    return project

@pytest.fixture(scope="function")
def tokens_col(test_mongo):
    return test_mongo.upload.tokens

@pytest.fixture(scope="function")
def projects_col(test_mongo):
    return test_mongo.upload.projects

@pytest.fixture(scope="function")
def users_col(test_mongo):
    return test_mongo.upload.users

@pytest.fixture(scope="function")
def files_col(test_mongo):
    """Initialize and return Files instance with db connection through
    Mongobox.
    """
    return test_mongo.upload.files

@pytest.fixture(scope="function")
def tasks_col(test_mongo):
    """Initialize and return Tasks instance with db connection through
    Mongobox.
    """
    return test_mongo.upload.tasks

@pytest.fixture(scope="function")
# pylint: disable=unused-argument
# usefixtures not supported in fixture functions
def lock_manager(mock_config):
    """
    Return a project lock manager
    """
    return ProjectLockManager()

@pytest.fixture(scope="function")
# pylint: disable=unused-argument
# usefixtures not supported in fixture functions
def test_auth(test_client, test_mongo):
    """Returns credentials header for "test_user"."""
    token_data = Token.create(
        name="User test token",
        username="test_user",
        projects=["test_project"],
        expiration_date=None,
        admin=False
    )
    token = token_data["token"]

    return {"Authorization": f"Bearer {token}"}

@pytest.fixture(scope="function")
# pylint: disable=unused-argument
# usefixtures not supported in fixture functions
def test_auth2(test_client, test_mongo):
    """Returns credentials header for "test_user2"."""
    token_data = Token.create(
        name="User test token",
        username="test_user2",
        projects=["test_project2"],
        expiration_date=None,
        admin=False
    )
    token = token_data["token"]

    return {"Authorization": f"Bearer {token}"}

@pytest.fixture(scope="function")
def admin_auth(test_mongo, mock_config):
    """Return credentials header containing a token with admin privileges"""
    mock_config["ADMIN_TOKEN"] = "fddps-admin"

    return {
        "Authorization": "Bearer fddps-admin"
    }

@pytest.fixture(scope="function")
def mock_get_file_checksum(monkeypatch):
    """Mock get_file_checksum function in upload module.

    The mocked get_file_checksum function will always return "foo".
    """
    mock = unittest.mock.Mock(side_effect=lambda x, y: 'foo')
    monkeypatch.setattr(
        'upload_rest_api.models.upload.get_file_checksum', mock
    )
    return mock


import flask
import sqlite3

from flask import request, jsonify

app = flask.Flask(__name__)
app.config['DEBUG'] = True

@app.route('/api/v1/report', methods=['GET'])
def report():
    args = request.args
    if 'date' in args:
        date = args['date']
        with sqlite3.connect('app.db') as con:
            cur = con.cursor()
            customers = cur.execute(
                'SELECT COUNT(DISTINCT customer_id) FROM orders '
                'WHERE date(created_at)={}'.format(date)
            ).fetchone()[0]
            total_discount_amount = cur.execute(
                'SELECT SUM(discounted_amount) FROM order_lines '
                'INNER JOIN orders ON order_lines.order_id=orders.id '
                'WHERE date(created_at)={}'.format(date)
            ).fetchone()[0]
            items = cur.execute(
                'SELECT COUNT(*) FROM order_lines '
                'INNER JOIN orders ON order_lines.order_id=orders.id '
                'WHERE date(created_at)={}'.format(date)
            ).fetchone()[0]
            order_total_avg = cur.execute(
                'SELECT AVG(total_amount) FROM order_lines '
                'INNER JOIN orders ON order_lines.order_id=orders.id '
                'WHERE date(created_at)={}'.format(date)
            ).fetchone()[0]
            discount_rate_avg = cur.execute(
                'SELECT AVG(discount_rate) FROM order_lines '
                'INNER JOIN orders ON order_lines.order_id=orders.id '
                'WHERE date(created_at)={}'.format(date)
            ).fetchone()[0]
        con.close()
        return jsonify(
            {
                'customers': customers,
                'total_discount_amount': round(total_discount_amount, 2),
                'items': items,
                'order_total_avg': round(order_total_avg, 2),
                'discount_rate_avg': round(discount_rate_avg, 2)
            }
        )
    else:
        return jsonify(
            {
                'error': 'Please include a date in the request parameters.'
            }
        )

app.run()


# -*- coding: utf-8 -*-

import maya.cmds as mc
import maya.mel as mel
from Qt.QtWidgets import *
from Qt.QtCore import *
from Qt.QtGui import *
from miraLibs.mayaLibs import get_maya_win

class NormalCopy:
    def __init__(self):
        self.select_list = mc.ls(sl=1, l=1)
        self.load_plugin()

    @staticmethod
    def load_plugin():
        if not mc.pluginInfo('normalCopy', q=1, l=1):
            mc.loadPlugin('normalCopy.mll')

    def do_normal_copy(self):
        if not len(self.select_list) == 2:
            maya_win = get_maya_win.get_maya_win("PySide")
            QMessageBox.information(maya_win, 'error', 'select two objects ,please')
            return
        src_obj = self.select_list[0]
        tar_obj = self.select_list[1]
        _cmd = "normalCopy -src \"%s\" -tar \"%s\"" % (src_obj, tar_obj)
        mel.eval(_cmd)

def main():
    op = NormalCopy()
    op.do_normal_copy()

if __name__ == '__main__':
    main()


import math
import os
import json
import numpy as np
import torch
from tensorboardX import SummaryWriter

import distributed
from models.reporter_ext import ReportMgr, Statistics
from others.logging import logger
from others.utils import test_rouge, rouge_results_to_str
from rouge_score import rouge_scorer
from tqdm import tqdm
from prepro.data_builder import BertData

def _tally_parameters(model):
    n_params = sum([p.nelement() for p in model.parameters()])
    return n_params

def build_trainer(args, device_id, model, optim):
    """
    Simplify `Trainer` creation based on user `opt`s*
    Args:
        opt (:obj:`Namespace`): user options (usually from argument parsing)
        model (:obj:`onmt.models.NMTModel`): the model to train
        fields (dict): dict of fields
        optim (:obj:`onmt.utils.Optimizer`): optimizer used during training
        data_type (str): string describing the type of data
            e.g. "text", "img", "audio"
        model_saver(:obj:`onmt.models.ModelSaverBase`): the utility object
            used to save the model
    """

    grad_accum_count = args.accum_count
    n_gpu = args.world_size

    if device_id >= 0:
        gpu_rank = int(args.gpu_ranks[device_id])
    else:
        gpu_rank = 0
        n_gpu = 0

    print('gpu_rank %d' % gpu_rank)

    tensorboard_log_dir = args.model_path

    writer = SummaryWriter(tensorboard_log_dir, comment="Unmt")

    report_manager = ReportMgr(args.report_every, start_time=-1, tensorboard_writer=writer)

    trainer = Trainer(args, model, optim, grad_accum_count, n_gpu, gpu_rank, report_manager)

    # print(tr)
    if (model):
        n_params = _tally_parameters(model)
        logger.info('* number of parameters: %d' % n_params)

    return trainer

class Trainer(object):
    """
    Class that controls the training process.

    Args:
            model(:py:class:`onmt.models.model.NMTModel`): translation model
                to train
            train_loss(:obj:`onmt.utils.loss.LossComputeBase`):
               training loss computation
            valid_loss(:obj:`onmt.utils.loss.LossComputeBase`):
               training loss computation
            optim(:obj:`onmt.utils.optimizers.Optimizer`):
               the optimizer responsible for update
            trunc_size(int): length of truncated back propagation through time
            shard_size(int): compute loss in shards of this size for efficiency
            data_type(string): type of the source input: [text|img|audio]
            norm_method(string): normalization methods: [sents|tokens]
            grad_accum_count(int): accumulate gradients this many times.
            report_manager(:obj:`onmt.utils.ReportMgrBase`):
                the object that creates reports, or None
            model_saver(:obj:`onmt.models.ModelSaverBase`): the saver is
                used to save a checkpoint.
                Thus nothing will be saved if this parameter is None
    """

    def __init__(self, args, model, optim,
                 grad_accum_count=1, n_gpu=1, gpu_rank=1,
                 report_manager=None):
        # Basic attributes.
        self.args = args
        self.save_checkpoint_steps = args.save_checkpoint_steps
        self.model = model
        self.optim = optim
        self.grad_accum_count = grad_accum_count
        self.n_gpu = n_gpu
        self.gpu_rank = gpu_rank
        self.report_manager = report_manager

        self.loss = torch.nn.BCELoss(reduction='none')
        assert grad_accum_count > 0
        # Set model in training mode.
        if (model):
            self.model.train()

    def train(self, train_iter_fct, train_steps, valid_iter_fct=None, valid_steps=-1):
        """
        The main training loops.
        by iterating over training data (i.e. `train_iter_fct`)
        and running validation (i.e. iterating over `valid_iter_fct`

        Args:
            train_iter_fct(function): a function that returns the train
                iterator. e.g. something like
                train_iter_fct = lambda: generator(*args, **kwargs)
            valid_iter_fct(function): same as train_iter_fct, for valid data
            train_steps(int):
            valid_steps(int):
            save_checkpoint_steps(int):

        Return:
            None
        """
        logger.info('Start training...')

        # step =  self.optim._step + 1
        step = self.optim._step + 1
        true_batchs = []
        accum = 0
        normalization = 0
        train_iter = train_iter_fct()

        total_stats = Statistics()
        report_stats = Statistics()
        self._start_report_manager(start_time=total_stats.start_time)

        while step <= train_steps:

            reduce_counter = 0
            for i, batch in enumerate(train_iter):
                if self.n_gpu == 0 or (i % self.n_gpu == self.gpu_rank):

                    true_batchs.append(batch)
                    normalization += batch.batch_size
                    accum += 1
                    if accum == self.grad_accum_count:
                        reduce_counter += 1
                        if self.n_gpu > 1:
                            normalization = sum(distributed
                                                .all_gather_list
                                                (normalization))

                        self._gradient_accumulation(
                            true_batchs, normalization, total_stats,
                            report_stats)

                        report_stats = self._maybe_report_training(
                            step, train_steps,
                            self.optim.learning_rate,
                            report_stats)

                        true_batchs = []
                        accum = 0
                        normalization = 0
                        if (step % self.save_checkpoint_steps == 0 and self.gpu_rank == 0):
                            self._save(step)

                        step += 1
                        if step > train_steps:
                            break
            train_iter = train_iter_fct()

        return total_stats

    def validate(self, valid_iter, step=0):
        """ Validate model.
            valid_iter: validate data iterator
        Returns:
            :obj:`nmt.Statistics`: validation loss statistics
        """
        # Set model in validating mode.
        self.model.eval()
        stats = Statistics()

        with torch.no_grad():
            for batch in valid_iter:
                src = batch.src
                labels = batch.src_sent_labels
                segs = batch.segs
                clss = batch.clss
                mask = batch.mask_src
                mask_cls = batch.mask_cls
                sentence_rhetorical_roles = batch.sentence_rhetorical_roles

                sent_scores, mask = self.model(src, segs, clss, mask, mask_cls,sentence_rhetorical_roles)

                loss = self.loss(sent_scores, labels.float())
                loss = (loss * mask.float()).sum()
                batch_stats = Statistics(float(loss.cpu().data.numpy()), len(labels))
                stats.update(batch_stats)
            self._report_step(0, step, valid_stats=stats)
            return stats

    def test(self, test_iter, step, cal_lead=False, cal_oracle=False):
        """ Validate model.
            valid_iter: validate data iterator
        Returns:
            :obj:`nmt.Statistics`: validation loss statistics
        """

        # Set model in validating mode.
        def _get_ngrams(n, text):
            ngram_set = set()
            text_length = len(text)
            max_index_ngram_start = text_length - n
            for i in range(max_index_ngram_start + 1):
                ngram_set.add(tuple(text[i:i + n]))
            return ngram_set

        def _block_tri(c, p):
            tri_c = _get_ngrams(3, c.split())
            for s in p:
                tri_s = _get_ngrams(3, s.split())
                if len(tri_c.intersection(tri_s)) > 0:
                    return True
            return False

        if (not cal_lead and not cal_oracle):
            self.model.eval()
        stats = Statistics()
        sent_score_labels = []
        file_chunk_sent_scores={} ## key is filename and value is list of sentences containing sentence scores
        can_path = '%s_step%d.candidate' % (self.args.result_path, step)
        gold_path = '%s_step%d.gold' % (self.args.result_path, step)
        summaries_folder = self.args.result_path + '/predicted_summaries/'
        if not os.path.exists(summaries_folder):
            os.makedirs(summaries_folder)
        sent_scores_path = '%s_step%d_sent_scores.pt' % (self.args.result_path, step)
        with open(can_path, 'w') as save_pred:
            with open(gold_path, 'w') as save_gold:
                with torch.no_grad():
                    for batch in test_iter:
                        src = batch.src
                        labels = batch.src_sent_labels
                        segs = batch.segs
                        clss = batch.clss
                        mask = batch.mask_src
                        mask_cls = batch.mask_cls
                        unique_ids = batch.unique_ids
                        sentence_rhetorical_roles = batch.sentence_rhetorical_roles

                        gold = []
                        pred = []
                        ids = []

                        if (cal_lead):
                            selected_ids = [list(range(batch.clss.size(1)))] * batch.batch_size
                        elif (cal_oracle):
                            selected_ids = [[j for j in range(batch.clss.size(1)) if labels[i][j] == 1] for i in
                                            range(batch.batch_size)]
                        else:
                            sent_scores, mask = self.model(src, segs, clss, mask, mask_cls,sentence_rhetorical_roles)

                            loss = self.loss(sent_scores, labels.float())
                            loss = (loss * mask.float()).sum()
                            batch_stats = Statistics(float(loss.cpu().data.numpy()), len(labels))
                            stats.update(batch_stats)

                            sent_scores = sent_scores.cpu().data.numpy()
                            file_name,chunk_id = batch.unique_ids[0].split('___')
                            chunk_id = int(chunk_id)
                            src_labels = list(labels.cpu().numpy()[0])
                            if type(sent_scores[0]) == np.float32:
                                sent_scores = np.array([sent_scores])
                            sent_scores_list = list(sent_scores[0])
                            sent_rhetorical_roles_list = list(sentence_rhetorical_roles.cpu().data.numpy()[0])
                            for sent_id,(sent_txt,sent_label,sent_score,sent_rhet_role) in enumerate(zip(batch.src_str[0],src_labels,sent_scores_list,sent_rhetorical_roles_list)):
                                if file_chunk_sent_scores.get(file_name) is None:
                                    file_chunk_sent_scores[file_name]=[]
                                sent_dict = {'file_name':file_name,'chunk_id':chunk_id,'sent_id':sent_id,'sent_txt':sent_txt,
                                             'sent_score':sent_score,'sent_label':sent_label,'sent_rhetorical_role':sent_rhet_role}
                                file_chunk_sent_scores[file_name].append(sent_dict)

        lawbriefs_summary_map = {1:"facts",2:"facts",3:"arguments",4:"arguments",
                                 5:"issue",6:"ANALYSIS",7:'ANALYSIS',
                                    8:'ANALYSIS',9:'ANALYSIS',10:'decision',11:'decision'}##### keys are baseline rhetorical roles and values are LawBriefs roles
        lawbriefs_categories =["PREAMBLE",'facts','issue','arguments','ratio','decision']
        predicted_categories = ['facts','issue','arguments','ANALYSIS','decision']
        additional_mandaroty_categories = ['issue','decision']

        def create_concatenated_summaries(file_chunk_sent_scores):
            #### this function accepts the sentence scores and returns predicted and gold summary texts
            predicted_summaries = []
            gold_summaries = []
            predicted_summaries_rr = []
            rr_summaries=[]
            for file_name,sent_list in file_chunk_sent_scores.items():

                if self.args.use_rhetorical_roles and self.args.seperate_summary_for_each_rr:
                    # ######## take top N sentences for each rhetorical role
                    file_rr_sents ={} ##### keys are rhetorical roles and values are dict of {'sentences':[],'token_cnt':100}
                    for sent_dict in sent_list:
                        sent_token_cnt = len(sent_dict['sent_txt'].split())
                        sent_rr= lawbriefs_summary_map[sent_dict['sent_rhetorical_role']]
                        if file_rr_sents.get(sent_rr) is None:
                            file_rr_sents[sent_rr] = {'sentences':[sent_dict],'token_cnt':sent_token_cnt}
                        else:
                            file_rr_sents[sent_rr]['sentences'].append(sent_dict)
                            file_rr_sents[sent_rr]['token_cnt']+=sent_token_cnt

                    min_token_cnt_per_rr = 50 ######## if original text for a rhetorical role is below this then it is not summarized.
                    selected_sent_list=[]
                    for rr,sentences_dict in file_rr_sents.items():
                        if sentences_dict['token_cnt']<=min_token_cnt_per_rr:
                            selected_sent_list.extend(sentences_dict['sentences'])
                        else:
                            rr_sorted_sent_list = sorted(sentences_dict['sentences'], key=lambda x: x['sent_score'], reverse=True)
                            sents_to_keep = math.ceil(self.args.summary_sent_precent * len(sentences_dict['sentences']) / 100)
                            rr_selected_sent = rr_sorted_sent_list[:sents_to_keep]
                            rr_selected_sent = sorted(rr_selected_sent, key=lambda x: (x['chunk_id'], x['sent_id']))
                            selected_sent_list.extend(rr_selected_sent)

                else:
                    ######### take top N sentences by combining all the rhetorical roles
                    sent_list = sorted(sent_list,key =lambda x: x['sent_score'],reverse=True)
                    sents_to_keep = math.ceil(self.args.summary_sent_precent * len(sent_list)/100)
                    selected_sent_list = sent_list[:sents_to_keep]
                    selected_sent_list = sorted(selected_sent_list,key = lambda x: (x['chunk_id'],x['sent_id']))

                predicted_summary='' ###### to be used  for rouge  calculation
                predicted_summary_rr = {} ## keys are rhetorical role and values are concatenated sentences
                ## create predicted summary
                rhetorical_roles_in_test = list(set([lawbriefs_summary_map[i['sent_rhetorical_role']] for i in selected_sent_list if i['sent_label']==1]))
                for sent_dict in selected_sent_list:
                    if self.args.rogue_exclude_roles_not_in_test and self.args.use_rhetorical_roles:
                        #########  if the selected sentence has rhetorical role not in test then exclude it
                        if lawbriefs_summary_map[sent_dict['sent_rhetorical_role']] in rhetorical_roles_in_test:
                            if predicted_summary=='':
                                predicted_summary = sent_dict['sent_txt']
                            else:
                                predicted_summary = predicted_summary + '\n' + sent_dict['sent_txt']
                    else:
                        if predicted_summary == '':
                            predicted_summary = sent_dict['sent_txt']
                        else:
                            predicted_summary = predicted_summary + '\n' + sent_dict['sent_txt']

                    sent_lawbriefs_role= lawbriefs_summary_map[sent_dict['sent_rhetorical_role']]
                    if predicted_summary_rr.get(sent_lawbriefs_role) is None:
                        predicted_summary_rr[sent_lawbriefs_role] = sent_dict['sent_txt']
                    else:
                        predicted_summary_rr[sent_lawbriefs_role] = predicted_summary_rr[sent_lawbriefs_role] + '\n' +sent_dict['sent_txt']

                ######## copy the additional mandatory roles to summary
                if self.args.use_rhetorical_roles and self.args.add_additional_mandatory_roles_to_summary and not self.args.seperate_summary_for_each_rr :
                    sent_list = sorted(sent_list,key = lambda x: (x['chunk_id'],x['sent_id']))
                    for category in additional_mandaroty_categories:
                        category_sentences = [i for i in sent_list if lawbriefs_summary_map[i['sent_rhetorical_role']]==category]
                        if category_sentences:
                            if predicted_summary_rr.get(category) is not None:
                                ###### remove the category as it may not have all the sentences.
                                predicted_summary_rr.pop(category)
                            for cat_sent in category_sentences:
                                if predicted_summary_rr.get(category) is None:
                                    predicted_summary_rr[category] = cat_sent['sent_txt']
                                else:
                                    predicted_summary_rr[category] = predicted_summary_rr[category] + '\n' + \
                                                                                cat_sent['sent_txt']

                predicted_summaries.append(predicted_summary)

                ## create gold summary
                gold_summary = ''
                gold_summary_rr = {}
                sent_list = sorted(sent_list, key=lambda x: (x['chunk_id'],x['sent_id']))
                for sent_dict in sent_list:
                    if sent_dict['sent_label']==1:
                        if gold_summary=='':
                            gold_summary = sent_dict['sent_txt']
                        else:
                            gold_summary = gold_summary + '\n' + sent_dict['sent_txt']

                        sent_lawbriefs_role = lawbriefs_summary_map[sent_dict['sent_rhetorical_role']]
                        if gold_summary_rr.get(sent_lawbriefs_role) is None:
                            gold_summary_rr[sent_lawbriefs_role] = sent_dict['sent_txt']
                        else:
                            gold_summary_rr[sent_lawbriefs_role] = gold_summary_rr[sent_lawbriefs_role] + '\n' + sent_dict['sent_txt']
                gold_summaries.append(gold_summary)

                rr_summaries.append({'predicted_summary':predicted_summary_rr , 'gold_summary':gold_summary_rr ,'file_name':file_name})
            return predicted_summaries,gold_summaries,rr_summaries

        torch.save(sent_score_labels, sent_scores_path)

        final_candidate,final_gold,rr_summaries = create_concatenated_summaries(file_chunk_sent_scores)

        print('********* Writing summaries to ', summaries_folder)

        ##### try to read original summaries

        original_summaries_path = os.path.split(self.args.bert_data_path)[0]+'/lawbriefs_summary_fulltext_map.json'

        if os.path.exists(original_summaries_path):
            original_summaries = json.load(open(original_summaries_path))
        else:
            original_summaries=[]

        with open(summaries_folder + 'predicted_summaries.json', 'w') as file:
            json.dump(rr_summaries,file)
        for pred_summary in rr_summaries:
            summary_txt=''
            if original_summaries:
                summary_txt = summary_txt + '\n-------------- PREAMBLE --------------------------------\n'
                for original_summary in original_summaries:
                    if original_summary['file_name'] == pred_summary['file_name']:
                        if original_summary.get('PREAMBLE') is not None:
                            summary_txt = summary_txt + original_summary['PREAMBLE'] + '\n\n'
                        break

            summary_txt =summary_txt+'\n-------------- Predicted summary --------------------------------\n'

            for predicted_category in predicted_categories:
                if pred_summary['predicted_summary'].get(predicted_category) is not None:
                    summary_txt = summary_txt + '\n#############'+ predicted_category +' summary ################\n'
                    summary_txt = summary_txt + pred_summary['predicted_summary'].get(predicted_category) + '\n\n'

            summary_txt =summary_txt + '\n-------------- LawBrief mapped summary --------------------------------\n'
            for rr_lawbrief,rr_text in pred_summary['gold_summary'].items():
                summary_txt = summary_txt + '\n#############'+ rr_lawbrief +' summary ################\n'
                summary_txt = summary_txt + rr_text + '\n\n'

            with open(summaries_folder+os.path.splitext(pred_summary['file_name'])[0]+'.txt', 'w') as file:
                file.write(summary_txt.encode("ascii", "ignore").decode())

        def rouge(label_sent: list, pred_sent: list) -> dict:
            """Approximate ROUGE scores, always run externally for final scores."""
            scorer = rouge_scorer.RougeScorer(
                ["rouge1", "rouge2", "rougeLsum"],
                use_stemmer=True)
            r1, r2, rl = 0.0, 0.0, 0.0
            for ls, ps in tqdm(zip(label_sent, pred_sent), desc=f"Calculating scores on {len(label_sent)} labels",
                               total=len(label_sent)):
                score = scorer.score(ls, ps)
                r1 += score["rouge1"].fmeasure
                r2 += score["rouge2"].fmeasure
                rl += score["rougeLsum"].fmeasure
            result = {"eval_rouge-1": r1 / len(label_sent),
                      "eval_rouge-2": r2 / len(label_sent),
                      "eval_rouge-L": rl / len(label_sent)}
            return result

        if (step != -1 and self.args.report_rouge):
            # rouges = test_rouge(self.args.temp_dir, can_path, gold_path)
            rouges = rouge(final_gold,final_candidate)
            for key in rouges.keys():
                logger.info(f'{key}:{rouges[key]}')
        self._report_step(0, step, valid_stats=stats)

        return stats

    def _gradient_accumulation(self, true_batchs, normalization, total_stats,
                               report_stats):
        if self.grad_accum_count > 1:
            self.model.zero_grad()

        for batch in true_batchs:
            if self.grad_accum_count == 1:
                self.model.zero_grad()

            src = batch.src
            labels = batch.src_sent_labels
            segs = batch.segs
            clss = batch.clss
            mask = batch.mask_src
            mask_cls = batch.mask_cls
            sentence_rhetorical_roles = batch.sentence_rhetorical_roles

            sent_scores, mask = self.model(src, segs, clss, mask, mask_cls,sentence_rhetorical_roles)

            loss = self.loss(sent_scores, labels.float())
            loss = (loss * mask.float()).sum()
            (loss / loss.numel()).backward()
            # loss.div(float(normalization)).backward()

            batch_stats = Statistics(float(loss.cpu().data.numpy()), normalization)

            total_stats.update(batch_stats)
            report_stats.update(batch_stats)

            # 4. Update the parameters and statistics.
            if self.grad_accum_count == 1:
                # Multi GPU gradient gather
                if self.n_gpu > 1:
                    grads = [p.grad.data for p in self.model.parameters()
                             if p.requires_grad
                             and p.grad is not None]
                    distributed.all_reduce_and_rescale_tensors(
                        grads, float(1))
                self.optim.step()

        # in case of multi step gradient accumulation,
        # update only after accum batches
        if self.grad_accum_count > 1:
            if self.n_gpu > 1:
                grads = [p.grad.data for p in self.model.parameters()
                         if p.requires_grad
                         and p.grad is not None]
                distributed.all_reduce_and_rescale_tensors(
                    grads, float(1))
            self.optim.step()

    def _save(self, step):
        real_model = self.model
        # real_generator = (self.generator.module
        #                   if isinstance(self.generator, torch.nn.DataParallel)
        #                   else self.generator)

        model_state_dict = real_model.state_dict()
        # generator_state_dict = real_generator.state_dict()
        checkpoint = {
            'model': model_state_dict,
            # 'generator': generator_state_dict,
            'opt': self.args,
            'optim': self.optim,
        }
        checkpoint_path = os.path.join(self.args.model_path, 'model_step_%d.pt' % step)
        logger.info("Saving checkpoint %s" % checkpoint_path)
        # checkpoint_path = '%s_step_%d.pt' % (FLAGS.model_path, step)
        if (not os.path.exists(checkpoint_path)):
            torch.save(checkpoint, checkpoint_path)
            return checkpoint, checkpoint_path

    def _start_report_manager(self, start_time=None):
        """
        Simple function to start report manager (if any)
        """
        if self.report_manager is not None:
            if start_time is None:
                self.report_manager.start()
            else:
                self.report_manager.start_time = start_time

    def _maybe_gather_stats(self, stat):
        """
        Gather statistics in multi-processes cases

        Args:
            stat(:obj:onmt.utils.Statistics): a Statistics object to gather
                or None (it returns None in this case)

        Returns:
            stat: the updated (or unchanged) stat object
        """
        if stat is not None and self.n_gpu > 1:
            return Statistics.all_gather_stats(stat)
        return stat

    def _maybe_report_training(self, step, num_steps, learning_rate,
                               report_stats):
        """
        Simple function to report training stats (if report_manager is set)
        see `onmt.utils.ReportManagerBase.report_training` for doc
        """
        if self.report_manager is not None:
            return self.report_manager.report_training(
                step, num_steps, learning_rate, report_stats,
                multigpu=self.n_gpu > 1)

    def _report_step(self, learning_rate, step, train_stats=None,
                     valid_stats=None):
        """
        Simple function to report stats (if report_manager is set)
        see `onmt.utils.ReportManagerBase.report_step` for doc
        """
        if self.report_manager is not None:
            return self.report_manager.report_step(
                learning_rate, step, train_stats=train_stats,
                valid_stats=valid_stats)

    def _maybe_save(self, step):
        """
        Save the model if a model saver is set
        """
        if self.model_saver is not None:
            self.model_saver.maybe_save(step)


# Write your solution here
import json
def print_persons(filename: str):
    with open(filename) as my_file:
        data = my_file.read()

    courses = json.loads(data)

    for course in courses:
        hobbies = ", ".join(course['hobbies'])
        print(f"{course['name']} {course['age']} years ({hobbies})")

#OFFICIAL SOLUTION:
# import json
# def print_persons(filename: str):
#     with open(filename) as f:
#         content = f.read()
#     persons = json.loads(content)
#     for person in persons:
#         name = person['name']
#         age = person['age']
#         hobbies = ", ".join(person['hobbies'])
#         print(f"{name} {age} years ({hobbies})")


"""
.. py:module:: nx
    :platform: Unix

Functions to create agent connections from
`NetworkX <https://networkx.github.io/>`_ graph structures and NetworkX graphs
from agent connections.

.. note::

    NetworkX has to be installed in order for the functions in this module to
    work. It is not installed as a default dependency.

    Use ``pip install creamas[extras]`` to install extra requirements, including NetworkX.

"""
from networkx import Graph, DiGraph

from creamas.util import sort_addrs

def connections_from_graph(env, G, edge_data=False):
    """Create connections for agents in the given environment from the given
    NetworkX graph structure.

    :param env:
        Environment where the agents live. The environment should be derived
        from :class:`~creamas.core.environment.Environment`,
        :class:`~creamas.mp.MultiEnvironment` or
        :class:`~creamas.ds.DistributedEnvironment`.

    :param G:
        NetworkX graph structure, either :class:`networkx.graph.Graph` or
        :class:`networkx.digraph.DiGraph`. The graph needs to have the same
        number of nodes as the environment has agents (excluding the managers).

    :param bool edge_data:
        If ``True``, edge data from the given graph is copied to the agents'
        :attr:`connections`.

    .. note::

        By design, manager agents are excluded from the connections and should
        not be counted towards environment's agent count.

    The created connections are stored in each agent's
    :attr:`~creamas.core.agent.CreativeAgent.connections` and the possible
    edge data is stored as key-value pairs in the connection dictionary.

    The agents are sorted by their environments' hosts and ports before each
    agent is mapped to a node in **G**. This should cause some network
    generation methods in NetworkX, e.g.
    :func:`~networkx.generators.random_graphs.connected_watts_strogatz_graph`,
    to create more connections between agents in the same environment and/or
    node when using :class:`~creamas.mp.MultiEnvironment` or
    :class:`~creamas.ds.DistributedEnvironment`.
    """
    if not issubclass(G.__class__, (Graph, DiGraph)):
        raise TypeError("Graph structure must be derived from Networkx's "
                        "Graph or DiGraph.")
    if not hasattr(env, 'get_agents'):
        raise TypeError("Parameter 'env' must have get_agents.")

    addrs = env.get_agents(addr=True)
    if len(addrs) != len(G):
        raise ValueError("The number of graph nodes and agents in the "
                         "environment (excluding the manager agent) must "
                         "match. Now got {} nodes and {} agents."
                         .format(len(G), len(addrs)))
    # Sort agent addresses to the order they were added to the environment.
    addrs = sort_addrs(addrs)
    _addrs2nodes(addrs, G)
    conn_map = _edges2conns(G, edge_data)
    env.create_connections(conn_map)

def graph_from_connections(env, directed=False):
    """Create NetworkX graph from agent connections in a given environment.

    :param env:
        Environment where the agents live. The environment must be derived from
        :class:`~creamas.core.environment.Environment`,
        :class:`~creamas.mp.MultiEnvironment` or
        :class:`~creamas.ds.DistributedEnvironment`.

    :param bool directed:
        If ``True``, creates an instance of :class:`~networkx.digraph.DiGraph`,
        otherwise creates an instance of :class:`~networkx.graph.Graph`.

    :returns: The created NetworkX graph.
    :rtype:
        :class:`~networkx.digraph.DiGraph` or :class:`~networkx.graph.Graph`

    .. note::

        If the created graph is undirected and two connected agents have
        different data stored for each other, then the data for the given edge
        is chosen randomly between the two agents.
    """
    G = DiGraph() if directed else Graph()
    conn_list = env.get_connections(data=True)
    for agent, conns in conn_list:
        G.add_node(agent)
        ebunch = []
        for nb, data in conns.items():
            ebunch.append((agent, nb, data))
        if len(ebunch) > 0:
            G.add_edges_from(ebunch)
    return G

def _addrs2nodes(addrs, G):
    """Map agent addresses to nodes in the graph.
    """
    for i, n in enumerate(G.nodes(data=True)):
        n[1]['addr'] = addrs[i]

def _edges2conns(G, edge_data=False):
    """Create a mapping from graph edges to agent connections to be created.

    :param G:
        NetworkX's Graph or DiGraph which has :attr:`addr` attribute for each
        node.

    :param bool edge_data:
        If ``True``, stores also edge data to the returned dictionary.

    :returns:
        A dictionary where keys are agent addresses and values are lists of
        addresses to which key-agent should create connections in order to
        recreate the graph structure in an agent society.

    :rtype: dict
    """
    cm = {}
    for n in G.nodes(data=True):
        if edge_data:
            cm[n[1]['addr']] = [(G.nodes[nb]['addr'], G[n[0]][nb])
                                for nb in G[n[0]]]
        else:
            cm[n[1]['addr']] = [(G.nodes[nb]['addr'], {}) for nb in G[n[0]]]
    return cm


from typing import Any, Dict, List, Type, TypeVar

import attr

T = TypeVar("T", bound="WorkflowStepAddForm")

@attr.s(auto_attribs=True)
class WorkflowStepAddForm:
    """
    Attributes:
        scheme_id (str):
        step_name (str):
        enable_escalation (bool):
        escalation_action (str):
        escalation_time (str):
        step_resolved (bool):
    """

    scheme_id: str
    step_name: str
    enable_escalation: bool
    escalation_action: str
    escalation_time: str
    step_resolved: bool
    additional_properties: Dict[str, Any] = attr.ib(init=False, factory=dict)

    def to_dict(self) -> Dict[str, Any]:
        scheme_id = self.scheme_id
        step_name = self.step_name
        enable_escalation = self.enable_escalation
        escalation_action = self.escalation_action
        escalation_time = self.escalation_time
        step_resolved = self.step_resolved

        field_dict: Dict[str, Any] = {}
        field_dict.update(self.additional_properties)
        field_dict.update(
            {
                "schemeId": scheme_id,
                "stepName": step_name,
                "enableEscalation": enable_escalation,
                "escalationAction": escalation_action,
                "escalationTime": escalation_time,
                "stepResolved": step_resolved,
            }
        )

        return field_dict

    @classmethod
    def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
        d = src_dict.copy()
        scheme_id = d.pop("schemeId")

        step_name = d.pop("stepName")

        enable_escalation = d.pop("enableEscalation")

        escalation_action = d.pop("escalationAction")

        escalation_time = d.pop("escalationTime")

        step_resolved = d.pop("stepResolved")

        workflow_step_add_form = cls(
            scheme_id=scheme_id,
            step_name=step_name,
            enable_escalation=enable_escalation,
            escalation_action=escalation_action,
            escalation_time=escalation_time,
            step_resolved=step_resolved,
        )

        workflow_step_add_form.additional_properties = d
        return workflow_step_add_form

    @property
    def additional_keys(self) -> List[str]:
        return list(self.additional_properties.keys())

    def __getitem__(self, key: str) -> Any:
        return self.additional_properties[key]

    def __setitem__(self, key: str, value: Any) -> None:
        self.additional_properties[key] = value

    def __delitem__(self, key: str) -> None:
        del self.additional_properties[key]

    def __contains__(self, key: str) -> bool:
        return key in self.additional_properties


from prophet import Prophet
from prophet.serialize import model_to_json, model_from_json

from sklearn.metrics import mean_squared_error

import pandas as pd
import numpy as np
import itertools
import datetime

class ProphetModel:
  models = {}

  def __init__(self, df, target:str, forecast_size:int=18):
    self.df = df
    self.target = target
    self.forecast_size = forecast_size

  def _model_tuning_and_train(self, interval_width=0.95, weekly_seasonality=True, save_path='prophet_model.json'):
    self.prophet = Prophet(interval_width=interval_width, weekly_seasonality=weekly_seasonality)
    prophet_target = pd.DataFrame(zip(list(self.df.index),list(self.df[self.target])),columns=['ds','y'])
    self.prophet.fit(prophet_target)
    forecast = self.prophet.make_future_dataframe(periods=self.forecast_size)
    forecast_target = forecast.copy()
    self.predictions = self.prophet.predict(forecast_target)
    score = np.sqrt(mean_squared_error(self.df[self.target], self.predictions['yhat'].head(self.df.shape[0])))

    ProphetModel.models['prophet'] = {
        'score':score,
        'model':self.prophet
    }
    # with open('saved_models/'+save_path, 'w') as f:
    #   f.write(model_to_json(self.prophet))

    return ProphetModel.models['prophet']

  def train(self):
    self._model_tuning_and_train()

  def data_predictions(self, start):
    preds = self.predictions[self.predictions['ds']>str(start)]
    # print(preds)
    return list(preds['yhat']), list(preds['ds'])

  def plot(self):
    print(self.prophet.plot(self.predictions))

"""
AN EVEN BETTER MODEL WITH HIGHER ACCURACY
"""

model_columns = ['date','new_cases','positive_rate',
                   'tests_per_case','male_smokers','female_smokers',
                   'new_vaccinations','handwashing_facilities',
                   'life_expectancy','new_tests','reproduction_rate'
                   ]
param_grid = {
    'changepoint_prior_scale': [0.001, 0.01, 0.1, 0.5],
    'seasonality_prior_scale': [0.01, 0.1, 1.0, 10.0]
}
all_params = [dict(zip(param_grid.keys(), v)) for v in itertools.product(*param_grid.values())]

def prophet_model(dat, target='new_cases', period=18):
  dat = dat[model_columns]
  dat.rename(columns={target:'y','date':'ds'}, inplace=True)

  for params in all_params:
    p = Prophet(interval_width=0.95, weekly_seasonality=True, **params)

  model_columns.remove('date')
  model_columns.remove('new_cases')

  for i in model_columns:
    p.add_regressor(i)

  p.fit(dat)
  # print(p)

  # prepping future values
  today = datetime.date.today()
  # print(today)
  period = period+1
  date_list = [str(today + datetime.timedelta(days=x)) for x in range(period)]
  date_list.remove(str(today))
  period = period-1
  temp = {}
  for i in model_columns:
    temp_ = [dat[i].mean()]*period
    temp[i] = temp_
  dummy_cases = [0]*period
  temp['ds'] = date_list
  temp['y'] = dummy_cases
  future_data = pd.DataFrame(temp)

  some = pd.concat([dat, future_data])
  # print(some)

  forecast_data = p.predict(some)
  return list(forecast_data['yhat']), [str(i.date()) for i in forecast_data['ds']]


# -*- coding: utf-8 -*-

import requests
import json

class AdminClient(object):

    def __init__(self, url, token):
        self.headers = {'Authorization': 'Bearer ' + token, 'Content-Type': 'application/x-www-form-urlencoded'}
        self.url = url.__add__('/api/admin')

    def createWebconfig(self, data):
        requestUrl = self.url.__add__('/webconfig/setting')
        print("requestUrl = " + requestUrl)
        print("data = " + data)
        response = requests.put(url=requestUrl,data=data,headers=self.headers)
        print(response.status_code)
        print(response.text)


from telethon import events, Button
import yt_dlp
from utils import Utils
from pathlib import Path

@events.register(events.NewMessage(pattern="/yt"))
async def yt_download(event):
    bot = event.client
    response = await bot.send_message(
        event.chat, message=" 📥 (testing purpose) Downloading... 📥"
    )
    msg = " ".join(event.text.split()[1:])
    if not len(msg):
        return await bot.edit_message(
            event.chat,
            message=response,
            text="❗Write the name of the song after the command❗\n"
            "(example: /yt despacito)",
        )
    url = Utils.get_url(msg)
    if "error" in msg:
        return await bot.edit_message(
            event.chat, message=response, text=f"{url}\n❌ An error occured ❌"
        )
    yt_dlp.YoutubeDL(Utils.ydl_opts).download([url])
    # get a list of all the files in tmp_song, and takes only the first one
    # Spoiler: there is only one file in it because yt_dl download
    # changes some characters sometimes (idk how it works)
    file_path = list(Path("tmp_song").rglob("*"))
    await bot.send_message(
        event.chat,
        file=file_path[0],
        buttons=bot.build_reply_markup(Button.url("🔗 YT link 🔗", url=url)),
    )
    await response.delete()
    [file.unlink() for file in file_path]  # clear ./tmp_song (debug reason)
    return


import numpy as np
import matplotlib.pyplot as plt

# function to create the square pulse
def square(t , T):
    f = np.logical_and( t <= T / 2.0 , t >= -T/2.0 )
    return f.astype('float')

N = 1024
n = np.arange(-N / 2.0, N / 2.0, 1)

# time axis
T = 4;
Tmax = 30.0
Dt = 2 * Tmax / N
t = n * Dt

# frequency axis
Df = 1.0 / (N * Dt)
f = n * Df

# signal in the time domain
x = square(t,T)

# signal in the frequency domain
X = Dt * np.fft.fftshift( np.fft.fft( np.fft.fftshift(x) ) )

# Analytical expression for the spectrum
X2= T * np.sinc( f * T )

# Plot results
plt.figure(1)
plt.xlabel('t [msec]')
plt.ylabel('x(t)')
plt.plot(t,x)

plt.figure(2)
plt.plot(t, X2, t, X, 'o')
plt.xlim(-3,3)
plt.legend(['analytical','numerical'])
plt.xlabel('f [kHz]')
plt.ylabel('X(f)')


from cv2 import imread, imwrite, equalizeHist, adaptiveThreshold, waitKey
from cv2 import resize, imshow, IMREAD_GRAYSCALE, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY
import argparse

def get_args():
    parser = argparse.ArgumentParser()
    parser.add_argument("-i", "--image_path", type=str, help="path to the image")
    parser.add_argument("-d", "--display", type=int, default=1,
                        help="whether showing the image after processing. (1 / 0). default is true. (optional)")
    parser.add_argument("-c", "--downsample", type=float,
                        help="the factor you want to shrink resolution of output. (optional)")
    return parser.parse_args()

def histeq(pic_path):
    image = imread(pic_path, IMREAD_GRAYSCALE)
    return equalizeHist(image)

def adaptive_thresholding(image, block_size, C):
    return adaptiveThreshold(image, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, block_size, C)

if __name__ == "__main__":
    args = get_args()
    histed_image = histeq(args.image_path)
    thresed_image = adaptive_thresholding(histed_image, 35, 20)
    if args.display:
        imshow("thresed_image", thresed_image)
        waitKey()

    if args.downsample:
        thresed_image = resize(thresed_image, (int(thresed_image.shape[1] // args.downsample),
                                   int(thresed_image.shape[0] // args.downsample)))
    imwrite("{}_1.jpg".format(args.image_path.split('.')[0]), thresed_image)


from epsilon.extime import Time

from axiom.item import Item
from axiom.attributes import text, reference, integer, AND, timestamp

class Tag(Item):
    typeName = 'tag'
    schemaVersion = 1

    name = text(doc="""
    The short string which is being applied as a tag to an Item.
    """)

    created = timestamp(doc="""
    When this tag was applied to the Item to which it applies.
    """)

    object = reference(doc="""
    The Item to which this tag applies.
    """)

    catalog = reference(doc="""
    The L{Catalog} item in which this tag was created.
    """)

    tagger = reference(doc="""
    An optional reference to the Item which is responsible for this tag's
    existence.
    """)

class _TagName(Item):
    """
    Helper class to make Catalog.tagNames very fast.  One of these is created
    for each distinct tag name that is created.  _TagName Items are never
    deleted from the database.
    """
    typeName = 'tagname'

    name = text(doc="""
    The short string which uniquely represents this tag.
    """, indexed=True)

    catalog = reference(doc="""
    The L{Catalog} item in which this tag exists.
    """)

class Catalog(Item):

    typeName = 'tag_catalog'
    schemaVersion = 2

    tagCount = integer(default=0)

    def tag(self, obj, tagName, tagger=None):
        """
        """
        # check to see if that tag exists.  Put the object attribute first,
        # since each object should only have a handful of tags and the object
        # reference is indexed.  As long as this is the case, it doesn't matter
        # whether the name or catalog attributes are indexed because selecting
        # from a small set of results is fast even without an index.
        if self.store.findFirst(Tag,
                                AND(Tag.object == obj,
                                    Tag.name == tagName,
                                    Tag.catalog == self)):
            return

        # if the tag doesn't exist, maybe we need to create a new tagname object
        self.store.findOrCreate(_TagName, name=tagName, catalog=self)

        # Increment only if we are creating a new tag
        self.tagCount += 1
        Tag(store=self.store, object=obj,
            name=tagName, catalog=self,
            created=Time(), tagger=tagger)

    def tagNames(self):
        """
        Return an iterator of unicode strings - the unique tag names which have
        been applied objects in this catalog.
        """
        return self.store.query(_TagName, _TagName.catalog == self).getColumn("name")

    def tagsOf(self, obj):
        """
        Return an iterator of unicode strings - the tag names which apply to
        the given object.
        """
        return self.store.query(
            Tag,
            AND(Tag.catalog == self,
                Tag.object == obj)).getColumn("name")

    def objectsIn(self, tagName):
        return self.store.query(
            Tag,
            AND(Tag.catalog == self,
                Tag.name == tagName)).getColumn("object")

def upgradeCatalog1to2(oldCatalog):
    """
    Create _TagName instances which version 2 of Catalog automatically creates
    for use in determining the tagNames result, but which version 1 of Catalog
    did not create.
    """
    newCatalog = oldCatalog.upgradeVersion('tag_catalog', 1, 2,
                                           tagCount=oldCatalog.tagCount)
    tags = newCatalog.store.query(Tag, Tag.catalog == newCatalog)
    tagNames = tags.getColumn("name").distinct()
    for t in tagNames:
        _TagName(store=newCatalog.store, catalog=newCatalog, name=t)
    return newCatalog

from axiom.upgrade import registerUpgrader
registerUpgrader(upgradeCatalog1to2, 'tag_catalog', 1, 2)


import sys
from collections import deque
input = sys.stdin.readline

def bfs(v):
    q = deque()
    q.append(v)
    dist = [-1 for _ in range(n+1)]
    dist[v] = 0
    while q:
        curr = q.popleft()
        for node, cost in graph[curr]:
            if dist[node] == -1:
                dist[node] = dist[curr] + cost
                q.append(node)

    return dist

n = int(input())
graph = [[] for _ in range(n+1)]
for _ in range(n-1):
    a, b, c = map(int, input().split())
    graph[a].append([b, c])
    graph[b].append([a, c])

rtn = bfs(1)
rtn = bfs(rtn.index(max(rtn)))
print(max(rtn))


import sys
import os
import subprocess
import ftplib
import platform
import time
from datetime import datetime

from .Configuration import Configuration
from .ConnectionSSH import ConnectionSSH
from .extras import Debug
from .extras import createFile, readFile

class Connection():

	@staticmethod
	def start(file_local = '', file_server = '', path = '', permissions = '', command = ''):
		Debug.print("=========================================================================")
		Debug.print("COMMAND: command = {} - file_local = {} - file_server = {} - path = {} - permissions = {}".format(command, file_local, file_server, path, permissions))
		Debug.print("DATE: ", datetime.now().strftime("%d-%m-%Y %H:%M:%S"))
		Debug.print("=========================================================================")
		salida = ""
		if Configuration.tipo == "sftp":
			if platform.system() == "Linux" or platform.system() == 'Darwin':
				salida = Connection._sftpLinux(file_local=file_local, file_server=file_server, path=path, permissions=permissions, command=command)
			elif platform.system() == "Windows" or platform.system() == 'win32' or platform.system() == 'win64':
				salida = Connection._sftpWindows(file_local=file_local, file_server=file_server, path=path, permissions=permissions, command=command)
		else:
			salida = Connection._ftp(file_local=file_local, file_server=file_server, path=path, permissions=permissions, command=command)
		Debug.print("\nSALIDA: ", str(salida))
		Debug.print("**************** END COMMAND - DATE: " + datetime.now().strftime("%d-%m-%Y %H:%M:%S") + " ****************\n")
		return salida

	@staticmethod
	def ls():
		return Connection.start(command='ls')

	@staticmethod
	def get(file_server, file_local):
		return Connection.start(file_server='"{}"'.format(file_server), file_local='"{}"'.format(file_local), command='get')

	@staticmethod
	def put(file_local, file_server):
		return Connection.start(file_local='"{}"'.format(file_local), file_server='"{}"'.format(file_server), command='put')

	@staticmethod
	def mkdir(path):
		return Connection.start(path='"{}"'.format(path), command='mkdir')

	@staticmethod
	def rm(path, is_file):
		return Connection.start(path='"{}"'.format(path), command=('del' if is_file else 'rmdir'))

	@staticmethod
	def mv(file_local, file_server):
		return Connection.start(file_local='"{}"'.format(file_local), file_server='"{}"'.format(file_server), command='mv')

	@staticmethod
	def chmod(path, permissions):
		return Connection.start(path='"{}"'.format(path), permissions=permissions, command='chmod')

	@staticmethod
	def _sftpLinux(file_local = '', file_server = '', path = '', permissions = '', command = ''):
		if command == "ls":
			retorno = ConnectionSSH.ssh_exec(["ssh"," && ls -lrtahF | sed '/\.\//d' | sed '/\.\.\//d'"], True)
		elif command == "get":
			retorno = ConnectionSSH.ssh_exec(["sftp", "\nget " + file_server + " " + file_local], True)
		elif command == "put":
			retorno = ConnectionSSH.ssh_exec(["sftp", "\nput " + file_local + " " + file_server], True)
		elif command == "chmod":
			retorno = ConnectionSSH.ssh_exec(["sftp", "\nchmod " + permissions + " " + path], True)
		elif command == "mv":
			retorno = ConnectionSSH.ssh_exec(["sftp", "\nrename " + file_local + " " + file_server], True)
		elif command == "rmdir":
			retorno = ConnectionSSH.ssh_exec(["sftp", "\nrmdir " + path], True)
		elif command == "del":
			retorno = ConnectionSSH.ssh_exec(["sftp", "\nrm " + path], True)
		elif command == "mkdir":
			retorno = ConnectionSSH.ssh_exec(["sftp", "\nmkdir " + path], True)
		else:
			Debug.print("Comando incorrecto")

		Debug.print("\n".join(retorno[2]))
		salida = str( retorno[1].decode('utf-8', 'ignore') )

		return salida.strip(" \r\n")

	@staticmethod
	def _sftpWindows(file_local = '', file_server = '', path = '', permissions = '', command = ''):
		salida = None
		command_line = ''
		if command == 'ls':
			command_line = 'ls\n'
		elif command == 'get':
			command_line = 'get {} {}'.format(file_server, file_local)
		elif command == 'put':
			command_line = 'put {} {}'.format(file_local, file_server)
		elif command == 'mkdir':
			command_line = 'mkdir {}'.format(path)
		elif command == 'rmdir' or command == 'del':
			command_line = "{} {}".format(command, path)
		elif command == 'mv':
			command_line = 'mv {} {}'.format(file_local, file_server)
		elif command == 'chmod':
			command_line = 'chmod {} {}'.format(permissions, path)

		if command_line != '':
			command_line = "cd \"{}\"\n{}".format(Configuration.currentPath, command_line)

		try:
			createFile( os.path.join(Configuration.psftp_path, "script.bat"), command_line)
			unix_time = str(time.time())
			unix_time = unix_time.replace('.', '')
			dataout_name = 'data_' + unix_time + '.out'
			f = open(os.path.join(Configuration.psftp_path, dataout_name), "w")
			executable = 'psftp64.exe' if platform.architecture()[0] == '64bit' else 'psftp32.exe'
			proceso = subprocess.Popen(['echo', 'n', '|', os.path.join(Configuration.package_path, 'bin', executable), "-P", Configuration.puerto, "-pw", Configuration.password, "-b" , os.path.join(Configuration.psftp_path, 'script.bat'), Configuration.usuario + "@" + Configuration.host], stdout=f, stderr=subprocess.PIPE, shell=True)
			proceso.wait(8)
			f.close()
			errores = proceso.stderr.read()
			if proceso.stdout == None:
				with open(os.path.join(Configuration.psftp_path, dataout_name), "r") as content_file:
					content = content_file.read()
				salida = content
				time.sleep(1)
				try:
					os.remove(os.path.join(Configuration.psftp_path, dataout_name))
				except Exception as e:
					Debug.print(str(e))
					raise e
			else:
				salida = proceso.stdout.read()
				proceso.stdout.close()
				salida = salida.decode(sys.getdefaultencoding())

			proceso.stderr.close()
			errores = errores.decode(sys.getdefaultencoding())
			Debug.print("Errores: ", errores);
		except Exception as e:
			if str(e).find('TimeOutExpired') != -1:
				salida = "TimeOutExpired."
			elif str(e).find('PermissionError: [WinError 32]') != -1:
				salida = 'PermissionError: [WinError 32]'
			elif str(e).find('timed out after 8 seconds') != -1:
				return "TimeOutExpired."
			raise e

		if salida.find('Network error: Connection timed out') != -1:
			return 'Network error: Connection timed out'
		if salida.find('permission denied') != -1:
			return 'permission denied'
		if salida.find('no such file or directory') != -1:
			return 'no such file or directory'
		if errores.find('Fatal: Network error: Connection refused') != -1:
			return 'Fatal: Network error: Connection refused'
		if errores.find('Access denied') != -1:
			return 'Usuario o password incorrectos.'
		return salida if salida != '' else errores

	@staticmethod
	def _ftp(file_local = '', file_server = '', path = '', permissions = '', command = ''):
		file_local = file_local.strip('\"')
		file_server = file_server.strip('\"')
		path = path.strip('\"')
		permissions = permissions.strip('\"')
		command = command.strip('\"')
		salida = ''
		ftp = ftplib.FTP();
		try:
			ftp.connect(Configuration.host, int(Configuration.puerto))
			ftp.login(Configuration.usuario, Configuration.password)
			ftp.cwd(Configuration.currentPath)
			data = []
			if command == "ls":
				ftp.dir(data.append)
			elif command == "get":
				fhandle = open(file_local, 'wb')
				ftp.retrbinary("RETR " + file_server, fhandle.write)
				fhandle.close()
			elif command == "put":
				with open(file_local, "rb") as f:
					ftp.storlines("STOR " + file_server, f)
			elif command == "chmod":
				salida = ftp.sendcmd("SITE CHMOD " + permissions + " " + path)
			elif command == "mv":
				salida = ftp.rename(file_local, file_server)
			elif command == "rmdir":
				Connection._rm_tree(ftp, path)
			elif command == "del":
				ftp.delete(path)
			elif command == "mkdir":
				ftp.mkd(path)
				ftp.cwd(path)
				ftp.dir(data.append)
			else:
				Debug.print("Comando incorrecto")
			ftp.quit()
		except Exception as e:
			if str(e).find('Login incorrect') != -1:
				ftp.quit()
			return str(e)
			raise e

		if command == 'ls' or command == 'mkdir':
			salida = ''
			for file in data:
				salida = salida + file + "\n"
		return salida

	@staticmethod
	def _rm_tree(ftp, path):
		wd = ftp.pwd()
		try:
			names = ftp.nlst(path)
		except ftplib.all_errors as e:
			Debug.print('_rm_tree: No se puede borrar {0}: {1}'.format(path, e))
			return
		for name in names:
			if os.path.split(name)[1] in ('.', '..'): continue
			Debug.print('_rm_tree: Checking {0}'.format(name))
			try:
				ftp.cwd(name)
				ftp.cwd(wd)
				Connection._rm_tree(ftp, name)
			except ftplib.all_errors:
				ftp.delete(name)
		try:
			ftp.rmd(path)
		except ftplib.all_errors as e:
			Debug.print('_rm_tree: No se puede borrar {0}: {1}'.format(path, e))


import pygame
class State:
    def __init__(self,conteudo):
        self.record = int(conteudo)
        self.tempo_frequencia =  0
        self.tempo_acelera = 0
        self.tempo_total = 0
        self.ultimo_tempo = 0
        self.tempo_atualizar = 0
        self.tiros = []
        self.tiro_vel = 300
        self.tiros_fora_tela = 0
        # self.player_pos_x = 270
        # self.player_pos_y = 380

class Assets:
    def __init__(self):
        self.fundo = pygame.image.load('fundo.jpg')
        self.blaster = pygame.mixer.Sound('som_blaster.mp3')
        self.som_fundo = pygame.mixer.Sound('som_fundo.mp3')

class Tiro:
    def __init__(self, pos_x, pos_y, width, height, vel, imagem, window):
        self.pos_x = pos_x
        self.pos_y = pos_y
        self.width = width
        self.height = height
        self.vel = vel
        self.imagem = imagem
        self.window = window

    def atualiza_posicao(self,dt):
        self.pos_y+= self.vel * dt

    def desenha(self):
        self.window.blit(self.imagem, (self.pos_x, self.pos_y))

    def colisao_rect(self, obj):
        colisao = False
        if (obj.pos_x <= self.pos_x + self.width <= obj.pos_x + obj.width or self.pos_x <= obj.pos_x + obj.width <= self.pos_x + self.width) and (obj.pos_y <= self.pos_y + self.height <= obj.pos_y + obj.height or self.pos_y <= obj.pos_y + obj.height <= self.pos_y + self.height):
            colisao = True
        return colisao

    def saiu_tela(self):
        return self.pos_y > 480

    def apaga_tiro(self, state):
        del state.tiros[0]

    def aumenta_vel(self,state):
        state.tiro_vel+=100

class Jogador:
    def __init__(self, pos_x, pos_y, width, height, imagem, window, vira = False):
        self.pos_x = pos_x
        self.pos_y = pos_y
        self.width = width
        self.height = height
        self.imagem = imagem
        self.window = window
        self.vira = vira
        self.keys = {'right':False, 'up':False, 'left':False, 'down':False}
        self.vel_x = 0
        self.vel_y = 0

    def move(self,evento):
        #print(evento)
        if evento.key == pygame.K_d:
            self.keys['right'] = True
            self.vira = 'direita'
            self.vel_x+=1
        if evento.key == pygame.K_w:
            self.keys['up'] = True
            self.vel_y-=1
        if evento.key == pygame.K_s:
            self.keys['down'] = True
            self.vel_y+=1
        if evento.key == pygame.K_a:
            self.keys['left'] = True
            self.vira = 'esquerda'
            self.vel_x-=1

        if self.keys['right']:
            if self.pos_x < 540:
                self.pos_x+=self.vel_x
        if self.keys['up']:
            if self.pos_y > 0:
                self.pos_y+=self.vel_y
        if self.keys['down']:
            if self.pos_y < 480 - self.height:
                self.pos_y+=self.vel_y
        if self.keys['left']:
            if self.pos_x > 0:
                self.pos_x+=self.vel_x

    def nao_muda(self,evento):
        if evento.key == pygame.K_d:
            self.keys['right'] = False
            self.vira = False
            self.vel_x = 0
        if evento.key == pygame.K_w:
            self.keys['up'] = False
            self.vel_y = 0
        if evento.key == pygame.K_s:
            self.keys['down'] = False
            self.vel_y = 0
        if evento.key == pygame.K_a:
            self.keys['left'] = False
            self.vira = False
            self.vel_x = 0

    def desenha(self):
        if self.vira == 'esquerda':
            self.window.blit(pygame.transform.rotate(self.imagem, 30), (self.pos_x, self.pos_y))
        elif self.vira == 'direita':
            self.window.blit(pygame.transform.rotate(self.imagem, -30), (self.pos_x, self.pos_y))
        else:
            self.window.blit(self.imagem, (self.pos_x, self.pos_y))