from supplychainmodelhelper import graphoperations as go

#import csv
#import pandas as pd # 0.25.2
#import os.path

import unittest
  
#testDF = {'col1': [1,2], 'col2':[3,4]}
# # Testing mds
class Mygo(unittest.TestCase):

    # Testing graph operations
    def test_combineActorBrand(self):
        assert go.combineActorBrand("test",["one","two"]) == ["test_one","test_two"]

    def test_createGraph(self):
        actorsWrong = 3
        locationWrong = 'string'
        productWrong = (2,4)

        actorOK = ['dfdf','dfdw','ddfdwq']
        locationOK = ['dfds']
        productOK = ['dfds','dsfs']
        
        with self.assertRaises(Exception):
            myNW = go.createGraph(listOfActors=actorsWrong,\
                listOfLocations=locationOK,\
                    listOfProducts=productOK)
        with self.assertRaises(Exception):
            myNW = go.createGraph(listOfActors=actorOK,\
                listOfLocations=locationWrong,\
                    listOfProducts=productOK)
        with self.assertRaises(Exception):
            myNW = go.createGraph(listOfActors=actorOK,\
                listOfLocations=locationOK,\
                    listOfProducts=productWrong)
                
        with self.assertRaises(Exception):
            myNW = go.createGraph(listOfActors=[],\
                listOfLocations=locationOK,\
                    listOfProducts=productOK)
        with self.assertRaises(Exception):
            myNW = go.createGraph(listOfActors=actorOK,\
                listOfLocations=[],\
                    listOfProducts=productOK)
        with self.assertRaises(Exception):
            myNW = go.createGraph(listOfActors=actorOK,\
                listOfLocations=locationOK,\
                    listOfProducts=[])
        
        myNW = go.createGraph(listOfActors=actorOK,\
                listOfLocations=locationOK,\
                    listOfProducts=productOK)

        assert len(list(myNW.nodes))==len(actorOK)*len(locationOK)*len(productOK)

    def test_getListOfNodeIDs(self):
        actorsWrong = 3
        locationWrong = 'string'
        productWrong = (2,4)

        actorOK = ['dfdf','dfdw','ddfdwq']
        locationOK = ['dfds']
        productOK = ['dfds','dsfs']

        myNW = go.createGraph(listOfActors=actorOK,\
                listOfLocations=locationOK,\
                    listOfProducts=productOK)

        with self.assertRaises(Exception):
            myList = go.getListOfNodeIDs(myNW,actors=actorsWrong,\
                locations=locationOK,\
                    products=productOK)
        with self.assertRaises(Exception):
            myList = go.getListOfNodeIDs(myNW,actors=actorOK,\
                locations=locationWrong,\
                    products=productOK)
        with self.assertRaises(Exception):
            myList = go.getListOfNodeIDs(myNW,actors=actorOK,\
                locations=locationOK,\
                    products=productWrong)
                
        with self.assertRaises(Exception):
            myList = go.getListOfNodeIDs(myNW,actors=[],\
                locations=locationOK,\
                    products=productOK)
        with self.assertRaises(Exception):
            myList = go.getListOfNodeIDs(myNW,actors=actorOK,\
                locations=[],\
                    products=productOK)
        with self.assertRaises(Exception):
            myList = go.getListOfNodeIDs(myNW,actors=actorOK,\
                locations=locationOK,\
                    products=[])
        
        assert len(go.getListOfNodeIDs(myNW)) == len(actorOK)*len(locationOK)*len(productOK)
        assert len(go.getListOfNodeIDs(myNW,products=[productOK[0]]))\
             == len(actorOK)*len(locationOK)
        assert len(go.getListOfNodeIDs(myNW,locations=[locationOK[0]]))\
             == len(actorOK)*len(productOK)
        assert len(go.getListOfNodeIDs(myNW,actors=[actorOK[0]]))\
             == len(productOK)*len(locationOK)


        # test if exception about actors is raised
        # test if exception about location is raised
        # test if exception about product is raised
 
    def test_addAttr2ExistingNodes(self):
        prod = ['milk','beer','schnaps']
        act = ['producer','consumer','warehouse','store']
        loc = ['BER','SXF','TXL']
        myNW = go.createGraph(listOfActors=act,listOfLocations=loc,listOfProducts=prod)

        producerIDs = go.getListOfNodeIDs(myNW, actors=['producer'])
        listOfAGS = ['01111','01112','01113','01111','01112','01113','01111','01112','01113']

        assert go.addAttr2ExistingNodes(myNW, listOfNodeIDs=producerIDs, nameOfAttr='ags', listOfAttr=listOfAGS)

    def test_getNodeIDswAttr(self):
        prod = ['milk','beer','schnaps']
        act = ['producer','consumer','warehouse','store']
        loc = ['BER','SXF','TXL']
        myNW = go.createGraph(listOfActors=act,listOfLocations=loc,listOfProducts=prod)

        producerIDs = go.getListOfNodeIDs(myNW, actors=['producer'], products=['milk'])
        assert go.getNodeIDswAttr(myNW,nameOfAttr='location',listOfNodeIDs=producerIDs) ==\
            go.convertTup2LoT(producerIDs,loc)

    def test_convertLoT2NTsANDconvertTup2LoT(self):
        prod = ['milk','beer','schnaps']
        act = ['producer','consumer','warehouse','store']
        loc = ['BER','SXF','TXL']

        myNW = go.createGraph(listOfActors=act,listOfLocations=loc,listOfProducts=prod)
        
        senderIDs = go.getListOfNodeIDs(myNW, actors=['producer'],products=['milk'])
        receiverIDs = go.getListOfNodeIDs(myNW, actors=['consumer'],products=['milk'])
        
        myEdges4Graph = go.getEdgeID(myNW,senderIDs,receiverIDs)
        list1,list2 = go.convertLoT2NTs(myEdges4Graph)
        assert go.convertTup2LoT(list1,list2) == myEdges4Graph


    def test_proxyModel(self):
        prod = ['milk','beer','schnaps']
        act = ['producer','consumer','warehouse','store']
        loc = ['BER','SXF','TXL']
        myNW = go.createGraph(listOfActors=act,listOfLocations=loc,listOfProducts=prod)
        senderIDs = go.getListOfNodeIDs(myNW, actors=['producer'],products=['milk'])

        nationalProduction = 100.
        proxynatProduction = 10.

        print('creating list for input for proxymodel')
        proxyregProduction = [2,4,2]

        regionalProduction = go.proxyModel(inputNr=nationalProduction, proxyData=proxyregProduction, proxyNr=proxynatProduction)
        print(regionalProduction)
        
        print('creating list of tuples for input for proxymodel')
        proxyregProdWithNodeIDs = go.convertTup2LoT(senderIDs,proxyregProduction)
        
        regionalProduction = go.proxyModel(inputNr=nationalProduction, proxyData=proxyregProdWithNodeIDs, proxyNr=proxynatProduction)
        _,list2 = go.convertLoT2NTs(regionalProduction)
        assert list2 == [10*i for i in proxyregProduction]

    def test_getExistingAttrs(self):
        prod = ['milk','beer','schnaps']
        act = ['producer','consumer','warehouse','store']
        loc = ['BER','SXF','TXL']
        myNW = go.createGraph(listOfActors=act,listOfLocations=loc,listOfProducts=prod)

        assert all([x for x in ['location', 'product', 'actor'] if x in go.getExistingAttrs(myNW, gtype = 'nodes')])
        assert go.getExistingAttrs(myNW, gtype = 'edges') == []

    def test_getAllCombinations(self):
        prod = ['milk','beer','schnaps']
        act = ['producer','consumer','warehouse','store']
    
        myNewList1,myNewList2 = go.getAllCombinations(prod,act)
        assert prod*4 == myNewList1
        assert [val for val in act for _ in (0,1,2)] == myNewList2

    def test_getEdgeID(self):
        prod = ['milk','beer','schnaps']
        act = ['producer','consumer','warehouse','store']
        loc = ['BER','SXF','TXL']

        myNW = go.createGraph(listOfActors=act,listOfLocations=loc,listOfProducts=prod)
        producerIDs = go.getListOfNodeIDs(myNW, actors=['producer'])
        consumerOfSchnapsIDs = go.getListOfNodeIDs(myNW, products=['schnaps'],actors=['producer'])
      
        myEdgeIDs = go.getEdgeID(myNW, outgoingNodes=producerIDs, incomingNodes=consumerOfSchnapsIDs)
        assert myEdgeIDs == go.convertTup2LoT(producerIDs,consumerOfSchnapsIDs)

    def test_addAttr2Edges(self):
        prod = ['milk','beer','schnaps']
        act = ['producer','consumer','warehouse','store']
        loc = ['BER','SXF','TXL']
        myNW = go.createGraph(listOfActors=act,listOfLocations=loc,listOfProducts=prod)

        producerIDs = go.getListOfNodeIDs(myNW, actors=['producer'])
        consumerOfSchnapsIDs = go.getListOfNodeIDs(myNW, products=['schnaps'],actors=['producer'])
        myEdgeIDs = go.getEdgeID(myNW, outgoingNodes=producerIDs, incomingNodes=consumerOfSchnapsIDs)

        listOfShipping = [10,1,2000]
        listOfDistances = [50.2,10.3,111.2]

        assert go.addAttr2Edges(myNW, listOfEdgeIDs=myEdgeIDs, listOfContent=listOfShipping)
        assert go.addAttr2Edges(myNW, listOfEdgeIDs=myEdgeIDs, listOfContent=listOfDistances, attr='distance')

    def test_getEdgesAttr(self):
        prod = ['milk','beer','schnaps']
        act = ['producer','consumer','warehouse','store']
        loc = ['BER','SXF','TXL']
        myNW = go.createGraph(listOfActors=act,listOfLocations=loc,listOfProducts=prod)

        producerIDs = go.getListOfNodeIDs(myNW, actors=['producer'])
        consumerOfSchnapsIDs = go.getListOfNodeIDs(myNW, products=['schnaps'],actors=['producer'])
        myEdgeIDs = go.getEdgeID(myNW, outgoingNodes=producerIDs, incomingNodes=consumerOfSchnapsIDs)

        listOfShipping = [10,1,2000]
        listOfDistances = [50.2,10.3,111.2]

        assert go.addAttr2Edges(myNW, listOfEdgeIDs=myEdgeIDs, listOfContent=listOfShipping)
        assert go.addAttr2Edges(myNW, listOfEdgeIDs=myEdgeIDs, listOfContent=listOfDistances, attr='distance')

        assert [listOfDistances[1]] == go.getEdgesAttr(myNW, attr = 'distance', listOfEdgeIDs = [myEdgeIDs[1]])

    def test_getEdgeIDswAttr(self):
        prod = ['milk','beer','schnaps']
        act = ['producer','consumer','warehouse','store']
        loc = ['BER','SXF','TXL']
        myNW = go.createGraph(listOfActors=act,listOfLocations=loc,listOfProducts=prod)

        producerIDs = go.getListOfNodeIDs(myNW, actors=['producer'], products=['schnaps'])
        consumerOfSchnapsIDs = go.getListOfNodeIDs(myNW, products=['schnaps'],actors=['producer'])
        
        myEdgeIDs = go.getEdgeID(myNW, outgoingNodes=producerIDs, incomingNodes=consumerOfSchnapsIDs)

        listOfShipping = [10,1,2000]
        listOfDistances = [50.2,10.3,111.2]

        assert go.addAttr2Edges(myNW, listOfEdgeIDs=myEdgeIDs, listOfContent=listOfShipping)
        assert go.addAttr2Edges(myNW, listOfEdgeIDs=myEdgeIDs, listOfContent=listOfDistances, attr='distance')

        print(go.getEdgeIDswAttr(myNW, attr = 'weight'))
        print(go.getEdgeIDswAttr(myNW, attr = 'distance', listOfEdgeIDs = myEdgeIDs))

        nodeOut, nodeIn, dist = go.convertLoT2NTs(\
            go.getEdgeIDswAttr(myNW,'distance'))
        
        assert nodeOut == producerIDs
        assert nodeIn == consumerOfSchnapsIDs
        assert dist == listOfDistances

    def test_optFlow(self):
        prod = ['milk','beer','schnaps']
        act = ['producer','consumer','warehouse','store']
        loc = ['BER','SXF','TXL']
        myNW = go.createGraph(listOfActors=act,listOfLocations=loc,listOfProducts=prod)

        producerIDs = go.getListOfNodeIDs(myNW, actors=['producer'], products=['schnaps'])
        consumerOfSchnapsIDs = go.getListOfNodeIDs(myNW, products=['schnaps'],actors=['producer'])
        
        myNewList1,myNewList2 = go.getAllCombinations(producerIDs,consumerOfSchnapsIDs)
        myEdgeIDs = go.getEdgeID(myNW, outgoingNodes=myNewList1, incomingNodes=myNewList2)
        
        listOfDistances = [50.2,111.,333.,111.,10.3,551.1,333.,551.1,111.2]

        assert go.addAttr2Edges(myNW, listOfEdgeIDs=myEdgeIDs, listOfContent=listOfDistances, attr='distance')
                
        with self.assertRaises(Exception):
            go.optFlow(beta=0.1, \
                SCGraph=myNW, \
                    listOfSenderIDs=producerIDs, \
                        listOfReceiverIDs=consumerOfSchnapsIDs, \
                            edgeattrDistName='distance', \
                                distanceModel='exp')
        
        listOfOutGoing = [1,2,3]
        listOfIncoming = [3,2,1]
        assert go.addAttr2ExistingNodes(myNW,producerIDs,'output',listOfOutGoing)
        assert go.addAttr2ExistingNodes(myNW,consumerOfSchnapsIDs,'input',listOfIncoming)
        go.optFlow(beta=0.1, \
            SCGraph=myNW, \
                listOfSenderIDs=producerIDs, \
                    listOfReceiverIDs=consumerOfSchnapsIDs, \
                        edgeattrDistName='distance', \
                            distanceModel='exp')
        
    def test_getDistMatrix(self):
        import pandas as pd

        # creating the graph
        prod = ['milk','beer','schnaps']
        act = ['producer','consumer','warehouse','store']
        loc = ['BER','SXF','TXL']
        myNW = go.createGraph(listOfActors=act,listOfLocations=loc,listOfProducts=prod)    

        # Creating Distance matrix
        myData={loc[0]:[1,2,50],loc[1]:[2,1,49],loc[2]:[50,49,1]}
        myDF = pd.DataFrame(myData,index=loc)

        # creating list of node IDs of participants
        senderIDs = go.getListOfNodeIDs(myNW, actors=['producer'],products=['milk'])
        receiverIDs = go.getListOfNodeIDs(myNW, actors=['consumer'],products=['milk'])

        # creating edgeIDs for adding 'distance' to graph
        allcombIDs1,allcombIDs2 = go.getAllCombinations(senderIDs,receiverIDs,order='1st')
        myEdges4Graph = go.getEdgeID(myNW,allcombIDs1,allcombIDs2)
        assert go.addAttr2Edges(myNW,myEdges4Graph, myDF.values.flatten(),attr='distance')

        assert all((myDF.values == \
            go.getDistMatrix(myNW, \
                listOfSenderIDs=senderIDs, \
                    listOfReceiverIDs=receiverIDs, \
                        edgeattrDistName='distance')).flatten())
    
    def test_propFlow(self):
        prod = ['milk','beer','schnaps']
        act = ['producer','consumer','warehouse','store']
        loc = ['BER','SXF','TXL']
        myNW = go.createGraph(listOfActors=act,listOfLocations=loc,listOfProducts=prod)

        producerIDs = go.getListOfNodeIDs(myNW, actors=['producer'], products=['schnaps'])
        consumerOfSchnapsIDs = go.getListOfNodeIDs(myNW, products=['schnaps'],actors=['producer'])
        
        myNewList1,myNewList2 = go.getAllCombinations(producerIDs,consumerOfSchnapsIDs)
        myEdgeIDs = go.getEdgeID(myNW, outgoingNodes=myNewList1, incomingNodes=myNewList2)
        
        listOfDistances = [50.2,111.,333.,111.,10.3,551.1,333.,551.1,111.2]

        assert go.addAttr2Edges(myNW, listOfEdgeIDs=myEdgeIDs, listOfContent=listOfDistances, attr='distance')
  
        with self.assertRaises(Exception):
            go.propFlow(myNW,producerIDs,consumerOfSchnapsIDs)
        
        listOfOutGoing = [1,2,3]
        listOfIncoming = [3,2,1]
        assert go.addAttr2ExistingNodes(myNW,producerIDs,'output',listOfOutGoing)
        assert go.addAttr2ExistingNodes(myNW,consumerOfSchnapsIDs,'input',listOfIncoming)

        go.propFlow(myNW,producerIDs,consumerOfSchnapsIDs)

    def test_calcPotTransportDistances(self):
        import pandas as pd
        prod = ['milk','beer','schnaps']
        act = ['producer','consumer','warehouse','store']
        loc = ['BER','SXF','TXL']
        myNW = go.createGraph(listOfActors=act,listOfLocations=loc,listOfProducts=prod)  

        senderIDs = go.getListOfNodeIDs(myNW, actors=['producer'],products=['milk'])
        receiverIDs = go.getListOfNodeIDs(myNW, actors=['consumer'],products=['milk'])
        allcombIDs1,allcombIDs2 = go.getAllCombinations(senderIDs,receiverIDs,order='1st')
        myEdges4Graph = go.getEdgeID(myNW,allcombIDs1,allcombIDs2)
        sendingMilk = [10,50,40]
        receivingMilk = [30,30,40]
        assert go.addAttr2ExistingNodes(myNW,senderIDs,'output',sendingMilk)
        assert go.addAttr2ExistingNodes(myNW,receiverIDs,'input',receivingMilk)

        # building distance matrix from scratch
        myData={loc[0]:[1,2,50],loc[1]:[2,1,49],loc[2]:[50,49,1]}
        myDF = pd.DataFrame(myData,index=loc)
        print(myDF)
        assert go.addAttr2Edges(myNW,myEdges4Graph, myDF.values.flatten(),attr='distance')
        
        print('\n Give me the average transport distances for these fake data points')
        myTDs = go.calcPotTransportDistances(myNW, listOfSenderIDs=senderIDs, listOfReceiverIDs=receiverIDs, nrOfValues=5)
        assert myTDs == [5.0, 9.0, 13.0, 17.0, 21.0]

    def test_hymanANDfurnessModel(self):
        import numpy as np
        import pandas as pd
        prod = ['milk','beer','schnaps']
        act = ['producer','consumer','warehouse','store']
        loc = ['BER','SXF','TXL']
        myNW = go.createGraph(listOfActors=act,listOfLocations=loc,listOfProducts=prod)  

        senderIDs = go.getListOfNodeIDs(myNW, actors=['producer'],products=['milk'])
        receiverIDs = go.getListOfNodeIDs(myNW, actors=['consumer'],products=['milk'])
        allcombIDs1,allcombIDs2 = go.getAllCombinations(senderIDs,receiverIDs,order='1st')
        myEdges4Graph = go.getEdgeID(myNW,allcombIDs1,allcombIDs2)
        sendingMilk = [10,50,40]
        receivingMilk = [30,30,40]
        go.addAttr2ExistingNodes(myNW,senderIDs,'output',sendingMilk)
        go.addAttr2ExistingNodes(myNW,receiverIDs,'input',receivingMilk)

        # building distance matrix from scratch
        myData={loc[0]:[1,2,50],loc[1]:[2,1,49],loc[2]:[50,49,1]}
        myDF = pd.DataFrame(myData,index=loc)

        assert go.addAttr2Edges(myNW,myEdges4Graph, myDF.values.flatten(),attr='distance')
        myTDs = go.calcPotTransportDistances(myNW, listOfSenderIDs=senderIDs, listOfReceiverIDs=receiverIDs, nrOfValues=5)

        test = np.array([[ 4.89436823,4.48960544,0.61597657],[23.1959832,  23.56995789,  3.23381242],[1.90964857,  1.94043667, 36.15021101]])
        assert np.sum(np.abs(go.hymanModel(myNW, listOfSenderIDs=senderIDs, listOfReceiverIDs=receiverIDs, transportDistance=myTDs[0], tolerance = 0.01)-test)) < 0.01

    def test_calibrationProcess(self):
        pass
'''     
test calibrationProcess
'''