import wx
from wx.lib.scrolledpanel import ScrolledPanel

import service
import gui.mainFrame
import gui.shipBrowser
import gui.globalEvents as GE

from gui import characterEditor as CharEditor

class GangView ( ScrolledPanel ):

    def __init__( self, parent ):
        ScrolledPanel.__init__ ( self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx.Size( 100,20 ), style = wx.TAB_TRAVERSAL | wx.HSCROLL | wx.VSCROLL )
        mainSizer = wx.BoxSizer( wx.VERTICAL )

        self.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
        self.mainFrame = gui.mainFrame.MainFrame.getInstance()

        self.draggedFitID = None

        help = _('''gangView_Set fit as booster to display in dropdown, or drag fitting from\nship browser to this window, or right click fit and select booster role.''')
        helpSizer = wx.BoxSizer( wx.HORIZONTAL )
        self.helpText = wx.StaticText( self, wx.ID_ANY, help, wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_CENTRE )
        helpSizer.Add( self.helpText, 1, wx.ALL, 5 )

        self.options = [_("gangView_Fleet"), _("gangView_Wing"), _("gangView_Squad")]

        self.fleet = {}
        for id, option in enumerate(self.options):

            # set content for each commander
            self.fleet[id] = {}
            self.fleet[id]['stLabel']  = wx.StaticText( self, wx.ID_ANY, self.options[id]+':', wx.DefaultPosition, wx.DefaultSize, 0 )
            self.fleet[id]['stText']   = wx.StaticText( self, wx.ID_ANY, 'None', wx.DefaultPosition, wx.DefaultSize, 0 )
            self.fleet[id]['chFit']    = wx.Choice( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, [] )
            self.fleet[id]['chChar']   = wx.Choice( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, [] )
            self.fleet[id]['fitSizer'] = wx.BoxSizer( wx.VERTICAL )

        self.FitDNDPopupMenu = self.buildBoostermenu()

        contentFGSizer = wx.FlexGridSizer( 5, 3, 0, 0 )
        contentFGSizer.AddGrowableCol( 1 )
        contentFGSizer.SetFlexibleDirection( wx.BOTH )
        contentFGSizer.SetNonFlexibleGrowMode( wx.FLEX_GROWMODE_SPECIFIED )

        ### Header
        self.stBooster = wx.StaticText( self, wx.ID_ANY, _("gangView_Booster"), wx.DefaultPosition, wx.DefaultSize, 0 )
        self.stBooster.Wrap( -1 )
        self.stBooster.SetFont( wx.Font( wx.NORMAL_FONT.GetPointSize(), 70, 90, 92, False, wx.EmptyString ) )
        contentFGSizer.Add( self.stBooster, 0, wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL, 5 )

        self.stFits = wx.StaticText( self, wx.ID_ANY, _("gangView_Fits"), wx.DefaultPosition, wx.DefaultSize, 0 )
        self.stFits.Wrap( -1 )
        self.stFits.SetFont( wx.Font( wx.NORMAL_FONT.GetPointSize(), 70, 90, 92, False, wx.EmptyString ) )
        contentFGSizer.Add( self.stFits, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTER_HORIZONTAL, 5 )

        self.stCharacters = wx.StaticText( self, wx.ID_ANY, _("gangView_Characters"), wx.DefaultPosition, wx.DefaultSize, 0 )
        self.stCharacters.Wrap( -1 )
        self.stCharacters.SetFont( wx.Font( wx.NORMAL_FONT.GetPointSize(), 70, 90, 92, False, wx.EmptyString ) )
        contentFGSizer.Add( self.stCharacters, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTER_HORIZONTAL, 5 )

        self.m_staticline2 = wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL )
        contentFGSizer.Add( self.m_staticline2, 0, wx.EXPAND, 5 )

        self.m_staticline3 = wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL )
        contentFGSizer.Add( self.m_staticline3, 0, wx.EXPAND, 5 )

        self.m_staticline4 = wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL )
        contentFGSizer.Add( self.m_staticline4, 0, wx.EXPAND, 5 )

        ### Content
        for id in self.fleet:
            # set various properties
            self.fleet[id]['stLabel'].Wrap( -1 )
            self.fleet[id]['stLabel'].SetFont( wx.Font( wx.NORMAL_FONT.GetPointSize(), 70, 90, 92, False, wx.EmptyString ) )
            self.fleet[id]['stText'].Wrap( -1 )

            # bind text and choice events
            self.fleet[id]['stText'].Bind(wx.EVT_LEFT_DCLICK, self.RemoveBooster)
            self.fleet[id]['stText'].Bind(wx.EVT_ENTER_WINDOW, self.OnEnterWindow)
            self.fleet[id]['stText'].Bind(wx.EVT_LEAVE_WINDOW, self.OnLeaveWindow)
            self.fleet[id]['stText'].SetToolTip(wx.ToolTip(_("gangView_Double click to remove booster")))
            self.fleet[id]['chChar'].Bind(wx.EVT_CHOICE, self.CharChanged)
            self.fleet[id]['chFit'].Bind(wx.EVT_CHOICE, self.OnFitChoiceSelected)

            # add fit text and choice to the fit sizer
            self.fleet[id]['fitSizer'].Add( self.fleet[id]['stText'], 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5 )
            self.fleet[id]['fitSizer'].Add( self.fleet[id]['chFit'], 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL|wx.EXPAND, 1 )

            # add everything to the content sizer
            contentFGSizer.Add( self.fleet[id]['stLabel'], 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5 )
            contentFGSizer.Add( self.fleet[id]['fitSizer'], 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL|wx.EXPAND, 5 )
            contentFGSizer.Add( self.fleet[id]['chChar'], 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5 )

        mainSizer.Add( contentFGSizer, 1, wx.EXPAND, 0 )
        mainSizer.Add( helpSizer, 0, wx.EXPAND, 0 )

        self.SetSizer( mainSizer )
        self.SetAutoLayout(True)
        self.SetupScrolling()

        self.mainFrame.Bind(GE.CHAR_LIST_UPDATED, self.RefreshCharacterList)
        self.mainFrame.Bind(GE.FIT_CHANGED, self.fitSelected)
        self.mainFrame.Bind(gui.shipBrowser.EVT_FIT_RENAMED, self.fitRenamed)
        self.mainFrame.Bind(gui.shipBrowser.BOOSTER_LIST_UPDATED, self.RefreshBoosterFits)

        self.RefreshBoosterFits()
        self.RefreshCharacterList()

    def buildBoostermenu(self):
        menu = wx.Menu()

        for id, option in enumerate(self.options):
            item = menu.Append(-1, option)
            # We bind it to the mainFrame because it may be called from either this class or from FitItem via shipBrowser
            self.mainFrame.Bind(wx.EVT_MENU, self.OnPopupItemSelected, item)
        return menu

    def OnEnterWindow(self, event):
        obj = event.GetEventObject()
        obj.SetCursor(wx.StockCursor(wx.CURSOR_HAND))
        event.Skip()

    def OnLeaveWindow(self, event):
        obj = event.GetEventObject()
        obj.SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
        event.Skip()

    def CharChanged(self, event):
        ''' Change booster character '''
        chBooster = event.GetEventObject()

        type = -1
        for id in self.fleet:
            if chBooster == self.fleet[id]['chChar']: type = id

        if type == -1:
            event.Skip()
            return

        sFit = service.Fit.getInstance()

        fleetSrv = service.Fleet.getInstance()

        activeFitID = self.mainFrame.getActiveFit()
        fit = sFit.getFit(activeFitID)

        sChar = service.Character.getInstance()
        charList = sChar.getCharacterList()

        if activeFitID:
            commanders = fleetSrv.loadLinearFleet(fit)
            if commanders is None:
                fleetCom, wingCom, squadCom = (None, None, None)
            else:
                fleetCom, wingCom, squadCom = commanders

            if type == 0:
                if fleetCom:
                    charID = chBooster.GetClientData(chBooster.GetSelection())
                    sFit.changeChar(fleetCom.ID, charID)
                else:
                    chBooster.SetSelection(0)

            if type == 1:
                if wingCom:
                    charID = chBooster.GetClientData(chBooster.GetSelection())
                    sFit.changeChar(wingCom.ID, charID)
                else:
                    chBooster.SetSelection(0)

            if type == 2:
                if squadCom:
                    charID = chBooster.GetClientData(chBooster.GetSelection())
                    sFit.changeChar(squadCom.ID, charID)
                else:
                    chBooster.SetSelection(0)

            sFit.recalc(fit, withBoosters=True)
            wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=activeFitID))

        else:
            chBooster.SetSelection(0)

    def RemoveBooster(self, event):
        activeFitID = self.mainFrame.getActiveFit()
        if  not activeFitID:
            return

        location = event.GetEventObject()

        for id in self.fleet:
            if location == self.fleet[id]['stText']: type = id

        sFit = service.Fit.getInstance()
        boostee = sFit.getFit(activeFitID)
        booster = None

        fleetSrv = service.Fleet.getInstance()

        if type == 0: fleetSrv.setLinearFleetCom(boostee, booster)
        if type == 1: fleetSrv.setLinearWingCom(boostee, booster)
        if type == 2: fleetSrv.setLinearSquadCom(boostee, booster)

        # Hide stText and, default fit selection, and enable it
        location.Hide()
        choice = self.fleet[type]['chFit']
        choice.SetSelection(0)
        choice.Show()

        sFit.recalc(boostee, withBoosters=True)
        wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=activeFitID))

    def fitRenamed(self, event):
        fleetSrv = service.Fleet.getInstance()
        activeFitID = self.mainFrame.getActiveFit()

        if activeFitID:
            ev = event
            ev.fitID = activeFitID
            self.fitSelected(ev)

    def fitSelected(self, event):
        ''' Fires when active fit is selected and when booster is saved to fit. Update the UI to reflect changes '''
        fleetSrv = service.Fleet.getInstance()

        activeFitID = self.mainFrame.getActiveFit()
        sFit = service.Fit.getInstance()
        fit = sFit.getFit(event.fitID or activeFitID)
        commanders = (None, None, None)

        if activeFitID:
            commanders = fleetSrv.loadLinearFleet(fit)

        for id in self.fleet:
            # try...except here as we're trying 2 different criteria and want to fall back on the same code
            try:
                commander = commanders[id]

                if not activeFitID or commander is None:
                    raise Exception()

                self.fleet[id]['stText'].SetLabel(commander.ship.item.name + ": " + commander.name)
                self.fleet[id]['chChar'].SetStringSelection(commander.character.name if commander.character is not None else "All 0")
                self.fleet[id]['chChar'].Enable()
                self.fleet[id]['chFit'].Hide()
                self.fleet[id]['stText'].Show()
            except:
                #set defaults, disable char selection, and enable fit selection
                self.fleet[id]['stText'].SetLabel("None")
                self.fleet[id]['chChar'].SetStringSelection("All 0")
                self.fleet[id]['chChar'].Disable()
                self.fleet[id]['chFit'].SetSelection(0)
                self.fleet[id]['chFit'].Show()
                self.fleet[id]['stText'].Hide()

        if activeFitID:
            self.Enable()
        else:
            self.Disable()

        self.Layout()
        self.SendSizeEvent()

    def AddCommander(self, fitID, type = None):
        ''' Adds booster to a fit, then recalculates active fit '''
        if type is None:
            return

        activeFitID = self.mainFrame.getActiveFit()
        if activeFitID:
            sFit = service.Fit.getInstance()

            boostee = sFit.getFit(activeFitID)
            booster = sFit.getFit(fitID)

            fleetSrv = service.Fleet.getInstance()

            if type == 0: fleetSrv.setLinearFleetCom(boostee, booster)
            if type == 1: fleetSrv.setLinearWingCom(boostee, booster)
            if type == 2: fleetSrv.setLinearSquadCom(boostee, booster)

            sFit.recalc(boostee)
            wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=activeFitID))

    def RefreshBoosterFits(self, event = None):
        sFit = service.Fit.getInstance()
        sMkt = service.Market.getInstance()
        fitList = sFit.getBoosterFits()

        for id in self.fleet:
            choice = self.fleet[id]['chFit']
            chCurrSelection = choice.GetSelection()
            chCurrData = -1
            if chCurrSelection != -1:
                chCurrData = choice.GetClientData(chCurrSelection)
                chCurrSelString = choice.GetString(chCurrSelection)
            choice.Clear()
            currSelFound = False
            choice.Append("None", -1)
            for fit in fitList:
                id,name,type = fit
                ship = sMkt.getItem(type)
                choice.Append(ship.name+': '+name, id)
                if chCurrData == id:
                    currSelFound = True

            if chCurrSelection == -1:
                choice.SetSelection(0)
            else:
                if currSelFound:
                    choice.SetStringSelection(chCurrSelString)
                else:
                    choice.SetSelection(0)

    def RefreshCharacterList(self, event = None):
        sChar = service.Character.getInstance()
        charList = sChar.getCharacterList()
        for id in self.fleet:
            choice = self.fleet[id]['chChar']
            chCurrSelection = choice.GetSelection()
            chCurrData = -1
            if chCurrSelection != -1:
                chCurrData = choice.GetClientData(chCurrSelection)
                chCurrSelString = choice.GetString(chCurrSelection)
            choice.Clear()
            currSelFound = False
            for char in charList:
                choice.Append(char.name, char.ID)
                if chCurrData == char.ID:
                    currSelFound = True

            if chCurrSelection == -1:
                choice.SetSelection(1)
            else:
                if currSelFound:
                    choice.SetStringSelection(chCurrSelString)
                else:
                    choice.SetSelection(1)

    def handleDrag(self, type, fitID):
        ''' Handle dragging of fit to fleet interface '''
        #Those are drags coming from pyfa sources, NOT builtin wx drags
        self.draggedFitID = None
        if type == "fit":
            activeFit = self.mainFrame.getActiveFit()
            if activeFit:
                self.draggedFitID = fitID

                pos = wx.GetMousePosition()
                pos = self.ScreenToClient(pos)

                self.PopupMenu(self.FitDNDPopupMenu, pos)

    def OnPopupItemSelected(self, event):
        ''' Fired when booster popup item is selected '''
        # Get menu selection ID via self.options
        menuItem = event.EventObject.FindItemById(event.GetId())
        type = self.options.index(menuItem.GetText())

        if self.draggedFitID:
            sFit = service.Fit.getInstance()
            draggedFit = sFit.getFit(self.draggedFitID)

            self.AddCommander(draggedFit.ID, type)
            self.mainFrame.additionsPane.select("Fleet")

    def OnFitChoiceSelected(self, event):
        ''' Fired when booster choice is selected '''
        sFit = service.Fit.getInstance()

        # set type via choice box used
        chFit = event.GetEventObject()
        fitID = chFit.GetClientData(chFit.GetSelection())

        type = -1
        for id in self.fleet:
            if chFit == self.fleet[id]['chFit']: type = id

        if type == -1 or fitID == -1:
            event.Skip()
            return

        fit = sFit.getFit(fitID)

        self.AddCommander(fit.ID, type)
        self.mainFrame.additionsPane.select("Fleet")


from pprint import pprint

# Write a function that receives a sequence of key-value pairs and creates a dictionary of lists
# (and returns it). For example:

# Original list:
# [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)]
# Grouping a sequence of key-value pairs into a dictionary of lists:
# {'yellow': [1, 3], 'blue': [2, 4], 'red': [1]}

def create_dict_list(*args: tuple[str, int]) -> dict[str | list]:
    new_dict: dict[str, list] = dict()

    for arg in args[0]:
        if arg[0] not in new_dict.keys():
            new_dict[arg[0]] = [arg[1]]
        else:
            new_dict[arg[0]].append(arg[1])
    return  new_dict

l = [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)]
d = create_dict_list(l)
# pprint(l)
pprint(d)


from cerberus.validator import DocumentError
from flask import abort
from flask import current_app as app

from eve.auth import requires_auth
from eve.methods.common import (build_response_document,
                                marshal_write_response, oplog_push, parse,
                                payload, pre_event, ratelimit,
                                resolve_document_etag, resolve_embedded_fields,
                                resolve_sub_resource_path,
                                resolve_user_restricted_access, resource_link,
                                store_media_files, utcnow)
from eve.utils import config, debug_error_message, parse_request
from eve.versioning import (insert_versioning_documents,
                            resolve_document_version)

@ratelimit()
@requires_auth("resource")
@pre_event
def post(resource, payl=None):
    """
    Default function for handling POST requests, it has decorators for
    rate limiting, authentication and for raising pre-request events. After the
    decorators are applied forwards to call to :func:`post_internal`

    .. versionchanged:: 0.5
       Split original post() into post/post_internal combo.
    """
    return post_internal(resource, payl, skip_validation=False)

def post_internal(resource, payl=None, skip_validation=False):
    """
    Intended for internal post calls, this method is not rate limited,
    authentication is not checked and pre-request events are not raised.
    Adds one or more documents to a resource. Each document is validated
    against the domain schema. If validation passes the document is inserted
    and ID_FIELD, LAST_UPDATED and DATE_CREATED along with a link to the
    document are returned. If validation fails, a list of validation issues
    is returned.

    :param resource: name of the resource involved.
    :param payl: alternative payload. When calling post() from your own code
                 you can provide an alternative payload. This can be useful,
                 for example, when you have a callback function hooked to a
                 certain endpoint, and want to perform additional post() calls
                 from there.

                 Please be advised that in order to successfully use this
                 option, a request context must be available.

                 See https://github.com/pyeve/eve/issues/74 for a
                 discussion, and a typical use case.
    :param skip_validation: skip payload validation before write (bool)

    .. versionchanged:: 0.7
       Add support for Location header. Closes #795.

    .. versionchanged:: 0.6
       Fix: since v0.6, skip_validation = True causes a 422 response (#726).

    .. versionchanged:: 0.6
       Initialize DELETED field when soft_delete is enabled.

    .. versionchanged:: 0.5
       Back to resolving default values after validation as now the validator
       can properly validate dependency even when some have default values. See
       #353.
       Push updates to the OpLog.
       Original post() has been split into post() and post_internal().
       ETAGS are now stored with documents (#369).

    .. versionchanged:: 0.4
       Resolve default values before validation is performed. See #353.
       Support for document versioning.

    .. versionchanged:: 0.3
       Return 201 if at least one document has been successfully inserted.
       Fix #231 auth field not set if resource level authentication is set.
       Support for media fields.
       When IF_MATCH is disabled, no etag is included in the payload.
       Support for new validation format introduced with Cerberus v0.5.

    .. versionchanged:: 0.2
       Use the new STATUS setting.
       Use the new ISSUES setting.
       Raise 'on_pre_<method>' event.
       Explicitly resolve default values instead of letting them be resolved
       by common.parse. This avoids a validation error when a read-only field
       also has a default value.
       Added ``on_inserted*`` events after the database insert

    .. versionchanged:: 0.1.1
       auth.request_auth_value is now used to store the auth_field value.

    .. versionchanged:: 0.1.0
       More robust handling of auth_field.
       Support for optional HATEOAS.

    .. versionchanged: 0.0.9
       Event hooks renamed to be more robust and consistent: 'on_posting'
       renamed to 'on_insert'.
       You can now pass a pre-defined custom payload to the funcion.

    .. versionchanged:: 0.0.9
       Storing self.app.auth.userid in auth_field when 'user-restricted
       resource access' is enabled.

    .. versionchanged: 0.0.7
       Support for Rate-Limiting.
       Support for 'extra_response_fields'.

       'on_posting' and 'on_posting_<resource>' events are raised before the
       documents are inserted into the database. This allows callback functions
       to arbitrarily edit/update the documents being stored.

    .. versionchanged:: 0.0.6
       Support for bulk inserts.

       Please note: validation constraints are checked against the database,
       and not between the payload documents themselves. This causes an
       interesting corner case: in the event of a multiple documents payload
       where two or more documents carry the same value for a field where the
       'unique' constraint is set, the payload will validate successfully, as
       there are no duplicates in the database (yet). If this is an issue, the
       client can always send the documents once at a time for insertion, or
       validate locally before submitting the payload to the API.

    .. versionchanged:: 0.0.5
       Support for 'application/json' Content-Type .
       Support for 'user-restricted resource access'.

    .. versionchanged:: 0.0.4
       Added the ``requires_auth`` decorator.

    .. versionchanged:: 0.0.3
       JSON links. Superflous ``response`` container removed.
    """

    date_utc = utcnow()
    resource_def = app.config["DOMAIN"][resource]
    schema = resource_def["schema"]
    validator = (
        None
        if skip_validation
        else app.validator(
            schema, resource=resource, allow_unknown=resource_def["allow_unknown"]
        )
    )

    documents = []
    results = []
    failures = 0
    id_field = resource_def["id_field"]

    if config.BANDWIDTH_SAVER is True:
        embedded_fields = []
    else:
        req = parse_request(resource)
        embedded_fields = resolve_embedded_fields(resource, req)

    # validation, and additional fields
    if payl is None:
        payl = payload()

    if isinstance(payl, dict):
        payl = [payl]

    if not payl:
        # empty bulk insert
        abort(400, description=debug_error_message("Empty bulk insert"))

    if len(payl) > 1 and not config.DOMAIN[resource]["bulk_enabled"]:
        abort(400, description=debug_error_message("Bulk insert not allowed"))

    for value in payl:
        document = []
        doc_issues = {}
        try:
            document = parse(value, resource)
            resolve_sub_resource_path(document, resource)
            if skip_validation:
                validation = True
            else:
                validation = validator.validate(document)
            if validation:  # validation is successful
                # validator might be not available if skip_validation. #726.
                if validator:
                    # Apply coerced values
                    document = validator.document

                # Populate meta and default fields
                document[config.LAST_UPDATED] = document[config.DATE_CREATED] = date_utc

                if config.DOMAIN[resource]["soft_delete"] is True:
                    document[config.DELETED] = False

                resolve_user_restricted_access(document, resource)
                store_media_files(document, resource)
                resolve_document_version(document, resource, "POST")
            else:
                # validation errors added to list of document issues
                doc_issues = validator.errors
        except DocumentError as e:
            doc_issues["validation exception"] = str(e)
        except Exception as e:
            # most likely a problem with the incoming payload, report back to
            # the client as if it was a validation issue
            app.logger.exception(e)
            doc_issues["exception"] = str(e)

        if doc_issues:
            document = {config.STATUS: config.STATUS_ERR, config.ISSUES: doc_issues}
            failures += 1

        documents.append(document)

    if failures:
        # If at least one document got issues, the whole request fails and a
        # ``422 Bad Request`` status is return.
        for document in documents:
            if (
                config.STATUS in document
                and document[config.STATUS] == config.STATUS_ERR
            ):
                results.append(document)
            else:
                results.append({config.STATUS: config.STATUS_OK})

        return_code = config.VALIDATION_ERROR_STATUS
    else:
        # notify callbacks
        getattr(app, "on_insert")(resource, documents)
        getattr(app, "on_insert_%s" % resource)(documents)

        # compute etags here as documents might have been updated by callbacks.
        resolve_document_etag(documents, resource)

        # bulk insert
        ids = app.data.insert(resource, documents)

        # update oplog if needed
        oplog_push(resource, documents, "POST")

        # assign document ids
        for document in documents:
            # either return the custom ID_FIELD or the id returned by
            # data.insert().
            id_ = document.get(id_field, ids.pop(0))
            document[id_field] = id_

            # build the full response document
            result = document
            build_response_document(result, resource, embedded_fields, document)

            # add extra write meta data
            result[config.STATUS] = config.STATUS_OK

            # limit what actually gets sent to minimize bandwidth usage
            result = marshal_write_response(result, resource)
            results.append(result)

        # insert versioning docs
        insert_versioning_documents(resource, documents)

        # notify callbacks
        getattr(app, "on_inserted")(resource, documents)
        getattr(app, "on_inserted_%s" % resource)(documents)
        # request was received and accepted; at least one document passed
        # validation and was accepted for insertion.

        return_code = 201

    if len(results) == 1:
        response = results.pop(0)
    else:
        response = {
            config.STATUS: config.STATUS_ERR if failures else config.STATUS_OK,
            config.ITEMS: results,
        }

    if failures:
        response[config.ERROR] = {
            "code": return_code,
            "message": "Insertion failure: %d document(s) contain(s) error(s)"
            % failures,
        }

    location_header = (
        None
        if return_code != 201 or not documents
        else [("Location", "%s/%s" % (resource_link(), documents[0][id_field]))]
    )

    return response, None, None, return_code, location_header


#
# @lc app=leetcode.cn id=300 lang=python3
#
# [300] 最长上升子序列
#

# @lc code=start
class Solution:
    def lengthOfLIS(self, nums: List[int]) -> int:
        if len(nums)==0:
            return 0
        dp = [1] * len(nums)
        ans = 1
        for i in range(len(nums)):
            for j in range(i):
                if nums[i]>nums[j]:
                    dp[i] = max(dp[i],dp[j]+1)
                    ans = max(dp[i],ans)
        return ans
# @lc code=end

300.最长上升子序列.py


# 5) (JSON exercise) Create a list of dictionaries with 5 countries, their capital, and approx.
# population(of the capital)
# Sample entry:
# {‘Country’: ‘Canada’, ‘Capital’:’Ottawa’, ‘Population’: 883,391}
# Write the dictionary to a json format file.
# Check the contents of the file in notepad or other text editor.
# Read the file back into your script and parse with json.
# Print the contents from the resulting dictionary. (key and value)

import json;

countries = [{"Country":"Canada", "Capital":"Ottawa", "Population":883391},
             {"Country":"United States of America", "Capital":"Washington DC", "Population":692683},
             {"Country":"Mexico", "Capital":"Mexico City", "Population":8885000},
             {"Country":"United Kingdom", "Capital":"London", "Population":9000000},
             {"Country":"China", "Capital":"Beijing", "Population":21540000}];

with open('py2_Class3_Ex5.json', 'w') as outfile:
    json.dump(countries, outfile, indent=4, separators=(',',':'));

importJson = open('py2_Class3_Ex5.json', 'r');

parsedJson = json.load(importJson);
for x in parsedJson:
    for i in x:
        print(i, x[i])


import os
import time
import paho.mqtt.client as mqtt

def on_message(client, userdata, message):
    msg = str(message.payload.decode("utf-8"))
    output = msg.split(';')
    # print('here : ', output)
    try:
        with open('profits', 'w') as f:
            for l in output:
                if l != '\n':
                    f.write(l)
                    f.write('\n')
    except Exception as e:
        print(e)

broker = '192.168.86.249'

client = mqtt.Client("RHClient")
client.connect(broker)

client.subscribe("rhscraper")
client.on_message=on_message

client.loop_forever()


from datetime import datetime
from django.contrib.postgres.fields import JSONField
from django_currentuser.middleware import get_current_user, get_current_authenticated_user
from django.db import models

from absolutum.mixins import CoreMixin, DisplayMixin

class Log(models.Model, CoreMixin, DisplayMixin):
    """
        Журнал телефонии
    """
    event_type = models.CharField(max_length=128, verbose_name='Тип события')
    entry_id = models.CharField(max_length=128, null=True, blank=True, verbose_name='Id звонка')
    entry_datetime = models.DateTimeField(null=True, blank=True, verbose_name='Время')
    from_number = models.CharField(max_length=256, null=True, verbose_name='Кто звонил')
    to_number = models.CharField(max_length=256, null=True, verbose_name='Набранный номер')
    created_at = models.DateTimeField(auto_now_add=True, verbose_name='Запись создана')
    created_by = models.ForeignKey('company.User', null=True, blank=True, verbose_name='Инициатор',
                                   on_delete=models.CASCADE, related_name='sip_logs')
    data = JSONField(default=dict)

    filters_ordered = ['event_type']
    filters_fields = dict(
        event_type=dict(
            label='Тип события', key='event_type__icontains', widget=dict(attrs={}, name='TextInput', input_type='text')
        ),
    )
    list_display = ['entry_datetime', 'event_type', 'from_number', 'to_number', 'created_by']
    list_form_fields = ['entry_datetime', 'entry_id', 'event_type', 'from_number', 'to_number']
    list_attrs = dict(
        entry_id=dict(disabled=True),
        event_type=dict(disabled=True),
        from_number=dict(disabled=True),
        to_number=dict(disabled=True),
    )

    icon = 'el-icon-tickets'

    class Meta:
        verbose_name = 'Запись в журнале телефонии'
        verbose_name_plural = 'Журнал телефонии'
        ordering = ['-entry_datetime']
        default_permissions = ()
        permissions = [
            ('view_log', 'Просматривать журнал'),
        ]

    def save(self, *args, **kwargs):
        if not self.entry_datetime:
            self.entry_datetime = datetime.now()

        if not self.created_by:
            try:
                self.created_by = get_current_authenticated_user().person
            except:
                pass
        super().save(*args, **kwargs)


import os
import sys
import pandas as pd
from text.constants import *
from text.logger import logging
from datasets import load_from_disk
from transformers import AutoTokenizer
from transformers import AdamWeightDecay
from text.exception import CustomException
from text.entity.config_entity import ModelTrainerConfig
from transformers import TFAutoModelForSeq2SeqLM, DataCollatorForSeq2Seq
from text.entity.artifacts_entity import DataTransformationArtifacts, ModelTrainerArtifacts
class ModelTrainer:
    def __init__(self,data_transformation_artifacts: DataTransformationArtifacts,
                    model_trainer_config: ModelTrainerConfig):
        """
        :param data_ingestion_artifact: Output reference of data ingestion artifact stage
        :param data_transformation_config: Configuration for data transformation
        """

        self.data_transformation_artifacts = data_transformation_artifacts
        self.model_trainer_config = model_trainer_config

    def preparing_train_data(self):
        try:
            # tokenized_datasets = load_from_disk(os.path.join(os.getcwd(),"tokenized_data"))
            # tokenized_datasets = load_from_disk(os.path.join(os.getcwd(),"artifacts/12_19_2022_12_02_25/DataTransformationArtifacts/" ))
            # print(self.data_transformation_artifacts.tokenized_datasets)
            tokenized_datasets = load_from_disk(self.model_trainer_config.PATH_TOKENIZED_DATA)
            small_train_dataset = tokenized_datasets["train"].shuffle(seed=42).select(range(6000))
            small_validation_dataset = tokenized_datasets["validation"].shuffle(seed=42).select(range(1000))
            small_test_dataset = tokenized_datasets["test"].shuffle(seed=42).select(range(1000))

            # Model checkpoint of pretrained model
            model_checkpoint = "t5-small"

            model = TFAutoModelForSeq2SeqLM.from_pretrained(model_checkpoint)
            model_name = model_checkpoint.split("/")[-1]
            push_to_hub_model_id = f"{model_name}-finetuned-xsum"
            tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)
            data_collator = DataCollatorForSeq2Seq(tokenizer, model=model, return_tensors="tf")

            generation_data_collator = DataCollatorForSeq2Seq(tokenizer, model=model, return_tensors="tf", pad_to_multiple_of=128)
            train_dataset = model.prepare_tf_dataset(
                small_train_dataset,
                batch_size=BATCH_SIZE,
                shuffle=True,
                collate_fn=data_collator,
            )

            validation_dataset = model.prepare_tf_dataset(
                small_validation_dataset,
                batch_size=BATCH_SIZE,
                shuffle=False,
                collate_fn=data_collator,
            )

            test_dataset = model.prepare_tf_dataset(
                small_test_dataset,
                batch_size=BATCH_SIZE,
                shuffle=False,
                collate_fn=data_collator,
            )

            optimizer = AdamWeightDecay(learning_rate=LEARNING_RATE, weight_decay_rate=WEIGHT_DECAY)
            model.compile(optimizer=optimizer)

            return model, train_dataset, validation_dataset, test_dataset

        except Exception as e:
            raise CustomException(e, sys) from e

    def start_model_training(self,model,train_dataset,validation_dataset):
        try:
            history = model.fit(train_dataset, validation_data=validation_dataset, epochs=EPOCHS)
            # logging.info(f"the loss of the trained model is {history.history}")

            return history
        except Exception as e:
            raise CustomException(e, sys) from e

    def initiate_model_trainer(self,) -> ModelTrainerArtifacts:
        logging.info("Entered initiate_model_trainer method of ModelTrainer class")

        """
        Method Name :   initiate_model_trainer
        Description :   This function initiates a model trainer steps

        Output      :   Returns model trainer artifact
        On Failure  :   Write an exception log and then raise an exception
        """

        try:

            model,train_dataset,validation_dataset,test_dataset = self.preparing_train_data()

            history= self.start_model_training(model=model,train_dataset=train_dataset,validation_dataset=validation_dataset)
            logging.info(f"the loss of the trained model is {history.history}")

            os.makedirs(self.model_trainer_config.TRAINED_MODEL_DIR, exist_ok=True)
            # torch.save(model, self.model_trainer_config.TRAINED_MODEL_PATH)
            # saving the pretrained model
            model.save_pretrained(self.model_trainer_config.TRAINED_MODEL_PATH)
            logging.info(f"Saved the trained model")

            model_trainer_artifacts = ModelTrainerArtifacts(
                trained_model_path=self.model_trainer_config.TRAINED_MODEL_PATH,
            )
            logging.info(f"Model trainer artifact: {model_trainer_artifacts}")

            return model_trainer_artifacts

        except Exception as e:
            raise CustomException(e, sys) from e


i = 0
contador = 0
addiction = 0
while i<6:
    valor = float(input())
    if valor>=0:
        addiction = addiction +valor
        contador+=1
    i+=1
addiction = addiction/contador
print(f"{contador} valores positivos")
print(f"{addiction:.1f}")


import unittest
from oracle import Oracle
from helper import Helper
import requests

class TestStockPrediction(unittest.TestCase):

    def test_stock_initalized(self):
        oracle = Oracle("msft", requests.session())
        helper = Helper(oracle)
        result = helper.prediction(1000)

        prediction = result['prediction']
        hold = result['hold']

        print(prediction)
        print(hold)

        assert prediction >= hold

class TestReporter(unittest.TestCase):

    def test_reporter(self):
        oracle = Oracle("msft", requests.session())
        oracle.report()
        oracle.close()

        self.assertEqual(0,0)

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


from django.shortcuts import render
from rest_framework.views import APIView
from django.http import JsonResponse, HttpResponse
from .models import TopicPlaylist, Playlists, Artists, PlaylistOfTopic, ArtistOfPlaylist
from .serializers import TopicPlaylistSerializers, PlaylistSerializers, ArtistSerializers, PlaylistSortDataSerializers
from rest_framework.permissions import AllowAny

# Create your views here.
class TopicPlaylistAPIView(APIView):
    permission_classes = [AllowAny]

    def post(self, request):
        data = request.data
        if not data:
            return HttpResponse(status=404)

        topics = TopicPlaylist.objects.create(
            title=data["title"]
        )
        topics.save()
        serializer = TopicPlaylistSerializers(topics).data
        return JsonResponse(serializer, safe=False)

class PlaylistAPIView(APIView):
    permission_classes = [AllowAny]

    def get(self, request):
        playlists = Playlists.objects.all()
        if not playlists.exists():
            return HttpResponse(status=404)

        serializer = PlaylistSerializers(playlists, many=True)
        return JsonResponse(serializer.data, safe=False)

    def post(self, request):
        data = request.data
        if not data:
            return HttpResponse(status=404)

        playlist = Playlists.objects.create(
            title=data["title"],
            thumbnail=data["thumbnail"],
            isoffical=data["isoffical"],
            is_indie=data["isIndie"],
            release_date=data["releaseDate"],
            sort_description=data["sortDescription"],
            released_at=data["releasedAt"],
            pr=data["PR"],
            artist_names=data["artistsNames"],
            play_item_mode=data["playItemMode"],
            sub_type=data["subType"],
            uid=data["uid"],
            thumbnail_m=data["thumbnailM"],
            is_shuffle=data["isShuffle"],
            is_private=data["isPrivate"],
            user_name=data["userName"],
            is_album=data["isAlbum"],
            text_type=data["textType"],
            is_single=data["isSingle"]
        )
        playlist.save()
        serializer = PlaylistSerializers(playlist).data

        return JsonResponse(serializer, safe=False)

class ArtistAPIView(APIView):
    permission_classes = [AllowAny]

    def get(self, request):
        artists = Artists.objects.all()
        if not artists.exists():
            return HttpResponse(status=404)

        serializer = ArtistSerializers(artists, many=True)
        return JsonResponse(serializer.data, safe=False)

    def post(self, request):
        data = request.data
        artist = Artists.objects.create(
            name=data["name"],
            spotlight=data["spotlight"],
            alias=data["alias"],
            thumbnail=data["thumbnail"],
            thumbnail_m=data["thumbnailM"],
            is_oa=data["isOA"],
            is_oa_brand=data["isOABrand"],
            total_follow=data["totalFollow"]
        )

        artist.save()
        serializers = ArtistSerializers(artist).data

        return JsonResponse(serializers, safe=False)

class PostPlaylistDataSort(APIView):
    permission_classes = [AllowAny]

    def post(self, request):
        data = request.data
        playlist = Playlists.objects.create(
            thumbnail=data["thumbnail"],
            thumbnail_m=data["thumbnailM"],
            title=data["title"],
            sort_description=data["sortDescription"],
            artist_names=data["artistsNames"],
        )

        playlist.save()
        serializers = PlaylistSortDataSerializers(playlist).data

        return JsonResponse(serializers, safe=False)


from pprint import pprint
import ast
import re
import json

import requests

TOKEN = "3018687491722745"

class Superhero:
    def __init__(self,name,intelligence):
        self.name = name
        self.intelligence = intelligence
hero_intel = []

def compare(heroes):
    max_int = 0
    hero_name = ""
    for hero in heroes:
        if int(hero.intelligence) > max_int:
            max_int = int(hero.intelligence)
            hero_name = hero.name
    pprint(hero_name)

def get_hero_int(intel_data):
    overall_data = json.loads(intel_data)
    name = overall_data["results-for"]
    intelligence = overall_data["results"][0]["powerstats"]["intelligence"]
    hero = Superhero(name, intelligence)
    hero_intel.append(hero)

def test_request():
    name = ["Hulk","Captain America","Thanos"]
    url = "https://superheroapi.com/api/"
    key = TOKEN
    for i in name:
        search = f"/search/{i}"
        endpoint_url =f"{url}{key}{search}"
        response = requests.get(endpoint_url,  timeout = 5)
        get_hero_int(response.text)
    compare(hero_intel)

test_request()


'''
The program must accept an integer N as the input. The program must print the desired pattern as shown in the Example Input/Output section.

Boundary Condition(s): 0 <= N <= 10^8

Input Format:  The first line contains N.
Output Format: The first N lines contain integers as shown in the Example Input/Output section.

Example Input/Output 1:

Input:  74381

Output: 7
        44444444
        333
        8888
        1111111

Example Input/Output 2:

Input:  5019
Output: 555555555
        0
        -
        99999

SOLUTION:
'''

N=input().strip()
for index in range(len(N)):
    if N[len(N)-index-1] == '0':
        print("-")
        continue
    print(N[index] * int(N[len(N) - index - 1]))


from operator import truediv
import sqlite3 as sl
import json
import time
import datetime
import os
from db_dictionary import dict_factory
from sys import platform

global car_space_id
car_space_id = 9

# no owner, need to add in frontend
def convert_path_to_data(path):
	text_file = open(path, "r")
	data = text_file.read()
	text_file.close()
	return data

def get_picture_path(id):
	cwd = os.getcwd()
	if platform == "linux" or platform == "linux2" or platform == 'darwin':
		directory = f"{cwd}/picture/{id}"
	else:
		directory = f"{cwd}\\picture\\{id}"
	return directory

def create_car_space(input):
	con = sl.connect('my-test.db')

	id = input["listing_id"]
	# delete if already exists
	sql_query = f'''DELETE FROM car_spaces
		WHERE id == {id}'''
	with con:
		con.execute(sql_query)

	name = input["name"]
	owner = input["user_id"]
	address = input["address"]

	cwd = os.getcwd()
	photo_path = get_picture_path(id)
	text_file = open(photo_path, "w+")
	n = text_file.write(input["photo_link"])
	text_file.close()
	photo_link = photo_path

	space_type = input["space_type"]

	amenity_24_7 = input["amenity_24_7"]
	amenity_sheltered = input["amenity_sheltered"]
	amenity_security_gates = input["amenity_security_gates"]

	description = input["description"]
	instruction = input["instruction"]
	monday = input["monday"]
	tuesday = input["tuesday"]
	wednesday = input["wednesday"]
	thursday = input["thursday"]
	friday = input["friday"]
	saturday = input["saturday"]
	sunday = input["sunday"]
	starttime = input["starttime"]
	endtime = input["endtime"]
	price_hourly = input["price_hourly"]
	price_daily = input["price_daily"]
	postcode = input["postcode"]
	coordinates = input["coordinates"]
	creation_date = int(time.time())

	sql_query = '''INSERT INTO car_spaces (id, name, owner, address, coordinates, description,
 	instruction, price_hourly, price_daily, postcode, photo_link, space_type, amenity_24_7, amenity_sheltered,
  	amenity_security_gates, monday, tuesday, wednesday, thursday, friday, saturday, sunday, starttime, endtime,
   	creation_date, hours_used)
   	values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'''
	data = [(id, name, owner, address, coordinates, description,
	instruction, price_hourly, price_daily, postcode, photo_link, space_type,
	amenity_24_7, amenity_sheltered, amenity_security_gates, monday, tuesday, wednesday, thursday, friday,
 	saturday, sunday, starttime, endtime, creation_date, 0)]
	with con:
		con.executemany(sql_query, data)

	con.commit()
	con.close()
	return

def delete_car_space(id, user_id):
	con = sl.connect('my-test.db')
	cur = con.cursor()

	sql_query = f'''SELECT * FROM car_spaces
		WHERE id = {id};'''

	cur.execute(sql_query)
	data = cur.fetchall()
	if len(data) == 0:
		return 404

	sql_query = "DELETE FROM car_spaces WHERE id = ?;"
	cur.execute(sql_query, (id,))
	con.commit()

	# check if deleted
	sql_query = f'''SELECT * FROM car_spaces
		WHERE id = {id};'''

	cur.execute(sql_query)
	data = cur.fetchall()
	if len(data) != 0:
		return 500

	con.close()

	return 200

def get_listing_by_id(id):
	#try:
	con = sl.connect('my-test.db')
	con.row_factory = dict_factory
	cur = con.cursor()
	sql_query = f"SELECT * FROM car_spaces WHERE id = {id};"
	cur.execute(sql_query)
	record = cur.fetchone()

	if not record:
		return 404, None

	sql_query = f'SELECT * FROM booking WHERE car_space = {id}'
	cur.execute(sql_query)
	records = cur.fetchall()
	bookings_num = 0
	bookings_in_the_past = 0
	now = datetime.datetime.now()
	for record2 in records:
		bookings_num = bookings_num + 1
		booking_end_time = record2['end_time']
		datetime_booking_end_time = datetime.datetime.strptime(booking_end_time, '%Y-%m-%d %H:%M:%S')
		if datetime_booking_end_time < now:
			bookings_in_the_past = bookings_in_the_past + 1

	record['total_bookings'] = bookings_num
	try:
		percent = bookings_in_the_past / bookings_num
	except ZeroDivisionError:
		percent = 0
	record['percentage_bookings'] = round(percent, 2) * 100

	car_space_id = record["id"]
	photo_path = get_picture_path(car_space_id)
	record["photo_link"] = convert_path_to_data(photo_path)

	return 200, record

def get_listings_all():
	#try:
	con = sl.connect('my-test.db')
	con.row_factory = dict_factory
	cur = con.cursor()
	sql_query = "SELECT * FROM car_spaces;"
	cur.execute(sql_query)
	records = cur.fetchall()

	for record in records:
		car_space_id = record["id"]
		photo_path = get_picture_path(car_space_id)
		record["photo_link"] = convert_path_to_data(photo_path)
	return records

def get_listing_by_userid(userid):
    	#try:
	con = sl.connect('my-test.db')
	con.row_factory = dict_factory
	cur = con.cursor()
	sql_query = f"SELECT * FROM car_spaces WHERE owner = {userid};"
	cur.execute(sql_query)
	records = cur.fetchall()

	if not records:
		return 404, None

	final_list = []
	for record in records:

		id = record['id']
		sql_query = f'SELECT * FROM booking WHERE car_space = {id}'
		cur.execute(sql_query)
		records = cur.fetchall()
		bookings_num = 0
		bookings_in_the_past = 0
		now = datetime.datetime.now()
		for record2 in records:
			bookings_num = bookings_num + 1
			booking_end_time = record2['end_time']
			datetime_booking_end_time = datetime.datetime.strptime(booking_end_time, '%Y-%m-%d %H:%M:%S')
			if datetime_booking_end_time < now:
				bookings_in_the_past = bookings_in_the_past + 1

		record['total_bookings'] = bookings_num
		percent = 0
		try:
			percent = bookings_in_the_past / bookings_num
		except ZeroDivisionError:
			percent = 0
		record['percentage_bookings'] = round(percent, 2)

		car_space_id = record["id"]
		photo_path = get_picture_path(car_space_id)
		record["photo_link"] = convert_path_to_data(photo_path)

		final_list.append(record)

	return 200, final_list

# checks if car space belongs to a user
def is_owner(listing_id, user_id):
	con = sl.connect('my-test.db')
	con.row_factory = dict_factory
	cur = con.cursor()

	sql_query = f"SELECT * FROM car_spaces WHERE id = {listing_id};"

	cur.execute(sql_query)
	record = cur.fetchone()

	if record["owner"] == user_id:
		return True

	return False

# check if a listing exists
def exists(listing_id):
	con = sl.connect('my-test.db')
	con.row_factory = dict_factory
	cur = con.cursor()

	sql_query = f"SELECT * FROM car_spaces WHERE id = {listing_id};"

	cur.execute(sql_query)
	record = cur.fetchone()

	if record:
		return True

	return False

def is_valid_time(listing_id, booking_start_time, booking_end_time):

	start_time = datetime.datetime.fromisoformat(booking_start_time)
	start_hour = start_time.hour

	end_time = datetime.datetime.fromisoformat(booking_end_time)
	end_hour = end_time.hour

	con = sl.connect('my-test.db')
	con.row_factory = dict_factory
	cur = con.cursor()

	sql_query = f"SELECT * FROM car_spaces WHERE id = {listing_id};"

	cur.execute(sql_query)
	record = cur.fetchone()

	if not record:
		return False

	if start_hour >= record["starttime"] and end_hour <= record["endtime"]:
		return True

	return False


from datetime import datetime, timedelta

from airflow.decorators import task, dag

default_args = {
    "start_date": datetime(2022, 4, 1),
}

@task.python(task_id="new_getting_data", do_xcom_push=False, multiple_outputs=True)
def getting_data():
    print("getting data")
    # multiple return xcom by key
    return {"name": "hello", "path": "/path/demo"}

@task.python
def processing_data(name, path):
    print("processing data", name, path)

@task.python
def check():
    print("checking")

@dag(description="demo new way", schedule_interval="*/10 * * * *",
    default_args=default_args, catchup=False, max_active_runs=1, tags=["taskflowapi"],
    dagrun_timeout=timedelta(minutes=10))
def new_way_dag():

    data = getting_data()

    processing_data(data["name"], data["path"]) >> check()

dag = new_way_dag()


t = int(input())
while t:
    n = int(input())
    sum = 0
    check = 1
    nho = n%10-2
    while n:
        sum += n%10
        if abs(n%10 - nho) != 2:
            check = 0
            break
        nho = n%10
        n = int(n/10)
    if check == 1 and sum%10 == 0: print("YES")
    else:    print("NO")
    t-=1


import crest_loader
import io
import struct
import unittest
from crest.filing.midi.track import MidiFileData
from crest.filing.midi.track import MidiFileHeader
from crest.filing.midi.track import MidiFileReader

_test = b'\x4D\x54\x68\x64' + \
        b'\x00\x00\x00\x06' + \
        b'\x00\x01\x00\x02\x01\xE0' + \
        b'\x4D\x54\x72\x6B' + \
        b'\x00\x00\x00\x0C' + \
        b'\x00\x90\x40\x40' + \
        b'\x40\x80\x40\x40' + \
        b'\x00\xFF\x2F\x00' + \
        b'\x4D\x54\x72\x6B' + \
        b'\x00\x00\x00\x04' + \
        b'\x00\xFF\x2F\x00'

class TestMidiFileReader(unittest.TestCase):
    def test_ctor(self):
        with self.assertRaises(Exception):
            MidiFileReader(None)

    def test_read(self):
        mfr = MidiFileReader.CreateFromBytes(_test)
        data = mfr.Read()
        self.assertTrue(isinstance(data, MidiFileData))
        self.assertTrue(isinstance(data.Header, MidiFileHeader))
        self.assertEqual(data.Header.Format, 1)
        self.assertEqual(data.Header.TrackCount, 2)
        self.assertEqual(data.Header.Resolution, 480)
        self.assertEqual(len(data.Tracks), 2)

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


import os

symbols_to_be_replaced = ["-", ",", ".", "!", "?"]

file_name = 'text.txt'
root_path = os.path.dirname(os.path.abspath(__file__))
file_path = os.path.join(root_path,file_name)

with open(file_path,'r') as file:
  text = file.readlines()

# readlines() gives us a list with the text separated as string for each new line for example:
# i am good
# so good
#['i am good\n', 'so good']

for string in range(0,len(text), 2):
  for symbol in symbols_to_be_replaced:
    text[string] = text[string].replace(symbol, '@')

  print(*text[string].split()[::-1], sep = ' ')


# https://leetcode.com/problems/task-scheduler/
# leetcode
# 그리디
# 테스크 스케줄러

# 해결 못하고 책 풀이 분석함

#%%
tasks = ["A","A","A","B","B","B"]
n = 2
# tasks = ["A","A","A","B","B","B"]
# n = 0
# tasks = ["A","A","A","A","A","A","B","C","D","E","F","G"]
# n = 2

#%%
from collections import Counter
count = Counter(tasks)
count
# %%
import heapq
heap = []
# %%
for _item, _count in count.items():
    heapq.heappush(heap, ((-1) * _count, _item))
heap
#%%
answer = 0
window = []
_answer = []

if n == 0:
    answer = len(tasks)
answer
#%%
while heap:
    print(f'heap : ', heap)
    answer += 1

    item = None
    # 초반 window 크기가 n보다 작을 때
    if len(window) < n:
        # window 공백 수가 heap의 문자 수보다 많다면
        if window.count("") >= len(heap):
            # 공백 추가
            window.append("")
            _answer.append("") # 삭제
            print('case 1-1')
            print(f'_answer : {_answer}')

        else:
            _temp = []
            while True:
                item = heapq.heappop(heap)
                # window에 문자가 이미 존재하는 경우에만 _temp 저장
                if item[1] in window:
                    _temp.append((item[0], item[1]))

                # window에 없는 문자를 찾아 삽입하기
                if item[1] not in window:
                    window.append(item[1])

                    _answer.append(item[1])
                    print(f'_answer : {_answer}')

                    # _temp -> heap 복원
                    for _item in _temp:
                        heapq.heappush(heap, _item)
                    break
            print('case 1-2')

        print(f'case 1, window : {window}')

    # window 수 == n 인 경우
    else:
        item = heapq.heappop(heap)
        if item[1] in window:
            window.remove(item[1])
            window.append("")
            print(f'case 2, window : {window}')
            _answer.append("")
            print(f'_answer : {_answer}')
        else:
            window.pop(0)
            window.append(item[1])
            print(f'case 3, window : {window}')
            _answer.append(item[1])
            print(f'_answer : {_answer}')

    if item and item[0] < -1:
        heapq.heappush(heap, (item[0] + 1, item[1]))

    print(f'answer : {answer}')
    print(f'heap {heap}')
    print('___________________')
answer
# %%

# 책 풀이 분석
import collections
class Solution:
    def leastInterval(self, tasks: List[str], n: int) -> int:
        counter = collections.Counter(tasks)
        result = 0

        while True:
            sub_count = 0

            # 개수 많은 순서로 추출
            # (n + 1)개 만큼 추출하여 불필요한 idle 수를 제거
            for task, _ in counter.most_common(n + 1):
                sub_count += 1
                result += 1

                counter.subtract(task)
                # 0 이하인 아이템을 목록에서 완전히 제거
                counter += collections.Counter()

            if not counter:
                break

            # 필요한 idle 개수를 더함
            result += n - sub_count + 1

        return result


from __future__ import print_function
from SOAP import *
import os
import profile
import sys
import pdb
import numpy as np

rfeature=[['d',0,15,0.5]]
#'sftype':'psn3','par':range(1,15,3),'parvalue':range(4)
runenv.otherrun=False

# uniform refers to the number of anchor points in the recovery function
par2={'uniform':2,'featurebins':rfeature[0],'distribute':'lownumberpriority','firstpoint':2.25}
par3={'uniform':3,'featurebins':rfeature[0],'distribute':'lownumberpriority','firstpoint':2.25}
par4={'uniform':4,'featurebins':rfeature[0],'distribute':'lownumberpriority','firstpoint':2.25}
par5={'uniform':5,'featurebins':rfeature[0],'distribute':'lownumberpriority','firstpoint':2.25}
par6={'uniform':6,'featurebins':rfeature[0],'distribute':'lownumberpriority','firstpoint':2.25}
par7={'uniform':7,'featurebins':rfeature[0],'distribute':'lownumberpriority','firstpoint':2.25}
par8={'uniform':8,'featurebins':rfeature[0],'distribute':'lownumberpriority','firstpoint':2.25}

# recovery function details
slo={'key':'sacnfflex4',
    'valueset':{
                    'sacnfflex3':{'sftype':'sacnf','par':par3,'parvalue':[1,1,1,1,1]},
                    'sacnfflex4':{'sftype':'sacnf','par':par4,'parvalue':[1,1,1,1,1,1]},
                    'sacnfflex5':{'sftype':'sacnf','par':par5,'parvalue':[1,1,1,1,1,1,1]},
                    'sacnfflex6':{'sftype':'sacnf','par':par6,'parvalue':[1,1,1,1,1,1,1,1]},
                    'sacnfflex7':{'sftype':'sacnf','par':par7,'parvalue':[1,1,1,1,1,1,1,1,1]},
                    'sacnfflex8':{'sftype':'sacnf','par':par8,'parvalue':[1,1,1,1,1,1,1,1,1,1]},
                    'ig':{'sftype':'sacnf','par':[0],'parvalue':[2.0]},
                    'dfire':{'sftype':'sacnf','par':[0],'parvalue':[2.0]},
                        }}
if 0:
                    for valuetype in ['aon','aoo','acn','aco','1oo','1cn','0oo']:
                            if valuetype.startswith('a'):
                                gl=['e','o','l','f','p','s']
                            elif valuetype.startswith('1'):
                                gl=['e','o','f','p']
                            elif valuetype.startswith('0'):
                                gl=['l','s']
                            for gentype in gl:
                                splinetype='s'+partype+valuetype+converttype+gentype
                                slo[splinetype+'2']={'sftype':splinetype,'par':[2.25,3.75]}
                                slo[splinetype+'3']={'sftype':splinetype,'par':[2.25,3.75,4.75]}
                                slo[splinetype+'4']={'sftype':splinetype,'par':[1.75,2.75,3.75,4.75]}

# atom typs
sfeature=[rfeature[0],'a158','as158']
pm=['','npendunsym']#unsym for generate unsymmetrical potential

# number of parallel tempering runs
ni=40

# initial values for the recovery functions
initvalues=list(np.arange(0,ni)/10.0+1)

initvalues=np.array(initvalues)

#decoyset(dsname='ppd4s',sourcedir='/bell2/gqdong/rawdecoyfiles/ppd/ppd4s/').update_ppdt_pir()

# dictionary for the recovery function
ref1={'type':'sf','features':rfeature,'sftype':slo,'par':slo,'parvalue':slo,'ratio':[1.0]}

# input PDB table definitions
scaledsp1={'type':'scaledsp','pdbset':'X_2.2A_0.25rfree','features':sfeature,'genmethod':'bs9dsp','pm':pm,'refs':[ref1],'ratio':[1.0]}
#define the benchmark scheme
#dslist=['DecoysRUS','casp58']+2xtop3_rmsd_mean_
#dslist=['DecoysRUS','casp58']+2xtop3_rmsd_mean_
#looplist=['mgl'+str(i) for i in range(4,21)]

# defines criteria for optimization
# optimizes area under the curve for 2 curves:
# 1. success rate vs. DB size for acceptable quality top1000 models on log scale
# 1. success rate vs. DB size for medium quality top1000 models on log scale
bmtype={'type':'dsscore','dslist':['aabench'],'criteria':'top1000_rlrank__rmsd10ORirmsd4FIRST+top1000_rlrank__rmsd5ORirmsd2FIRST',
    'combine':'scoresum','bm':'cs1','filters':[{'criteria':'has','criteriadict':{'rmsd':10,'irmsd':4}}],'finalcriteria':'top10__nonempty_rmsd10ORirmsd4'}#,{'criteria':'rep500'}
#top10_rmsd10_irmsd4  10xtop10_rmsd10_irmsd4+10xtop10_rmsd5_irmsd2+  +100xtop10_rmsd5_irmsd2+top10_rmsd+top10_irmsd top10_rmsd10_irmsd4+top20_rmsd+
#define the search positions #3xtop1_rmsd_mean_+NativeSelection+2xtop3_rmsd_mean_

# search for recovery function parameters
search1={'object':ref1,'key':'parvalue','pos':[0,1,2,3],'InitialGenerator':{'type':'dfire','values':initvalues}}
search2={'object':ref1,'key':'par','pos':[0,1,2,3],'InitialGenerator':{'type':'dfire','values':initvalues}}

# search for discreete variables

dsearch1={'object':[scaledsp1,bmtype],'key':['genmethod','bm'],'valueset':[['bs1dsp','bs1dsp'],['bs2dsp','bs2dsp'],['bs3dsp','bs3dsp'],['bs4dsp','bs4dsp'],['bs6dsp','bs6dsp'],['bs8dsp','bs8dsp'],['bs9dsp','bs9dsp'],['bs10dsp','bs10dsp'],['ss1','ss1'],['ss2','ss2'],['ss3','ss3'],['ss4','ss4'],['ss9','ss9']]}#'bs4dsp','bs5dsp','bs9dsp',,'bs2dsp'

# distance cut-off
dsearch2={'object':rfeature[0],'key':2,'valueset':[20,17,15,12,10,8]}#[6,5.4,5,4.8,4.6,4.4,4.2]#15, 7,6,5,4.5,4,3.5,3#,10,8,7,6,5#15,12,10, 8,7,15,12,10, 8,7,6,

# bin size
dsearch3={'object':[rfeature[0],sfeature[0]],'key':[3,3],'valueset':[[0.05,0.05],[0.1,0.1]]}
#dsearch4={'object':par,'key':'uniform','valueset':[10,9,8,7,6]}

dsearch5={'object':scaledsp1,'key':'genmethod','valueset':['bs12dsp','bs13dsp','bs14dsp','bs15dsp','bs16dsp','bs17dsp','bs20dsp']}#'bs4dsp','bs5dsp','bs9dsp',,'bs2dsp'

#dsearch6={'object':bmtype,'key':'bm','valueset':['bs12dsp','bs13dsp','bs14dsp','bs15dsp','bs16dsp','bs17dsp','bs20dsp']}#'bs4dsp','bs5dsp','bs9dsp',,'bs2dsp'

#dsearch5={'object':rfeature[0],'key':3,'valueset':[0.05,0.1,0.2]}#0.025,0.05 #,0.25,0.1,0.05 #0.05,0.1,0.25,

dsearch6={'object':slo,'key':'key','valueset':slo['valueset'].keys()}#slo['valueset'].keys()'psn5','psn3','psn4','psn8'

#dsearch6={'object':[rfeature[0],slo],'key':[2,'key'],'valueset':[[7,'sacnf73'], [7,'sacnf74'],[7,'sacnf75'],[5,'sacnf52'], [5,'sacnf53'],[6,'sacnf64'], [6,'sacnf63'],[6,'sacnf62'],]}

# smoothing - not important
#dsearch7={'object':pm,'key':0,'valueset':['','gps0.1','gps0.2','gps0.3','gps0.6','gps1.0','gps2.0','ks0.1','ks0.2','ks0.3','ks0.6','ks1.0','ks2.0']}

#dsearch10={'object':scaledsp1,'key':'pdbset','valueset':['X_2.1A_0.25rfree','X_2.1A_0.25rfree_30','X_2.1A_0.25rfree_60','X_2.1A_0.25rfree_95']}

# how to normalize the table
dsearch8={'object':pm,'key':1,'valueset':['npend.unsym','nbsum.unsym']}

# changing the genration method and the PDB set
dsearch9={'object':[scaledsp1,scaledsp1],'key':['genmethod','pdbset'],'valueset':[['cs1','X2_2.2A_0.25rfree'],['bs9','X_2.2A_0.25rfree'],['bs8','X_2.2A_0.25rfree'],['bs4','X_2.2A_0.25rfree'],['bs3','X_2.2A_0.25rfree'],['bs2','X_2.2A_0.25rfree'],['bs1','X_2.2A_0.25rfree'],['bs9','X_2.2A_0.25rfree_95'],['bs9','X_2.2A_0.25rfree_60'],['bs9','X_2.2A_0.25rfree_30']]}#,,'cs1'

#dsearch9={'object':scaledsp1,'key':'pdbset','valueset':['X_2.2A_0.25rfree','X_2.2A_0.25rfree_95','X_2.2A_0.25rfree_60','X_2.2A_0.25rfree_30']}#,,'cs1'

# sampling parameters - do not change
inner={'improved':2,'maxloop':30,'minloop':2}
outer={'improved':4,'maxloop':5018}

td=list(np.sqrt(np.linspace(1,float(10)**2,ni)))
td=np.array(td)

sampleschedule={'inner':inner,'outer':outer}
ssm={'sm':'mcp','reset_betweenruns':2,'blockupdate':True, 'exchange_method':1,
      'sample_schedule':sampleschedule,
      'stepmethod':'mxmp2.0','tune_interval':200,'add_bias':False,'temperature_distribution':td}

ssm0={'sm':'mcs','reset_betweenruns':2,'blockupdate':False,'using_globalbest':True,
      'sample_schedule':sampleschedule,
      'stepmethod':'mxmp2.0','tune_interval':201,'add_bias':False,'temperature_distribution':td}

ssm2={'sm':'mcp','reset_betweenruns':2,'blockupdate':False, 'exchange_method':1,
      'sample_schedule':sampleschedule,
      'stepmethod':'mxmp2.0','tune_interval':200,'add_bias':False,'temperature_distribution':td}

ssm20={'sm':'mcs','reset_betweenruns':2,'blockupdate':True,'using_globalbest':True,
      'sample_schedule':sampleschedule,
      'stepmethod':'mxmp2.0','tune_interval':201,'add_bias':False,'temperature_distribution':td}

ssm1={'sm':'mca','reset_betweenruns':2,'blockupdate':True, 'using_globalbest':True,
      'sample_schedule':sampleschedule,
      'stepmethod':'mxmp2.0','tune_interval':201,'add_bias':False,'temperature_distribution':np.zeros(ni)+2}

sml=[ssm20,ssm2,ssm0,ssm,ssm0,ssm,ssm0,ssm, ssm1]
#sml=[ssm2,ssm0]

#'sftype':'psn3','par':range(1,15,3),'parvalue':range(4)
#dsearch6,dsearch1,dsearch2,dsearch7,dsearch9
#define the final model
#define the final model
#

# parameters for SAS term search

# fraction of atom SAS
rfeature=[['b',0,1.0,0.1]]

sfeature=[rfeature[0],'a158']
pm=['pp5','','npend'] #simple smoothing?
sratio=[3.8] # weight for the scoring term
scaledsp2={'type':'scaledsp','pdbset':'X_2.2A_0.25rfree','features':sfeature,'genmethod':'','pm':'nbsum','bm':'ss1','refs':[],'ratio':sratio}

ref2={'type':'sf','features':rfeature,'sftype':'bins','par':range(10),'parvalue':[1 for i in range(10)],'bm':'','ratio':sratio}

search3={'object':ref2,'key':'parvalue','pos':range(0,10),'InitialGenerator':{'type':'dfire','values':list(np.zeros(40))}}

search4={'object':scaledsp2,'key':'ratio','pos':[0],'InitialGenerator':[[3.8] for i in range(40)]}

#dsearch15={'object':sratio,'key':0,'valueset':np.arange(3.5,4.5,0.1)}

# different scoring functions
# distance - distance on atom pairs, same for recovery function
# distancefixed - same as above, vary recovery function values
# distanceacc - distance and surface accessaibility
ssl={'distance':[[scaledsp1,ref1],[search1,search2]],'distancefixed':[[scaledsp1,ref1],[search1]],'distanceacc':[[scaledsp1,ref1,scaledsp2,ref2],[search1,search2,search3,search4]]}

ssmodel={'index':'distanceacc','valueset':ssl}

dsearch21={'object':ssmodel,'key':'index','valueset':ssl.keys()}

# the model to optimize
# scorers = different scoring function options (different terms)
# bmtype = success criteria
model1={'scorers':ssmodel,'bmtype':bmtype,'searches':ssmodel, 'runsperscorer':ni,
    'dsearches':[dsearch21,dsearch2,dsearch9],'sml':sml,'cvk':2,'repeat':1,'fold':3}#,dsearch2,dsearch5,dsearch6,dsearch7 #,'testperc':0.33
#dsearch1,dsearch2,dsearch6,dsearch7

#so=scorer(model=convert2old(model1))
#print(so.assess_ideal())
#print(so.assess_worst())
#print(so.assess_sascore(slevel='top1_rmsdbbif_mean_'))
#pdb.set_trace()

#opt=optimizer(scorer=so)
#opt.get_initial_value()
#optres=opt.optimize()
#profile.run('opt.optimize()')
#so.assess_model()
#pdb.set_trace()
#spso=sps(modellist=[convert2old(model1)])
#pdb.set_trace()
#spso.cv()#spl.eval_allpars()

#pdb.set_trace()
spl=spss(model=model1)

spl.currentcutoffpercinitial=0.0
spl.currentcutoffpercratio=0.0
spl.maximumround=200
#spl.eval_allpars()
spl.find_best_par()
spl.log()
pdb.set_trace()

spl.currentcutoffpercinitial=1.0
spl.currentcutoffpercratio=0.0
spl.maximumround=1
spl.find_best_par()
spl.log()


import os
import sys

def bool_env(varname: str, default: bool) -> bool:
  """Read an environment variable and interpret it as a boolean.

  True values are (case insensitive): 'y', 'yes', 't', 'true', 'on', and '1';
  false values are 'n', 'no', 'f', 'false', 'off', and '0'.

  Args:
    varname: the name of the variable
    default: the default boolean value
  Raises: ValueError if the environment variable is anything else.
  """
  val = os.getenv(varname, str(default))
  val = val.lower()
  if val in ('y', 'yes', 't', 'true', 'on', '1'):
    return True
  elif val in ('n', 'no', 'f', 'false', 'off', '0'):
    return False
  else:
    raise ValueError(
        'invalid truth value %r for environment %r' % (val, varname)
    )

class Config(object):

  def __init__(self):
    self.values = {}
    self.meta = {}
    self.FLAGS = NameSpace(self.read)
    self.use_absl = False

  def update(self, name, val):
    if self.use_absl:
      setattr(self.absl_flags.FLAGS, name, val)
    else:
      self.check_exists(name)
      if name not in self.values:
        raise Exception("Unrecognized config option: {}".format(name))
      self.values[name] = val

  def read(self, name):
    if self.use_absl:
      return getattr(self.absl_flags.FLAGS, name)
    else:
      self.check_exists(name)
      return self.values[name]

  def add_option(self, name, default, opt_type, meta_args, meta_kwargs):
    if name in self.values:
      raise Exception("Config option {} already defined".format(name))
    self.values[name] = default
    self.meta[name] = (opt_type, meta_args, meta_kwargs)

  def check_exists(self, name):
    if name not in self.values:
      raise Exception("Unrecognized config option: {}".format(name))

  def DEFINE_bool(self, name, default, *args, **kwargs):
    self.add_option(name, default, bool, args, kwargs)

  def DEFINE_integer(self, name, default, *args, **kwargs):
    self.add_option(name, default, int, args, kwargs)

  def DEFINE_string(self, name, default, *args, **kwargs):
    self.add_option(name, default, str, args, kwargs)

  def DEFINE_enum(self, name, default, *args, **kwargs):
    self.add_option(name, default, 'enum', args, kwargs)

  def config_with_absl(self):
    # Run this before calling `app.run(main)` etc
    import absl.flags as absl_FLAGS
    from absl import app, flags as absl_flags

    self.use_absl = True
    self.absl_flags = absl_flags
    absl_defs = { bool: absl_flags.DEFINE_bool,
                  int:  absl_flags.DEFINE_integer,
                  str:  absl_flags.DEFINE_string,
                  'enum': absl_flags.DEFINE_enum }

    for name, val in self.values.items():
      flag_type, meta_args, meta_kwargs = self.meta[name]
      absl_defs[flag_type](name, val, *meta_args, **meta_kwargs)

    app.call_after_init(lambda: self.complete_absl_config(absl_flags))

  def complete_absl_config(self, absl_flags):
    for name, _ in self.values.items():
      self.update(name, getattr(absl_flags.FLAGS, name))

  def parse_flags_with_absl(self):
    global already_configured_with_absl
    if not already_configured_with_absl:
      import absl.flags
      self.config_with_absl()
      absl.flags.FLAGS(sys.argv, known_only=True)
      self.complete_absl_config(absl.flags)
      already_configured_with_absl = True

class NameSpace(object):
  def __init__(self, getter):
    self._getter = getter

  def __getattr__(self, name):
    return self._getter(name)

config = Config()
flags = config
FLAGS = flags.FLAGS

already_configured_with_absl = False

flags.DEFINE_bool(
    'jax_enable_checks',
    bool_env('JAX_ENABLE_CHECKS', False),
    help='Turn on invariant checking (core.skip_checks = False)')

flags.DEFINE_bool('tf_numpy_additional_tests', True,
                  'Run tests added specifically for TF numpy')


from datetime import date, datetime, timedelta
try:
    from .song_history import load_df
    from .song_history import  get_durations
except:
    from song_history import load_df
    from song_history import  get_durations
import pandas as pd
import plotly.offline as opy
pd.options.plotting.backend = "plotly"

def to_html(figure):
    context = {}
    div = opy.plot(figure, auto_open=False, output_type='div')
    context['plot'] = div
    return context

def artist_duration(n=37):
    df, mdf = load_df()
    df = select_month(df, 7)

    durations = get_durations(list(set(df.id)))
    df = df.merge(durations, on='id', how='left')

    df_artist_track = df.groupby(['artist', 'id'])
    duration = df_artist_track['duration'].sum()
    count = df_artist_track.count()['played_at']

    total_time_artist = (count*duration).groupby('artist').sum()
    total_time_artist.sort_values(ascending=True, inplace=True)

    top_artists_ms = total_time_artist.tail(n)

    top_artists = top_artists_ms//(1000*60)  # minutes:1000*60   hours:1000*60*60

    top_artists = pd.DataFrame(top_artists).reset_index()
    top_artists.columns=['artist', 'time']
    top_artists.rename(columns={"time":"minutes"}, inplace=True)
    top_artists.index = [f'Rank: {n-i}' for i in range(n)]

    figure = top_artists.plot.barh(
        x = 'minutes',
        y = 'artist',
        color='minutes',
        hover_name = top_artists.index,
        template='plotly_dark'
    )
    figure.update(layout_showlegend=False)

    return to_html(figure)

def song_plays(n=37):
    print('Downloading to df()...')
    df, mdf = load_df()
    df = select_month(df, 7)

    name_artist = df.groupby(['name', 'artist', 'id'])  # TODO: maybe ids in front?
    _counts = name_artist.size().reset_index(name='play_count')
    sorted_counts = _counts.sort_values('play_count', ascending=True)
    top_songs = sorted_counts.tail(n)

    # ############# this should all be inside a duration f #############
    durations = get_durations(top_songs.id.unique())
    df = top_songs.merge(durations, how='left', on='id')

    df["minutes"] = (df.play_count * df.duration)/(1000*60)  # mintues:1000*60   hours:1000*60*60
    df.sort_values("minutes", ascending=True, inplace=True)

    df.index = ['Rank: '+str(n-i) for i in range(n)]

    df.rename(columns={"play_count":"count"}, inplace=True)

    #############HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH###################

    figure = df.plot.barh(
        y = 'name',
        x = 'minutes',
        labels = {
            'artist':'Artist',
            'name': 'Track',
        },
        custom_data = ['artist', 'name'],
        color='count',
        hover_name = df.index,
        hover_data=['artist', 'name', 'count'],
        template='plotly_dark'
    )
    figure.update_layout(
        showlegend=False
    )
    return to_html(figure)

def select_month(df, months=6):
    n_months_ago = (date.today() - timedelta(days=months*30)).strftime("%Y-%m-%dT00:00:00.000Z")
    return df[df.played_at > n_months_ago]

#### other tools #####

def show_named_plotly_colours():
    """
    function to display to user the colours to match plotly's named
    css colours.

    Reference:
        #https://community.plotly.com/t/plotly-colours-list/11730/3

    Returns:
        plotly dataframe with cell colour to match named colour name

    """
    s='''
        aliceblue, antiquewhite, aqua, aquamarine, azure,
        beige, bisque, black, blanchedalmond, blue,
        blueviolet, brown, burlywood, cadetblue,
        chartreuse, chocolate, coral, cornflowerblue,
        cornsilk, crimson, cyan, darkblue, darkcyan,
        darkgoldenrod, darkgray, darkgrey, darkgreen,
        darkkhaki, darkmagenta, darkolivegreen, darkorange,
        darkorchid, darkred, darksalmon, darkseagreen,
        darkslateblue, darkslategray, darkslategrey,
        darkturquoise, darkviolet, deeppink, deepskyblue,
        dimgray, dimgrey, dodgerblue, firebrick,
        floralwhite, forestgreen, fuchsia, gainsboro,
        ghostwhite, gold, goldenrod, gray, grey, green,
        greenyellow, honeydew, hotpink, indianred, indigo,
        ivory, khaki, lavender, lavenderblush, lawngreen,
        lemonchiffon, lightblue, lightcoral, lightcyan,
        lightgoldenrodyellow, lightgray, lightgrey,
        lightgreen, lightpink, lightsalmon, lightseagreen,
        lightskyblue, lightslategray, lightslategrey,
        lightsteelblue, lightyellow, lime, limegreen,
        linen, magenta, maroon, mediumaquamarine,
        mediumblue, mediumorchid, mediumpurple,
        mediumseagreen, mediumslateblue, mediumspringgreen,
        mediumturquoise, mediumvioletred, midnightblue,
        mintcream, mistyrose, moccasin, navajowhite, navy,
        oldlace, olive, olivedrab, orange, orangered,
        orchid, palegoldenrod, palegreen, paleturquoise,
        palevioletred, papayawhip, peachpuff, peru, pink,
        plum, powderblue, purple, red, rosybrown,
        royalblue, saddlebrown, salmon, sandybrown,
        seagreen, seashell, sienna, silver, skyblue,
        slateblue, slategray, slategrey, snow, springgreen,
        steelblue, tan, teal, thistle, tomato, turquoise,
        violet, wheat, white, whitesmoke, yellow,
        yellowgreen
        '''
    li=s.split(',')
    li=[l.replace('\n','') for l in li]
    li=[l.replace(' ','') for l in li]

    import pandas as pd
    import plotly.graph_objects as go

    df=pd.DataFrame.from_dict({'colour': li})
    fig = go.Figure(data=[go.Table(
      header=dict(
        values=["Plotly Named CSS colours"],
        line_color='black', fill_color='white',
        align='center', font=dict(color='black', size=14)
      ),
      cells=dict(
        values=[df.colour],
        line_color=[df.colour], fill_color=[df.colour],
        align='center', font=dict(color='black', size=11)
      ))
    ])

    fig.show()

######################

'''
Checklist of things to do:
    X hours per artist
    X songs plays with color
    - genre plots?
'''

if __name__=='__main__':
    # print('Downloading to df()...')
    # df, mdf = load_df()

    # artist_duration()
    song_plays()

    print('end')


from tkinter import *
root = Tk()
root.geometry("400x400")
root.resizable(False, False)
lf1 = LabelFrame(root, text="Positive Comments")
lf1.pack(fill=BOTH, expand=YES)
l1 = Label(lf1, text="Place To Put The Positive Comment")
l1.pack()

lf2 = LabelFrame(root, text="Negative Comments")
lf2.pack(fill=BOTH, expand=YES)
l2 = Label(lf2, text="Place To Put The Negative Comment")
l2.pack()
mainloop()


import logging
import configparser
from novaclient import client as novaclient
from neutronclient.v2_0 import client as neutronclient

import utils
from neutronapi import NeutronIF

logging.basicConfig(level=logging.INFO)

class Booter():
    """
    Boots the corresponding topology.
    """
    def __init__(self, topo='fw_lb', *args, **kwargs):
        """
        Read from config.ini the configuration for the topologies.
        """
        self.topo = topo
        self.args = args
        self.kwargs = kwargs
        self.config = configparser.ConfigParser()
        self.config.read('config.ini')

    def up(self):
        """
        Call the booter to the corresponding topology class.
        """
        if self.topo == 'fw_lb':
            config = utils.parse_config(self.config['FwLbTopo'])
            fw_config = utils.parse_fw_config(self.config['FWaaS'])
            FwLbTopo(opts=config,
                fw_opts=fw_config,
                session=self.kwargs['session'],
                token=self.kwargs['token'],
                neutron_endpoint=self.kwargs['neutron_endpoint']).up()

class FwLbTopo():
    """
    Implements the Firewall and LoadBalancer topology:
    - Two or three networks
    - FaaS and LbaaS
    - n instances as servers
    - m instances as storage nodes
    """

    def __init__(self,
        opts=None,
        fw_opts=None,
        net_names=['net0','net1'],
        net_prefixes=['10.0.0.0/24','10.0.1.0/24'],
        subnet_names=['subnet0','subnet1'],
        dns_nameservers=['8.8.8.8'],
        router_name='r',
        router_ports=['port0','port1'],
        flavors=['tiny_personalizada','tiny_personalizada'],
        images=['trusty-server-cloudimg-amd64-cnvr','trusty-server-cloudimg-amd64-cnvr'],
        secgroups=['default','default'],
        key_names=['my_key','my_key'],
        instances=[3,3],
        userdata=['userdata/balancer-ud.txt',
                  'userdata/server-ud.txt',
                  'userdata/storage-ud.txt'],
        fixed_ips=None,
        session=None,
        token=None,
        neutron_endpoint=None,
        *args, **kwargs):
        """
        Initialize the topology. Authorization with nova and neutron using
        a session from keystone for nova and a token for neutron.
        """

        self.fw_opts = fw_opts
        self.net_names = opts.get('net_names', net_names)
        self.net_prefixes = opts.get('net_prefixes', net_prefixes)
        self.subnet_names = opts.get('subnet_names', subnet_names)
        self.dns_nameservers = opts.get('dns_nameservers', dns_nameservers)
        self.router_name = opts.get('router_name', router_name)
        self.router_ports = opts.get('router_ports', router_ports)
        self.flavors = opts.get('flavors', flavors)
        self.images = opts.get('images', images)
        self.secgroups = opts.get('secgroups', secgroups)
        self.key_names = opts.get('key_names', key_names)
        self.instances = opts.get('instances', instances)
        self.userdata = opts.get('userdata', userdata)
        self.fixed_ips = opts.get('fixed_ips')
        if session is None:
            raise ValueError('No session provided')
        if token is None:
            raise ValueError('No token provided')
        if neutron_endpoint is None:
            raise ValueError('No neutron_endpoint provided')
        self.session = session
        self.token = token
        self.neutron_endpoint = neutron_endpoint
        self.nets = []
        self.subnets = []
        self.servers = []

    def _create_net(self,neutron=None, net_name=None):
        """
        Create the network using the neutronclient.
        """
        net_body = {'network':{
                'name': net_name,
                'admin_state_up': True
            }
        }
        net = neutron.create_network(body=net_body)
        return net['network']

    def _create_subnet(self, neutron=None, subnet_name=None,
        subnet_prefix=None, net_id=None):
        """
        Create the subnet attached to a network using the
        neutronclient.
        """
        subnet_body = {'subnets':[{
                'cidr': subnet_prefix,
                'ip_version': 4,
                'network_id': net_id,
                'name': subnet_name
            }
        ]}
        subnet = neutron.create_subnet(body=subnet_body)

        body = {'subnet':{
            'dns_nameservers': self.dns_nameservers
        }}
        neutron.update_subnet(subnet['subnets'][0]['id'],
            body)

        return subnet['subnets'][0]

    def _create_router(self, neutron=None, nova=None, net_name=None,
        router_name=None, port_names=None):
        """
        Create router and add a port to the subnet in the net
        specified by the name
        """
        if isinstance(router_name, list):
            router_name = router_name[0]

        net_ids = list()
        if isinstance(net_name, list):
            for net in net_name:
                net = nova.networks.find(label=net)
                net_id = net.id
                net_ids.append(net_id)
        else:
            net = nova.networks.find(label=net_name)
            net_id = net.id
            net_ids.append(net_id)

        ext_net = nova.networks.find(label='ExtNet')
        ext_net_id = ext_net.id
        # net_ids = [net_id, ext_net_id]

        request = {'router': {'name': router_name,
                    'admin_state_up': True}}

        router = neutron.create_router(request)
        router_id = router['router']['id']

        neutron.add_gateway_router(router_id, {'network_id': ext_net_id})

        for net_id in net_ids:
            subnet_id = None
            for subnet in neutron.list_subnets()['subnets']:
                if subnet['network_id'] == net_id:
                    subnet_id = subnet['id']
                    neutron.add_interface_router(router_id,
                                             {'subnet_id': subnet_id})

    def _boot_instance(self, nova=None, image=None, flavor=None, nets=None,
        key_name=None, secgroups=None, name=None, userdata=None, fixed_ip=None,
        count=1):
        """
        Boot the instace/s using the novaclient.
        """
        image = nova.images.find(name=image)
        flavor = nova.flavors.find(name=flavor)
        secgroups = [secgroups]
        nics = []
        if not isinstance(nets,list):
            nets = [nets]
        for net in nets:
            net = nova.networks.find(label=net)
            nics.append({'net-id':net.id})
        if not fixed_ip is None:
            for i, nic in enumerate(nics):
                nic['v4-fixed-ip'] = fixed_ip[i]

        if userdata is None:
            f_userdata = None
        else:
            f_userdata = open(userdata, 'r')

        instance = nova.servers.create(name=name, image=image, flavor=flavor,
            key_name=key_name, nics=nics, max_count=count,
            min_count=count, security_groups=secgroups,
            userdata=f_userdata)

    def up(self):
        """
        Set up the topology.
        """

        nova = novaclient.Client('2', session=self.session)
        neutron = neutronclient.Client(endpoint_url=self.neutron_endpoint,
            token=self.token)
        neutron_if = NeutronIF()

        # Create nets
        logging.info('Creating networks...')
        try:
            for i in range(len(self.instances)):
                self.nets.append(self._create_net(neutron=neutron,
                    net_name=self.net_names[i]))
        except Exception as e:
            logging.error('ERROR at creating networks:')
            logging.error(e)
        else:
            logging.info('Success!')

        # Create subnets into the created nets
        logging.info('Creating subnetworks...')
        try:
            for i in range(len(self.instances)):
                self.subnets.append(self._create_subnet(neutron=neutron,
                    subnet_name=self.subnet_names[i],
                    subnet_prefix=self.net_prefixes[i],
                    net_id=self.nets[i]['id']))
        except Exception as e:
            logging.error('ERROR at creating subnetworks')
            logging.error(e)
        else:
            logging.info('Success!')

        # Create router and connect to net
        logging.info('Creating router...')
        try:
            self._create_router(neutron=neutron, nova=nova,
                net_name=self.net_names[0],
                router_name=self.router_name[0],
                port_names=self.router_ports)

            self._create_router(neutron=neutron, nova=nova,
                               net_name=self.net_names[1:],
                               router_name=self.router_name[1])
        except Exception as e:
            logging.error('ERROR at creating router')
            logging.error(e)
        else:
            logging.info('Success!')

        # Boot the load balancer instance
        logging.info('Booting load balancer instance')
        self._boot_instance(nova=nova, image=self.images[0],
                           flavor=self.flavors[0], nets=self.net_names[:2],
                           key_name=self.key_names[0],
                           secgroups=self.secgroups[0],
                           name='loadbalancer', userdata=self.userdata[0],
                           count=self.instances[0])
        logging.info('Success!')

        # Boot the server instances
        logging.info("Booting server instances")
        try:
            self._boot_instance(nova=nova, image=self.images[1],
                flavor=self.flavors[1], nets=self.net_names[1:],
                key_name=self.key_names[1],
                secgroups=self.secgroups[1],
                name='server', userdata=self.userdata[1],
                count=self.instances[1] )
        except Exception as e:
            logging.error('ERROR when creating servers')
            logging.error(e)
        else:
            logging.info('Success!')

        # Boot the storage instances
        logging.info('Booting storage instances')
        try:
            self._boot_instance(nova=nova, image=self.images[2],
                flavor=self.flavors[2], nets=self.net_names[2],
                key_name=self.key_names[2],
                secgroups=self.secgroups[2],
                name='persist_1', count=1,
                userdata=self.userdata[2],
                fixed_ip=[self.fixed_ips[2]])
            self._boot_instance(nova=nova, image=self.images[2],
                flavor=self.flavors[2], nets=self.net_names[2],
                key_name=self.key_names[2],
                secgroups=self.secgroups[2],
                name='persist_2', count=1,
                userdata=self.userdata[3],
                fixed_ip=[self.fixed_ips[3]])
        except Exception as e:
            logging.error('ERROR when creating storage instances')
            logging.error(e)
        else:
            logging.info('Success!')

        # Allocate a floating IP and associate to balancer instance
        logging.info('Allocating IP')
        try:
            for server in nova.servers.list():
                if server.name == 'loadbalancer':
                    id_ = server.id
            nova.floating_ips.create(pool='ExtNet')
            floating_ips = nova.floating_ips.list()
            floating_ip = floating_ips[0].ip
            nova.servers.add_floating_ip(server=id_,
                                         address=floating_ip)

        except Exception as e:
            logging.error('ERROR when allocating IP')
            logging.error(e)
        else:
            logging.info('Success!')

        # Create Firewall
        logging.info('Creating FWaaS')
        try:
            for fw_rule in self.fw_opts['rules']:
                neutron_if.firewall_rule_create(fw_rule)
            neutron_if.firewall_policy_create(name=self.fw_opts['policy_name'],
                                             fw_rules=self.fw_opts['policy_rules'])

            neutron_if.firewall_create(name=self.fw_opts['fw_name'],
                                      fw_policy=self.fw_opts['policy_name'],
                                      router=self.fw_opts['fw_router'])
        except Exception as e:
            logging.error('ERROR at creating FWaaS')
            logging.error(e)
        else:
            logging.info('Success!')


from model.contact import CONTACT
from selenium.webdriver.support.ui import Select
import re

class CONTACTHelper:

    def __init__(self, app):
        self.app = app

    def open_home_page(self):
        wd = self.app.wd
        if not (wd.current_url.endswith("/addressbook") and len(
                wd.find_element_by_xpath("//input[@value='Send e-Mail']")) > 0):
           wd.find_element_by_link_text("home").click()

    def create(self, add_new):
        wd = self.app.wd
        wd.find_element_by_link_text("add new").click()
        self.fill_contact_form(add_new)
        # press enter
        wd.find_element_by_xpath("//div[@id='content']/form/input[21]").click()
        self.contact_cache = None

    def fill_contact_form(self, add_new):
        wd = self.app.wd
        self.change_field_value("firstname", add_new.name)
        self.change_field_value("middlename", add_new.middlename)
        self.change_field_value("lastname", add_new.lastname)
        self.change_field_value("nickname", add_new.nickname)
        self.change_field_value("title", add_new.title)
        self.change_field_value("company", add_new.company)
        self.change_field_value("address", add_new.address)
        self.change_field_value("mobile", add_new.mobile)
        self.change_field_value("work", add_new.work)
        self.change_field_value("fax", add_new.fax)
        self.change_field_value("email", add_new.email)
        self.change_field_value("byear", add_new.byear)
        self.change_field_value("address2", add_new.address2)
        self.change_field_value("phone2", add_new.phone2)
        self.change_field_value("notes", add_new.notes)

    def change_field_value(self, field_name, text):
        wd = self.app.wd
        if text is not None:
            wd.find_element_by_name(field_name).click()
            wd.find_element_by_name(field_name).clear()
            wd.find_element_by_name(field_name).send_keys(text)

    def select_contact_by_index(self, index):
        wd = self.app.wd
        wd.find_elements_by_name("selected[]")[index].click()

    def delete_first_add_new(self):
        self.delete_add_new_by_index(0)

    def delete_add_new_by_index(self, index):
        wd = self.app.wd
        self.select_contact_by_index(index)
        # submit deletion
        wd.find_element_by_xpath("//input[@value='Delete']").click()
        wd.switch_to_alert().accept()
        self.open_home_page()
        self.contact_cache = None

    def delete_add_new_by_id(self, id):
        wd = self.app.wd
        self.select_contact_by_id(id)
        # submit deletion
        wd.find_element_by_xpath("//input[@value='Delete']").click()
        wd.switch_to_alert().accept()
        self.open_home_page()
        self.contact_cache = None

    def select_contact_by_id(self, id):
        wd = self.app.wd
        wd.find_element_by_xpath(".//input[@value='%s']" % id).click()

    def select_first_contact(self):
        wd = self.app.wd
        wd.find_element_by_name("selected[]").click()

    def mod_add_new(self):
        wd = self.app.wd
        # select first add_new
        wd.find_element_by_name("selected[]").click()
        #нажимаем на редактирование
        wd.find_element_by_xpath("//img[@alt='Edit']").click()

    def modifyok_first_add_new(self):
        self.modifyok_add_new_by_index(0)

    def modifyok_contact_by_index(self, index, new_contact_data):
        #заходим на страницу home page
        wd = self.app.wd
        # wd.find_element_by_name("selected[]").click()
        wd.find_elements_by_xpath("//img[@alt='Edit']")[index].click()
        #модификация
        self.fill_contact_form(new_contact_data)
        #подтверждаю изменения
        wd.find_element_by_name("update").click()
        #возвращаемся на страницу
        self.return_to_home_page()
        self.contact_cache = None

    def modifyok_contact_by_id(self, id, contact):
        #заходим на страницу home page
        wd = self.app.wd
        # wd.find_element_by_name("selected[]").click()
        # wd.find_element_by_css_selector("a[href='edit.php?id=%s']" % contact.id)
        wd.find_element_by_xpath('.//a[@href="edit.php?id=%s"]' % id).click()
        #модификация
        self.fill_contact_form(contact)
        #подтверждаю изменения
        wd.find_element_by_name("update").click()
        #возвращаемся на страницу
        self.return_to_home_page()
        self.contact_cache = None

    def return_to_home_page(self):
        wd = self.app.wd
        wd.find_element_by_link_text("home").click()

    def count(self):
        wd = self.app.wd
        self.open_home_page()
        return len(wd.find_elements_by_name("selected[]"))

    contact_cache = None

    def get_add_new_list(self):
        if self.contact_cache is None:
            wd = self.app.wd
            self.app.open_home_page()
            self.contact_cache = []
            for row in wd.find_elements_by_name("entry"):
                cells = row.find_elements_by_tag_name("td")
                lastname = cells[1].text
                name = cells[2].text
                address = cells[3].text
                id = cells[0].find_element_by_name("selected[]").get_attribute("value")
                all_email = cells[4].text
                all_phones = cells[5].text
                self.contact_cache.append(CONTACT(lastname=lastname, name=name, id=id, address=address, all_email_from_home_page = all_email,
                                                  all_phones_from_home_page=all_phones))
        return list(self.contact_cache)

    def open_add_new_to_edit_by_index(self, index):
        wd = self.app.wd
        self.app.open_home_page()
        row = wd.find_elements_by_name("entry")[index]
        cell = row.find_elements_by_tag_name("td")[7]
        cell.find_element_by_tag_name("a").click()

    def open_add_new_view_by_index(self, index):
        wd = self.app.wd
        self.app.open_home_page()
        row = wd.find_elements_by_name("entry")[index]
        cell = row.find_elements_by_tag_name("td")[6]
        cell.find_element_by_tag_name("a").click()

    def get_add_new_info_from_edit_page(self, index):
        wd = self.app.wd
        self.open_add_new_to_edit_by_index(index)
        name = wd.find_element_by_name("firstname").get_attribute("value")
        lastname = wd.find_element_by_name("lastname").get_attribute("value")
        id = wd.find_element_by_name("id").get_attribute("value")
        home = wd.find_element_by_name("home").get_attribute("value")
        mobile = wd.find_element_by_name("mobile").get_attribute("value")
        work = wd.find_element_by_name("work").get_attribute("value")
        phone2 = wd.find_element_by_name("phone2").get_attribute("value")
        address = wd.find_element_by_name("address").get_attribute("value")
        email = wd.find_element_by_name("email").get_attribute("value")
        email2 = wd.find_element_by_name("email2").get_attribute("value")
        email3 = wd.find_element_by_name("email3").get_attribute("value")
        return CONTACT(name=name, lastname=lastname, id=id, email=email, email2=email2, email3=email3, address=address,
                       home=home, mobile=mobile, work=work, phone2=phone2)

    def get_add_new_from_view_page(self, index):
        wd = self.app.wd
        self.open_add_new_view_by_index(index)
        text = wd.find_element_by_id("content").text
        home = re.search("H: (.*)", text).group(1)
        mobile = re.search("M: (.*)", text).group(1)
        work = re.search("W: (.*)", text).group(1)
        phone2 = re.search("P: (.*)", text).group(1)
        return CONTACT(home=home, mobile=mobile, work=work, phone2=phone2)

    def add_to_group(self, contact_id, group_id):
        wd = self.app.wd
        self.return_to_home_page()
        self.select_contact_by_id(contact_id)
        self.select_groups_by_value('to_group', group_id)
        wd.find_element_by_name('add').click()
        self.return_to_home_page()

    def select_groups_by_value(self, menu_name, value):
        wd = self.app.wd
        select = Select(wd.find_element_by_name(menu_name))
        select.select_by_value(value)

    def remove_from_group(self, contact_id, group_id):
        wd = self.app.wd
        self.return_to_home_page()
        self.select_groups_by_value('group', group_id)
        self.select_contact_by_id(contact_id)
        wd.find_element_by_name('remove').click()
        self.return_to_home_page()
        self.select_groups_by_value('group', "")