def setUp(self): """Load data needed for most test cases.""" identifier = os.environ.get('TINYCARDS_IDENTIFIER') password = os.environ.get('TINYCARDS_PASSWORD') if not identifier or not password: raise ValueError("Both identifier and password must be set in ENV") self.tinycards = Tinycards(identifier, password) # Delete all existing decks to start from a clean slate. self._clean_up()
def csv_to_deck(csv_path): """Creates a Tinycards deck from a CSV file. The CSV file is expected to have two columns named 'front' and 'back'. """ # Create new deck. tinycards = Tinycards(user_identifier, user_password) deck = Deck('French Words') deck = tinycards.create_deck(deck) # Extract data from CSV file. word_pairs = [] with open(csv_path, 'r') as csv_file: csv_reader = csv.DictReader(csv_file) for row in csv_reader: current_word_pair = (row['front'], row['back']) word_pairs.append(current_word_pair) # Populate deck with cards from CSV data. for pair in word_pairs: deck.add_card(pair) # Save changes to Tinycards. tinycards.update_deck(deck)
def generate_deck(id_of_first_word, target_lang, words_to_be_excluded): tinycards = Tinycards("frequencyl", "Uktuzugo_55") cards_to_be_added = [] db = sqlite3.connect("databases/words.db") cursor = db.cursor() origin_table = cursor.execute("SELECT * FROM words").fetchall() translation_table = cursor.execute(f"SELECT * FROM {target_lang}").fetchall() for i in range(id_of_first_word - 1, 6318): if origin_table[i][1] not in words_to_be_excluded: cards_to_be_added.append((origin_table[i][1], translation_table[i][2])) deck_no = 1 speech_lang_list = ["en", ""] if target_lang in tinycards_speech_langs: speech_lang_list[1] = tinycards_speech_langs[f"{target_lang}"] links = {} while len(cards_to_be_added) > 150: deck_name = f"Deck no: {deck_no}" deck = Deck(deck_name, private=True, shareable=True, tts_languages=speech_lang_list) created_deck = tinycards.create_deck(deck) for i in range(150): created_deck.add_card(cards_to_be_added[i]) del cards_to_be_added[:150] tinycards.update_deck(created_deck) links[deck_no] = created_deck.shareable_link deck_no += 1 deck_name = f"Deck no: {deck_no}" deck = Deck(deck_name, private=True, shareable=True, tts_languages=speech_lang_list) created_deck = tinycards.create_deck(deck) for i in range(len(cards_to_be_added)): created_deck.add_card(cards_to_be_added[i]) tinycards.update_deck(created_deck) links[deck_no] = created_deck.shareable_link return links
from tinycards import Tinycards from tinycards.model import Deck from PyDictionary import PyDictionary import yaml, json, sys import config dictionary = PyDictionary() tinycards = Tinycards(config.TINY_CARDS_CREDENTIALS['email'], config.TINY_CARDS_CREDENTIALS['password']) deck = Deck('12 Rules Of Life') deck = tinycards.create_deck(deck) fh = open('words.txt') for word in fh: meaning = str(dictionary.meaning(word)) translation_table = dict.fromkeys(map(ord, '{}[]\''), None) meaning = meaning.translate(translation_table) print(meaning) deck.add_card((word, meaning)) fh.close() tinycards.update_deck(deck)
class TestIntegration(unittest.TestCase): def _clean_up(self): """Before tests are run, make sure we start from a clean slate.""" all_decks = self.tinycards.get_decks() for d in all_decks: self.tinycards.delete_deck(d.id) def setUp(self): """Load data needed for most test cases.""" identifier = os.environ.get('TINYCARDS_IDENTIFIER') password = os.environ.get('TINYCARDS_PASSWORD') if not identifier or not password: raise ValueError("Both identifier and password must be set in ENV") self.tinycards = Tinycards(identifier, password) # Delete all existing decks to start from a clean slate. self._clean_up() def _test_create_empty_deck(self): """Create a new empty deck.""" new_deck = Deck('Test Deck') created_deck = self.tinycards.create_deck(new_deck) self.assertTrue(isinstance(created_deck, Deck)) self.assertEqual('', created_deck.shareable_link) self.assertEqual(DEFAULT_COVER_URL, created_deck.image_url) self.assertIsNone(created_deck.cover_image_url) num_decks = len(self.tinycards.get_decks()) self.assertEqual(1, num_decks) def _test_update_deck_without_change(self): """Commit an update without making any changes.""" first_deck = self.tinycards.get_decks()[0] updated_deck = self.tinycards.update_deck(first_deck) self.assertTrue(isinstance(updated_deck, Deck)) def _test_update_deck_title(self): """Update the title of our deck.""" test_deck = self.tinycards.find_deck_by_title('Test Deck') test_deck.title = 'Updated Test Deck' updated_deck = self.tinycards.update_deck(test_deck) self.assertTrue(isinstance(updated_deck, Deck)) self.assertEqual('Updated Test Deck', updated_deck.title) def _test_add_cards(self): """Add to cards to our deck.""" first_deck = self.tinycards.get_decks()[0] first_deck.add_card(('front test 1', 'back test 1')) first_deck.add_card(('front test 2', 'back test 2')) updated_deck = self.tinycards.update_deck(first_deck) self.assertTrue(isinstance(updated_deck, Deck)) self.assertEqual(2, len(updated_deck.cards)) def _test_delete_deck(self): """Delete the deck. Requires that a deck with title 'Updated Test Deck' was created earlier. """ first_deck = self.tinycards.find_deck_by_title('Updated Test Deck') self.tinycards.delete_deck(first_deck.id) num_decks = len(self.tinycards.get_decks()) self.assertEqual(0, num_decks) def _test_create_shareable_deck(self): """Create a new empty, shareable deck.""" new_deck = Deck('Test shareable Deck', private=True, shareable=True) created_deck = self.tinycards.create_deck(new_deck) self.assertTrue(isinstance(created_deck, Deck)) self.assertNotEqual('', created_deck.shareable_link) resp = requests.get(created_deck.shareable_link) self.assertEqual(200, resp.status_code) self._delete_deck(created_deck.id) # Clean up after ourselves. def _test_create_advanced_deck(self): """Create a new empty deck, with advanced options.""" deck = Deck('Test advanced Deck', self.tinycards.user_id, blacklisted_side_indices=[0], # Only test knowledge with back side of cards. blacklisted_question_types=NO_TYPING, # Only test knowledge with questions which do not require any typing. grading_modes=NO_TYPOS, # Stricter evaluation of answers. tts_languages=['en', 'ja'], # Text-to-speech for both front (English) and back (Japanese) sides. ) deck = self.tinycards.create_deck(deck) self._assert_advanced_options_are_set(deck) # Add a few tests cards and update the deck, in order to test PATCH with an application/json content-type: deck.add_card(('one', 'いち')) deck.add_card(('two', 'に')) deck = self.tinycards.update_deck(deck) self._assert_advanced_options_are_set(deck) # Set a cover on the deck and update it, in order to test PATCH with a multipart-form content-type: deck.cover = path_to('test_logo_blue.jpg') deck = self.tinycards.update_deck(deck) self._assert_advanced_options_are_set(deck) self._delete_deck(deck.id) # Clean up after ourselves. def _assert_advanced_options_are_set(self, deck): self.assertTrue(isinstance(deck, Deck)) self.assertEqual([0], deck.blacklisted_side_indices) self.assertEqual([['ASSISTED_PRODUCTION', 'PRODUCTION'],['ASSISTED_PRODUCTION', 'PRODUCTION']], deck.blacklisted_question_types) self.assertEqual(['NO_TYPOS', 'NO_TYPOS'], deck.grading_modes) self.assertEqual(['en', 'ja'], deck.tts_languages) def _test_create_deck_with_cover_from_file(self): """Create a new empty deck, with a cover using a local file.""" blue_cover_filepath = path_to('test_logo_blue.jpg') deck = Deck('Test Deck with cover', cover=blue_cover_filepath) deck = self.tinycards.create_deck(deck) self.assertTrue(isinstance(deck, Deck)) self._assert_cover_was_updated_with_file(blue_cover_filepath, deck.image_url) self._assert_cover_was_updated_with_file(blue_cover_filepath, deck.cover_image_url) # Add a few tests cards (to pass server-side validation) & update the deck's cover: deck.add_card(('front test 1', 'back test 1')) deck.add_card(('front test 2', 'back test 2')) red_cover_filepath = path_to('test_logo_red.png') deck.cover = red_cover_filepath deck = self.tinycards.update_deck(deck) self.assertTrue(isinstance(deck, Deck)) self._assert_cover_was_updated_with_file(red_cover_filepath, deck.image_url) self._assert_cover_was_updated_with_file(red_cover_filepath, deck.cover_image_url) self._delete_deck(deck.id) # Clean up after ourselves. def _test_create_deck_with_cover_from_url(self): """Create a new empty deck, with a cover using an image available online.""" url = 'https://d9np3dj86nsu2.cloudfront.net/thumb/5bd5092200f7fe41e1d926158b5e8243/350_403' deck = Deck('Test Deck with cover', cover=url) deck = self.tinycards.create_deck(deck) self.assertTrue(isinstance(deck, Deck)) self._assert_cover_was_updated_with_url(url, deck.image_url) self._assert_cover_was_updated_with_url(url, deck.cover_image_url) # Add a few tests cards (to pass server-side validation) & update the deck's cover: deck.add_card(('front test 1', 'back test 1')) deck.add_card(('front test 2', 'back test 2')) url = 'https://d9np3dj86nsu2.cloudfront.net/thumb/8aaa075410df4c562bdd6c42659f02e2/350_403' deck.cover = url deck = self.tinycards.update_deck(deck) self.assertTrue(isinstance(deck, Deck)) self._assert_cover_was_updated_with_url(url, deck.image_url) self._assert_cover_was_updated_with_url(url, deck.cover_image_url) self._delete_deck(deck.id) # Clean up after ourselves. def _assert_cover_was_updated_with_file(self, filepath, deck_cover_url): self.assertNotEqual(DEFAULT_COVER_URL, deck_cover_url) self.assertTrue(deck_cover_url.startswith('https://')) resp = requests.get(deck_cover_url) self.assertEqual(200, resp.status_code) with open(filepath, 'rb') as f: self.assertEqual(f.read(), resp.content) def _assert_cover_was_updated_with_url(self, url, deck_cover_url): self.assertNotEqual(DEFAULT_COVER_URL, deck_cover_url) self.assertTrue(deck_cover_url.startswith('https://')) resp_deck = requests.get(deck_cover_url) self.assertEqual(200, resp_deck.status_code) resp_source = requests.get(url) self.assertEqual(200, resp_source.status_code) self.assertEqual(resp_source.content, resp_deck.content) def _delete_deck(self, deck_id): self.tinycards.delete_deck(deck_id) num_decks = len(self.tinycards.get_decks()) self.assertEqual(0, num_decks) def test_integration(self): """Test the whole API. Needs to run serially to avoid side effects when operating on the same backend. """ self._test_create_empty_deck() self._test_update_deck_without_change() self._test_update_deck_title() self._test_add_cards() self._test_delete_deck() self._test_create_shareable_deck() self._test_create_advanced_deck() self._test_create_deck_with_cover_from_file() self._test_create_deck_with_cover_from_url() def tearDown(self): """Clean up after all tests have finished running.""" # Delete all decks created during the test routines. self._clean_up()
from tinycards import Tinycards from tinycards.model import Deck import os client = Tinycards(os.environ['TC_LOGIN'], os.environ['TC_PASSWORD']) def deck_has_duplicates(deck, front): cards = deck.cards if len(cards) != 0: for card in cards: if len(card.front.concepts) != 0: card_front = card.front.concepts[0].fact.text if card_front == front: print('Card with this word on front already exists...') return True return False def add(pair): deck = client.find_deck_by_title(os.environ['TC_CURRENT_DECK']) if deck is None: deck = Deck(os.environ['TC_CURRENT_DECK']) deck = client.create_deck(deck) if not deck_has_duplicates(deck, pair[0]): deck.add_card(pair) client.update_deck(deck)
def csv_to_deck(csv_path, deck_base_name): """ Creates a Tinycards deck from a CSV file. The CSV file is expected to have two columns """ # Extract data from CSV file. word_pairs = [] pairs_amount = 0 with open(csv_path, 'r') as csv_file: csv_reader = csv.reader(csv_file) for row in csv_reader: word_pairs.append((row[1], row[0])) pairs_amount = len(word_pairs) if not DEBUG: client_tinycards = Tinycards(user_identifier, user_password) if pairs_amount == 0: print('No word pairs were found in CSV') sys.exit() decks = math.ceil(pairs_amount / DECK_CARDS_PER_UNIT) for index, deck in enumerate(range(decks)): # Get deck by name. If doesn't exist - create new subdeck_name = f'{deck_base_name} - {index}' if not DEBUG: deck = client_tinycards.find_deck_by_title(subdeck_name) if not deck: deck = Deck(subdeck_name) deck = client_tinycards.create_deck(deck) elif DECK_REMOVE_EXISTING: deck.cards = [] # Customize deck deck.cover = DECK_COVER_PATH deck.description = ( f'{DECK_DESC}. ' f'Last updated on {datetime.now().strftime("%d/%m/%Y %H:%M:%S")}') deck.tts_languages = DECK_TTS_LANGUAGES # Populate deck with cards from CSV data for pair in range(DECK_CARDS_PER_UNIT): try: deck.add_card(word_pairs.pop()) except IndexError: pass # Save changes to Tinycards if not DEBUG: client_tinycards.update_deck(deck) print(f'Successfully created "{subdeck_name}"') if not DEBUG: print( f'Successfully created {decks} decks from {pairs_amount} word pairs' ) else: print('Dry run. No Tinycard decks were created') print( f'{decks} decks from {pairs_amount} word pairs are ready to be uploaded' )
class TestIntegration(unittest.TestCase): def _clean_up(self): """Before tests are run, make sure we start from a clean slate.""" all_decks = self.tinycards.get_decks() for d in all_decks: self.tinycards.delete_deck(d.id) def setUp(self): """Load data needed for most test cases.""" identifier = os.environ.get('TINYCARDS_IDENTIFIER') password = os.environ.get('TINYCARDS_PASSWORD') if not identifier or not password: raise ValueError("Both identifier and password must be set in ENV") self.tinycards = Tinycards(identifier, password) # Delete all existing decks to start from a clean slate. self._clean_up() def _test_create_empty_deck(self): """Create a new empty deck.""" new_deck = Deck('Test Deck', self.tinycards.user_id) created_deck = self.tinycards.create_deck(new_deck) self.assertTrue(isinstance(created_deck, Deck)) num_decks = len(self.tinycards.get_decks()) self.assertEqual(1, num_decks) def _test_update_deck_without_change(self): """Commit an update without making any changes.""" first_deck = self.tinycards.get_decks()[0] updated_deck = self.tinycards.update_deck(first_deck) self.assertTrue(isinstance(updated_deck, Deck)) def _test_update_deck_title(self): """Update the title of our deck.""" test_deck = self.tinycards.find_deck_by_title('Test Deck') test_deck.title = 'Updated Test Deck' updated_deck = self.tinycards.update_deck(test_deck) self.assertTrue(isinstance(updated_deck, Deck)) self.assertEqual('Updated Test Deck', updated_deck.title) def _test_add_cards(self): """Add to cards to our deck.""" first_deck = self.tinycards.get_decks()[0] first_deck.add_card(('front test 1', 'back test 1')) first_deck.add_card(('front test 2', 'back test 2')) updated_deck = self.tinycards.update_deck(first_deck) self.assertTrue(isinstance(updated_deck, Deck)) self.assertEqual(2, len(updated_deck.cards)) def _test_delete_deck(self): """Delete the deck. Requires that a deck with title 'Updated Test Deck' was created earlier. """ first_deck = self.tinycards.find_deck_by_title('Updated Test Deck') self.tinycards.delete_deck(first_deck.id) num_decks = len(self.tinycards.get_decks()) self.assertEqual(0, num_decks) def test_integration(self): """Test the whole API. Needs to run serially to avoid side effects when operating on the same backend. """ self._test_create_empty_deck() self._test_update_deck_without_change() self._test_update_deck_title() self._test_add_cards() self._test_delete_deck() def tearDown(self): """Clean up after all tests have finished running.""" # Delete all decks created during the test routines. self._clean_up()