import unittest

WEIGHT_VIEW = 1
WEIGHT_LIGHTREVIEW = 3
WEIGHT_HEAVYREVIEW = 9

class Customer:
    def __init__(self):
        self.bookList = []
        self.bookViewList = {}
        self.lightReviewBookList = {}
        self.heavyReviewBookList = {}

        self.startPoint = 0

    def viewBooks(self, aBookList):
        for book in aBookList:
            self.viewBook(book)

    def _getPrefCoef(self, aBook):
        point = 0
        if self.bookViewList.has_key(aBook):
            point += self.bookViewList[aBook] * WEIGHT_VIEW
        if self.lightReviewBookList.has_key(aBook):
            point += self.lightReviewBookList[aBook] * WEIGHT_LIGHTREVIEW
        if self.heavyReviewBookList.has_key(aBook):
            point += self.heavyReviewBookList[aBook] * WEIGHT_HEAVYREVIEW

        return point

    def _addBookRelation(self, aNewBook, anIncrementPoint):
        for book in self.bookList:
            aNewBook.addBookRelation(book, self._getPrefCoef(book) + self._getPrefCoef(aNewBook))
            book.addBookRelation(aNewBook, self._getPrefCoef(aNewBook) + self._getPrefCoef(book))

        self.bookList.append(aNewBook)

    def _editBookRelation(self, anEditBook, anIncrementPoint):
        for book in self.bookList:
            if book is not anEditBook:
                anEditBook.addBookRelation(book, anIncrementPoint)
                book.addBookRelation(anEditBook, anIncrementPoint)



    def _bookAction(self, aBook, anIncrementPoint):
        if not aBook in self.bookList:
            self._addBookRelation(aBook, anIncrementPoint)
        else:
            self._editBookRelation(aBook, anIncrementPoint)
            self.bookList.append(aBook)

    def viewBook(self, aBook):
        if self.bookViewList.has_key(aBook):
            self.bookViewList[aBook] += 1
        else:
            self.bookViewList[aBook] = 1

        self._bookAction(aBook, 1 * WEIGHT_VIEW)

    def getViewBookList(self):
        return tuple(self.bookList)

    def lightReviewBook(self, aBook, aPoint):
        self.lightReviewBookList[aBook] = aPoint
        self._bookAction(aBook, aPoint * WEIGHT_LIGHTREVIEW)

    def heavyReviewBook(self, aBook, aPoint):
        self.heavyReviewBookList[aBook] = aPoint
        self._bookAction(aBook, aPoint * WEIGHT_HEAVYREVIEW)


class Book:
    def __init__(self, aBookName):
        self.bookRelation = {}
        self.bookName = aBookName

    def addBookRelation(self, aBook, aRelationPoint):
        if self.bookRelation.has_key(aBook):
            self.bookRelation[aBook] += aRelationPoint
        else:
            self.bookRelation[aBook] = aRelationPoint

    def getRelatedBookList(self):
        return tuple(self.bookRelation)

    def getBookCoef(self, aBook):
        return self.bookRelation[aBook]

    def getRecommendationBookList(self):
        bookList = []
        for book in self.bookRelation.keys():
            bookList.append((self.bookRelation[book], book))

        bookList.sort()
        bookList.reverse()

        returnList = []
        for bookdata in bookList:
            returnList.append(bookdata[1])

        return tuple(returnList)

    def getRecommendationBookListLimitScore(self, aScore):
        bookList = []
        for book in self.bookRelation.keys():
            #print self.bookName, " relation : " , book.bookName, self.bookRelation[book]
            if self.bookRelation[book] >= aScore:
                bookList.append((self.bookRelation[book], book))

        bookList.sort()
        bookList.reverse()

        returnList = []
        for bookdata in bookList:
            returnList.append(bookdata[1])

        return tuple(returnList)

class TestCustomer(unittest.TestCase):
    def testOne(self):
        man = Customer()
        book1 = Book("B1")
        book2 = Book("B2")

        man.lightReviewBook(book1, 2)
        man.lightReviewBook(book2, 3)

        self.assert_ (man.lightReviewBookList == {book1:2, book2:3})

class TestRecommendationSystem(unittest.TestCase):
    def setUp(self):
        self.musician = Customer()
        self.mathematician = Customer()
        self.book1 = Book('B1')
        self.book2 = Book('B2')
        self.book3 = Book('B3')
        self.book4 = Book('B4')
        self.book5 = Book('B5')
        self.book6 = Book('B6')
        self.book7 = Book('B7')
        self.book8 = Book('B8')
        self.book9 = Book('B9')
        self.book10 = Book('B10')

    def tearDown(self):
        self.musician = None
        self.mathematician = None

    def testHeavyReviewSmall(self):
        #1,2,3,4,5 HR Point
        self.musician.heavyReviewBook(self.book1, 3)
        self.musician.heavyReviewBook(self.book2, 4)

        expected = 3*WEIGHT_HEAVYREVIEW + 4*WEIGHT_HEAVYREVIEW
        actual = self.book1.getBookCoef(self.book2)
        #self.assertEquals( book1.getBookCoef(book2), expected)
        self.assertEquals(actual, expected)

    def testHeavyReviewBig(self):
        self.musician.heavyReviewBook(self.book1, 3)
        self.musician.heavyReviewBook(self.book2, 4)

        self.assertEquals(self.book1.getBookCoef(self.book2), (3 + 4) * WEIGHT_HEAVYREVIEW)

        self.musician.heavyReviewBook(self.book3, 2)
        self.assertEquals(self.book3.getBookCoef(self.book1),(3+2) * WEIGHT_HEAVYREVIEW)
        self.assertEquals(self.book3.getBookCoef(self.book2), (2+4) * WEIGHT_HEAVYREVIEW)

        self.mathematician.heavyReviewBook(self.book2, 1)
        self.mathematician.heavyReviewBook(self.book3, 2)

        self.assertEquals(self.book2.getBookCoef(self.book3), (6+3) * WEIGHT_HEAVYREVIEW)
        self.assertEquals(self.book3.getBookCoef(self.book2), (6+3) * WEIGHT_HEAVYREVIEW)

        self.mathematician.heavyReviewBook(self.book1, 5)

        self.assertEquals(self.book1.getBookCoef(self.book3), (2+5+5) * WEIGHT_HEAVYREVIEW)
        self.assertEquals(self.book1.getBookCoef(self.book2), (7+5+1) * WEIGHT_HEAVYREVIEW)

        self.musician.heavyReviewBook(self.book4, 4)
        self.mathematician.heavyReviewBook(self.book4,3)

        self.assertEquals(self.book4.getBookCoef(self.book1), (7+8) * WEIGHT_HEAVYREVIEW)
        self.assertEquals(self.book4.getBookCoef(self.book2), (8+4) * WEIGHT_HEAVYREVIEW)
        self.assertEquals(self.book4.getBookCoef(self.book3), (6+5) * WEIGHT_HEAVYREVIEW)

    def testLightReviewBig(self):
        self.musician.lightReviewBook(self.book1, 3)
        self.musician.lightReviewBook(self.book2, 4)

        self.assertEquals(self.book1.getBookCoef(self.book2), (3 + 4) * WEIGHT_LIGHTREVIEW)

        self.musician.lightReviewBook(self.book3, 2)
        self.assertEquals(self.book3.getBookCoef(self.book1),(3+2) * WEIGHT_LIGHTREVIEW)
        self.assertEquals(self.book3.getBookCoef(self.book2), (2+4) * WEIGHT_LIGHTREVIEW)

        self.mathematician.lightReviewBook(self.book2, 1)
        self.mathematician.lightReviewBook(self.book3, 2)

        self.assertEquals(self.book2.getBookCoef(self.book3), (6+3) * WEIGHT_LIGHTREVIEW)
        self.assertEquals(self.book3.getBookCoef(self.book2), (6+3) * WEIGHT_LIGHTREVIEW)

        self.mathematician.lightReviewBook(self.book1, 5)

        self.assertEquals(self.book1.getBookCoef(self.book3), (2+5+5) * WEIGHT_LIGHTREVIEW)
        self.assertEquals(self.book1.getBookCoef(self.book2), (7+5+1) * WEIGHT_LIGHTREVIEW)

        self.musician.lightReviewBook(self.book4, 4)
        self.mathematician.lightReviewBook(self.book4,3)

        self.assertEquals(self.book4.getBookCoef(self.book1), (7+8) * WEIGHT_LIGHTREVIEW)
        self.assertEquals(self.book4.getBookCoef(self.book2), (8+4) * WEIGHT_LIGHTREVIEW)
        self.assertEquals(self.book4.getBookCoef(self.book3), (6+5) * WEIGHT_LIGHTREVIEW)



    def testLightReviewSmall(self):
        #1,2,3,4,5 LR Point
        self.musician.lightReviewBook(self.book1, 3)
        self.musician.lightReviewBook(self.book2, 4)

        expected = 3 * WEIGHT_LIGHTREVIEW + 4 * WEIGHT_LIGHTREVIEW
        actual = self.book1.getBookCoef(self.book2)
        self.assertEquals(actual, expected)

    def testViewBook(self):
        self.musician.viewBooks((self.book9,))
        self.musician.viewBooks((self.book10,))
        expectedList = (self.book9,self.book10,)

        self.assert_(expectedList == self.musician.getViewBookList())

    def testViewSmall(self):
        self.musician.viewBook(self.book9)
        self.musician.viewBook(self.book10)

        actual = self.book9.getBookCoef(self.book10)
        expected = 2
        self.assertEquals (actual, expected)

        self.musician.viewBook(self.book1)

        self.assertEquals(self.book1.getBookCoef(self.book9), 2)
        self.assertEquals(self.book1.getBookCoef(self.book10), 2)

        self.assertEquals(self.book9.getBookCoef(self.book1), 2)
        self.assertEquals(self.book9.getBookCoef(self.book10), 2)

        self.assertEquals(self.book10.getBookCoef(self.book1), 2)
        self.assertEquals(self.book10.getBookCoef(self.book9), 2)

    def testViewBig(self):
        self.musician.viewBook(self.book9)
        self.musician.viewBook(self.book10)

        actual = self.book9.getBookCoef(self.book10)
        expected = 2
        self.assertEquals (actual, expected)

        self.musician.viewBook(self.book1)

        self.assertEquals(self.book1.getBookCoef(self.book9), 2)
        self.assertEquals(self.book1.getBookCoef(self.book10), 2)

        self.assertEquals(self.book9.getBookCoef(self.book1), 2)
        self.assertEquals(self.book9.getBookCoef(self.book10), 2)

        self.assertEquals(self.book10.getBookCoef(self.book1), 2)
        self.assertEquals(self.book10.getBookCoef(self.book9), 2)

        self.mathematician.viewBook(self.book6)
        self.mathematician.viewBook(self.book7)
        self.mathematician.viewBook(self.book10)
        self.mathematician.viewBook(self.book9)

        self.assertEquals(self.book10.getBookCoef(self.book9), 4)
        self.assertEquals(self.book9.getBookCoef(self.book10), 4)

        self.assertEquals(self.book6.getBookCoef(self.book7), 2)
        self.assertEquals(self.book6.getBookCoef(self.book9), 2)

    def testVLH(self):
        self.musician.viewBook(self.book1)
        self.musician.viewBook(self.book2)
        self.assertEquals(self.book1.getBookCoef(self.book2), 2 * WEIGHT_VIEW)

        self.musician.lightReviewBook(self.book1, 2)
        self.assertEquals(self.book2.getBookCoef(self.book1), 2 * WEIGHT_LIGHTREVIEW + 2 * WEIGHT_VIEW)
        self.assertEquals(self.book1.getBookCoef(self.book2), 2 * WEIGHT_LIGHTREVIEW + 2 * WEIGHT_VIEW)

    def testRecommendationBookList(self):
        self.musician.lightReviewBook(self.book1, 3)
        self.musician.lightReviewBook(self.book2, 4)
        self.musician.lightReviewBook(self.book3, 5)
        self.musician.lightReviewBook(self.book4, 1)

        bookList = self.book1.getRecommendationBookList()
        expected = ( self.book3, self.book2, self.book4 )
        self.assertEquals(bookList, expected)

        #for book in bookList:
        #    print book.bookName


    def testRecommendationBookListBig(self):
        self.musician.lightReviewBook(self.book1, 3)
        self.musician.lightReviewBook(self.book2, 4)
        self.musician.lightReviewBook(self.book3, 5)
        self.musician.lightReviewBook(self.book4, 1)

        self.musician.heavyReviewBook(self.book1, 3)
        self.musician.heavyReviewBook(self.book2, 5)
        self.musician.heavyReviewBook(self.book3, 4)


        self.mathematician.heavyReviewBook(self.book4, 5)
        self.mathematician.heavyReviewBook(self.book9, 2)
        self.mathematician.lightReviewBook(self.book3, 3)

        bookList = self.book1.getRecommendationBookList()
        self.assertEquals(bookList,(self.book2,self.book3,self.book4))

        bookList = self.book2.getRecommendationBookList()
        self.assertEquals(bookList,(self.book3,self.book1,self.book4))

        bookList = self.book3.getRecommendationBookList()
        self.assertEquals(bookList,(self.book2,self.book1,self.book4,self.book9))

        bookList = self.book4.getRecommendationBookList()
        self.assertEquals(bookList,(self.book3,self.book9,self.book2,self.book1))

    def testRecommendationBookListLimitScore(self):
        self.musician.lightReviewBook(self.book1, 3)
        self.musician.lightReviewBook(self.book2, 4)
        self.musician.lightReviewBook(self.book3, 5)
        self.musician.lightReviewBook(self.book4, 1)

        limitedBookList = self.book1.getRecommendationBookListLimitScore(15)
        expected = (self.book3, self.book2)
        self.assertEquals(limitedBookList, expected)
    def testRecommendationBookListLimitScoreMore(self):
        self.musician.lightReviewBook(self.book1, 3)
        self.musician.lightReviewBook(self.book2, 4)
        self.musician.lightReviewBook(self.book3, 5)
        self.musician.lightReviewBook(self.book4, 1)

        self.mathematician.lightReviewBook(self.book1, 3)
        self.mathematician.lightReviewBook(self.book3, 4)
        self.mathematician.lightReviewBook(self.book5, 5)
        self.mathematician.lightReviewBook(self.book7, 5)
        self.mathematician.lightReviewBook(self.book9, 5)

        limitedBookList = self.book1.getRecommendationBookListLimitScore(15)
        expected = (self.book3,self.book9,self.book7,self.book5,self.book2)
        self.assertEquals(limitedBookList, expected)


def printBookList(aBookList):
    output = '\n'
    for book in aBookList:
        output += book.bookName
        output += ','
    print output


if __name__=="__main__":
    unittest.main(argv=('', '-v'))