Пример #1
0
class CardModel(QAbstractItemModel):
    """Model to be used for list and tree view."""

    class InvalidIndexError(Exception): pass
    class ModelNotActiveError(Exception): pass

    def __init__(self, parent=None):
        QAbstractListModel.__init__(self, parent)
        self.cardDb = CardDb()


    def _checkIndex(self, index):
        if index is None or not index.isValid() or index == QModelIndex():
            raise CardModel.InvalidIndexError, "Invalid index given"

    def _checkActive(self):
        if not self.isActive():
            raise CardModel.ModelNotActiveError, "Model is not active. Use open first."


    def open(self, dbpath):
        self.cardDb.open(str(dbpath))
        # FIXME why these do not work??
        self.reset()
        # ^ self.emit(SIGNAL('modelReset()'))

    def close(self):
        self.emit(SIGNAL('modelAboutToBeReset()'))
        self.cardDb.close()
        self.reset()


    def filepath(self):
        """Returns path to currently open database"""
        if self.cardDb.is_open():
            return self.cardDb.db_path
        else:
            return None

    def isActive(self):
        return self.cardDb.is_open()


    def parent(self, index):
        return QModelIndex()


    def rowCount(self, parent=QModelIndex()):
        if parent.isValid():
            return 0
        else:
            if self.cardDb.is_open():
                return self.cardDb.get_cards_count()
            else:
                return 0


    def columnCount(self, parent=QModelIndex()):
        if parent.isValid():
            return 0
        else:
            if self.cardDb.is_open():
                return 5
            else:
                return 0


    def index(self, row, column, parent=QModelIndex()):
        if row < 0 or column < 0 or not self.cardDb.is_open():
            return QModelIndex()
        else:
            #  returns index with given card id
            header = self.cardDb.get_card_headers('', row, row + 1)
            if len(header) == 1:
                return self.createIndex(row, column, int(header[0][0]))
            else:
                return QModelIndex()

    # for display role only id+question in following columns will be
    # for specific data , in the following columns

    def data(self, index, role=Qt.DisplayRole):
        self._checkIndex(index)
        if role not in (Qt.DisplayRole, Qt.UserRole):
            return QVariant()

        card = self.cardDb.get_card(index.internalId())
        if role == Qt.UserRole:
            return card
        else:
            if index.column() == 0:
                return QVariant('#%d %s' % (card.id, str(card.question).strip()))
            elif index.column() == 1:
                return QVariant('%s' % str(card.answer).strip())
            elif index.column() == 2:
                return QVariant('%s' % str(card.question_hint).strip())
            elif index.column() == 3:
                return QVariant('%s' % str(card.answer_hint).strip())
            elif index.column() == 4:
                return QVariant('%s' % str(card.score))
            else:
                return QVariant()


    def flags(self, index):
        return QAbstractListModel.flags(self, index) | Qt.ItemIsEnabled | Qt.ItemIsSelectable


    def headerData(self, section, orientation, role=Qt.DisplayRole):
        if role == Qt.DisplayRole:
            if orientation == Qt.Horizontal:
                if section == 0:
                    return QVariant("Question")
                elif section == 1:
                    return QVariant("Answer")
                elif section == 2:
                    return QVariant(tr("Question hint"))
                elif section == 3:
                    return QVariant(tr("Answer hint"))
                elif section == 4:
                    return QVariant(tr("Score"))
                else:
                    return QVariant()
            else:
                return QVariant(str(section))
        return QVariant()



    def getPreviousIndex(self, index):
        """Returns previous index before given or given if it's first."""
        self._checkIndex(index)
        if index.row() == 0:
            return index
        else:
            return self.index(index.row() - 1, 0)
        # pointer , get row before


    def getNextIndex(self, index):
        """Returns next index after given or given if it's last."""
        self._checkIndex(index)
        if index.row() == self.rowCount() - 1:
            return index
        else:
            return self.index(index.row() + 1, 0)
        # get row after ?

    # TODO
    # what about inserting rows
    # and moving rows up and down ??
    # must have parameter position or display position ??

    # TODO
    # add special handlers like rowsAboutToBeInserted etc .
    # right now only model to be reset


    def addNewCard(self):
        """Adds a new empty card."""
        self.emit(SIGNAL('modelAboutToBeReset()'))

        rowid = self.cardDb.add_card(Card())
        # TODO is it ok to return it here?
        result = self.createIndex(self.cardDb.get_cards_count(), 0, rowid)

        # cards.addCard(Card())
        # TODO
        # why these do not work ?
        self.reset()
        # self.emit(SIGNAL('modelReset()'))
        #
        return result


    def deleteCard(self, index):
        self._checkIndex(index)
        self.emit(SIGNAL('modelAboutToBeReset()'))

        self.cardDb.delete_card(index.internalId())

        # why these do not work??
        self.reset()
        # self.emit(SIGNAL('modelReset()'))
        # cards - delete_card  card_id

    # TODO question
    # how to update card if peg is somewhere else ?
    # maybe keep blob as well ?
    # the items are then splitted
    def updateCard(self, index, question, answer):
        self._checkIndex(index)

        card = Card(index.internalId(), question, answer)
        self.cardDb.update_card(card)

        # update data in the model
        self.emit(SIGNAL('dataChanged(QModelIndex)'), index)



    # TODO model should not have any algorithms - it should be just as a proxy
    # between database and any more advanced algorithm
    # e.g. database importer
    # btw. they should use the same classes with the probe program
    # TODO progress bar for importing and possibility to cancel if is a long
    # operatoin
    def importQAFile(self, file, clean=True):
        """Import cards from given question&answer file.
        @param file can be file name or file like object
        """
        self.emit(SIGNAL('modelAboutToBeReset()'))
        self._checkActive()
        if isstring(file):
            file = open(file, 'rt')
        if clean:
            self.cardDb.delete_all_cards()
        prefix = ''
        last_prefix = ''
        card = Card()
        for line in file.readlines():
            if line.upper().startswith('Q:') or line.upper().startswith('A:'):
                last_prefix = prefix
                prefix = line[:2].upper()
                line = line[3:]
                # if new card then recreate
                if prefix == 'Q:' and prefix != last_prefix:
                    if not card.is_empty():
                        self.cardDb.add_card(card, False)
                    card = Card()
                if line.strip() != '':
                    if prefix == 'Q:':
                        card.question += line
                    else: # prefix == a
                        card.answer += line
        # add last card
        if not card.is_empty():
            self.cardDb.add_card(card)

        # TODO do it in a real transaction way
        # in case of error do a rollback
        self.cardDb.commit()
        self.reset()
Пример #2
0
class TestCards(unittest.TestCase):



    def setUp(self):
        self.cards = CardDb()
        self.cards.open(':memory:')

    def tearDown(self):
        self.cards.close()

    def test_get_card(self):
        card1 = Card(1, 'question', 'answer', 'qhint', 'ahint', 13)
        card2 = Card(2, 'frage', 'antwort', 'qhint', 'ahint2', 20)
        id1 = self.cards.add_card(card1)
        id2 = self.cards.add_card(card2)
        card1a = self.cards.get_card(id1)
        card2a = self.cards.get_card(id2)
        self.assertEqual(card1, card1a)
        self.assertEqual(card2, card2a)


    def test_get_card_count(self):
        self.cards.add_card(Card(None, 'co', 'tutaj'))
        self.cards.add_card(Card(None, 'ale', 'fajnie'))
        self.assertEqual(self.cards.get_cards_count(), 2)


    def test_delete_card(self):
        id1 = self.cards.add_card(Card(None, 'one', 'eins'))
        id2 = self.cards.add_card(Card(None, 'two', 'zwei'))
        id3 = self.cards.add_card(Card(None, 'three', 'drei'))
        # delete card 1, check if other exist
        self.cards.delete_card(id1)
        self.assertFalse(self.cards.exists_card(id1))
        self.assertTrue(self.cards.exists_card(id2))
        self.assertTrue(self.cards.exists_card(id3))
        # add and delete next card
        id4 = self.cards.add_card(Card(4, 'four', 'vier'))
        self.cards.delete_card(id4)
        self.assertFalse(self.cards.exists_card(id4))
        self.assertTrue(self.cards.exists_card(id2))
        self.assertTrue(self.cards.exists_card(id3))


    def test_update_card(self):
        id1 = self.cards.add_card(Card(None, 'one', 'eins'))
        id2 = self.cards.add_card(Card(None, 'two', 'zwei'))
        id3 = self.cards.add_card(Card(None, 'three', 'drei'))
        # update second card and check if update is successful
        # and the other cards are intact
        self.cards.update_card(Card(id2, 'two!', 'zwei!'))
        self.assertEqual(self.cards.get_card(id2), Card(id2, 'two!', 'zwei!'))
        self.assertEqual(self.cards.get_card(id1), Card(id1, 'one', 'eins'))
        self.assertEqual(self.cards.get_card(id3), Card(id3, 'three', 'drei'))


    def test_get_card_headers(self):
        id1 = self.cards.add_card(Card(None, 'one', 'eins'))
        id2 = self.cards.add_card(Card(None, 'two', 'zwei'))
        id3 = self.cards.add_card(Card(None, 'three', 'drei'))
        id4 = self.cards.add_card(Card(None, 'four', 'vier'))
        id5 = self.cards.add_card(Card(None, 'five', 'fuenf'))
        id6 = self.cards.add_card(Card(None, 'six', 'sechs'))
        id7 = self.cards.add_card(Card(None, 'seven', 'sieben'))
        id8 = self.cards.add_card(Card(None, 'eight', 'acht'))
        id9 = self.cards.add_card(Card(None, 'nine', 'neun'))
        # retrieve card from rownum 0 to 0
        cards = self.cards.get_card_headers('', 0, 1)
        self.assertEqual(len(cards), 1)
        self.assertEqual(cards[0], (1, 'one'))
        # retrieve cards from rownum 1 to 1
        cards = self.cards.get_card_headers('', 1, 2)
        self.assertEqual(len(cards), 1)
        self.assertEqual(cards[0], (2, 'two'))
        # retrieve cards from rownum 2 to 4
        cards = self.cards.get_card_headers('', 2, 6)
        self.assertEqual(len(cards), 4)
        self.assertEqual(cards[0], (3, 'three'))
        self.assertEqual(cards[1], (4, 'four'))
        self.assertEqual(cards[2], (5, 'five'))
        self.assertEqual(cards[3], (6, 'six'))

        # delete some and check retrieve again
        # retrieve cards < 7
        self.cards.delete_card(id3)
        self.cards.delete_card(id4)
        cards = self.cards.get_card_headers('ID < 7', 2, 4)
        self.assertEqual(len(cards), 2)
        self.assertEqual(cards[0], (5, 'five'))
        self.assertEqual(cards[1], (6, 'six'))

        # add some cards and check retrieve again
        # retrieve last 3 cards
        id10 = self.cards.add_card(Card(None, 'ten', 'zehn'))
        id11 = self.cards.add_card(Card(None, 'eleven', 'einzehn'))
        id12 = self.cards.add_card(Card(None, 'twelve', 'zwoelf'))
        cards = self.cards.get_card_headers('', 7, 13)
        self.assertEqual(len(cards), 3)
        self.assertEqual(cards[0], (id10, 'ten'))
        self.assertEqual(cards[1], (id11, 'eleven'))
        self.assertEqual(cards[2], (id12, 'twelve'))

        # check for query when no range is added
        cards = self.cards.get_card_headers()
        self.assertEqual(len(cards), 10)

        # check for assertion error when invalid minrow, maxrow
        self.assertRaises(AssertionError, self.cards.get_card_headers, '', 4, 1)