"""
AWS Certified Cloud Practitioner Practice Exam to Anki Deck Converter
This script parses practice exam markdown files and generates Anki decks.
"""
import re
import os
import glob
import random
import genanki
def generate_unique_id():
"""Generate a unique ID for Anki models and decks."""
return random.randrange(1 << 30, 1 << 31)
# Define the Anki card model with interactive quiz functionality
AWS_MODEL = genanki.Model(
generate_unique_id(),
'AWS Practice Exam Interactive Model',
fields=[
{'name': 'Question'},
{'name': 'OptionsHTML'},
{'name': 'Answer'},
{'name': 'Source'},
{'name': 'IsMultiple'}, # "true" or "false" - whether multiple answers are expected
],
templates=[
{
'name': 'Interactive Quiz Card',
'qfmt': '''
'''
options_html_parts.append(option_html)
options_html = '\n'.join(options_html_parts)
note = genanki.Note(
model=AWS_MODEL,
fields=[
q['question'],
options_html,
q['answer'],
f"{source_file} - Q{q['number']}",
"true" if is_multiple else "false"
]
)
deck.add_note(note)
return deck
def process_single_file(filepath: str, output_dir: str = None):
"""
Process a single practice exam file and create an Anki deck.
Args:
filepath: Path to the markdown file
output_dir: Output directory for the .apkg file (defaults to same as input)
"""
if output_dir is None:
output_dir = os.path.dirname(filepath)
filename = os.path.basename(filepath)
deck_name = f"AWS CCP - {filename.replace('.md', '').replace('-', ' ').title()}"
print(f"Processing: {filename}")
questions = parse_markdown_file(filepath)
print(f" Found {len(questions)} questions")
if questions:
deck = create_anki_deck(questions, deck_name, filename)
output_path = os.path.join(output_dir, filename.replace('.md', '.apkg'))
genanki.Package(deck).write_to_file(output_path)
print(f" Created: {output_path}")
return deck
else:
print(f" No questions found in {filename}")
return None
def process_all_files(directory: str, output_dir: str = None, combined: bool = True):
"""
Process all practice exam files in a directory.
Args:
directory: Directory containing practice exam markdown files
output_dir: Output directory for .apkg files
combined: If True, create a single combined deck; if False, create separate decks
"""
if output_dir is None:
output_dir = directory
pattern = os.path.join(directory, 'practice-exam-*.md')
files = sorted(glob.glob(pattern))
if not files:
print(f"No practice exam files found matching: {pattern}")
return
print(f"Found {len(files)} practice exam files")
if combined:
# Create a combined deck
all_questions = []
for filepath in files:
filename = os.path.basename(filepath)
print(f"Processing: {filename}")
questions = parse_markdown_file(filepath)
# Add source file info to each question
for q in questions:
q['source_file'] = filename
all_questions.extend(questions)
print(f" Found {len(questions)} questions")
print(f"\nTotal questions: {len(all_questions)}")
if all_questions:
deck = genanki.Deck(
generate_unique_id(),
'AWS Certified Cloud Practitioner - All Practice Exams'
)
for q in all_questions:
# Format options as interactive HTML with checkboxes or radio buttons
is_multiple = q.get('is_multiple', False)
input_type = 'checkbox' if is_multiple else 'radio'
options_html_parts = []
for opt in q['options']:
letter = opt[0] # First character is the option letter
text = opt[3:] # Skip "X. " prefix
option_html = f'''
'''
options_html_parts.append(option_html)
options_html = '\n'.join(options_html_parts)
note = genanki.Note(
model=AWS_MODEL,
fields=[
q['question'],
options_html,
q['answer'],
f"{q['source_file']} - Q{q['number']}",
"true" if is_multiple else "false"
]
)
deck.add_note(note)
output_path = os.path.join(output_dir, 'aws-ccp-all-practice-exams.apkg')
genanki.Package(deck).write_to_file(output_path)
print(f"\nCreated combined deck: {output_path}")
else:
# Create separate decks for each file
for filepath in files:
process_single_file(filepath, output_dir)
def main():
"""Main entry point."""
import argparse
parser = argparse.ArgumentParser(
description='Convert AWS Practice Exam markdown files to Anki decks'
)
parser.add_argument(
'input',
nargs='?',
default='.',
help='Input file or directory (default: current directory)'
)
parser.add_argument(
'-o', '--output',
help='Output directory for .apkg files (default: same as input)'
)
parser.add_argument(
'-s', '--separate',
action='store_true',
help='Create separate decks for each file instead of a combined deck'
)
parser.add_argument(
'-f', '--file',
help='Process a single specific file'
)
args = parser.parse_args()
if args.file:
# Process single file
process_single_file(args.file, args.output)
elif os.path.isfile(args.input):
# Input is a file
process_single_file(args.input, args.output)
else:
# Input is a directory
process_all_files(args.input, args.output, combined=not args.separate)
if __name__ == '__main__':
main()