# Automating Anki card creation from MCQs with AI (/writing/anki)
Anki is an incredibly powerful tool for spaced repetition learning. However, manually creating high-quality Anki cards from multiple-choice questions (MCQs) can be time-consuming. In this post, I will share my streamlined approach to generating Anki cards from MCQs using AI, significantly reducing the time and effort required.
We will cover the following steps:
1. **Extract MCQs** from a PDF file using AI.
2. **Convert extracted MCQs** into Anki cards using AI.
3. **Add the generated Anki cards** to an Anki collection for study.
## Prerequisites [#prerequisites]
Before starting, make sure you have:
1. [Python 3.8+](https://www.python.org/downloads/) installed on your system
2. [Anki desktop application](https://apps.ankiweb.net) installed and set up
3. [Google AI Studio API key](https://aistudio.google.com/apikey) for using Gemini (it's [FREE](https://ai.google.dev/gemini-api/docs/pricing))
4. I will be using the latest `gemini-2.0-flash-exp` model for this tutorial. You can use other models as well.
Due to the model's maximum output token limit of 8192, it's recommended to
process PDF files containing 50-80 MCQs at a time. If you have more MCQs,
consider splitting them into multiple files.
## Project Setup [#project-setup]
1. Create a new project directory and navigate to it:
```bash title="Terminal"
mkdir anki-mcq
cd anki-mcq
```
2. Create and activate a virtual environment:
```bash title="Terminal"
# Windows
python -m venv venv
.\venv\Scripts\activate
# Linux/macOS
python -m venv venv
source venv/bin/activate
```
3. Create a `requirements.txt` file in the `anki-mcq` directory with the following content:
```txt title="requirements.txt"
google-genai
aqt
python-dotenv
pydantic
```
Then, install the dependencies:
```bash title="Terminal"
pip install -r requirements.txt
```
The following packages are used in the code:
* [`google-genai`](https://ai.google.dev/gemini-api/docs/sdks#python): Google Gen AI SDK for interacting with the Gemini API
* [`aqt`](https://apps.ankiweb.net/): Anki's Python package for adding cards to Anki
* [`python-dotenv`](https://pypi.org/project/python-dotenv/): For loading environment variables from a `.env` file
4. Create a `.env` file in the project directory and add your API key:
```txt title=".env"
GOOGLE_AI_STUDIO_API_KEY=your_api_key_here
```
5. Create a `pdfs` directory in the project directory and place your PDF files containing MCQs there.
6. Create a `main.py` file in the project directory and add the following code:
```python title="main.py"
import logging
import pathlib
import json
import os
from pydantic import BaseModel
from google import genai
from google.genai import types
from anki.collection import Collection
from anki.notes import Note
from aqt.operations.note import add_note
from dotenv import load_dotenv
# Load environment variables from .env file
load_dotenv()
# Configure basic logging with INFO level for debugging and tracking
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
```
After following these steps, your project directory should look like this:
## Defining the Anki Card Structure [#defining-the-anki-card-structure]
Using Pydantic, we ensure structured and validated Anki cards:
```python title="main.py"
class AnkiCard(BaseModel):
Text: str # Front side of the card containing cloze deletions
Extra: str # Back side of the card with explanations
```
## Setting up AI Client [#setting-up-ai-client]
Google Gemini Flash 2.0 is used for AI-powered conversion. Replace the API key with your own:
```python title="main.py"
# Initialize the Gemini client
client = genai.Client(api_key=os.getenv("GOOGLE_AI_STUDIO_API_KEY"))
```
## Extracting MCQs from PDF [#extracting-mcqs-from-pdf]
We define `extract_mcqs()` to extract MCQs while correcting grammatical mistakes. My PDF files have scanned images, therefore I am using AI for better accuracy. You can also use `PyMuPDF` or `PyPDF2` for text-based PDF extraction.
```python title="main.py"
def extract_mcqs(filepath: pathlib.Path) -> str:
# Validate if file exists and is PDF format
if not filepath.exists():
raise FileNotFoundError(f"PDF file not found: {filepath}")
if filepath.suffix.lower() != '.pdf':
raise ValueError(f"File must be a PDF: {filepath}")
# Configure generation parameters for MCQ extraction
prompt = "Extract MCQs"
response = client.models.generate_content(
model="gemini-2.0-flash-exp", # Using Gemini 2.0 flash model for fast processing
config=types.GenerateContentConfig(
# Set system instruction for the AI model
system_instruction="Extract all the MCQs from the following pdf file. Make sure the text is grammatically correct and structured according to MCQ",
temperature=1, # Maximum creativity in responses
top_p=0.95, # High diversity in token selection
top_k=40, # Consider top 40 tokens for each step
max_output_tokens=8192, # Maximum length of generated response
response_mime_type="text/plain",
),
contents=[
# Convert PDF to bytes for API consumption
types.Part.from_bytes(
data=filepath.read_bytes(),
mime_type='application/pdf',
),
prompt
]
)
return response.text
```
## Converting MCQs to Anki Cards [#converting-mcqs-to-anki-cards]
Next, we will define a function `convert_to_anki_cards` to convert the extracted MCQs into Anki cards.
The function will take the MCQs string (generated by the previous function `extract_mcqs`)
as input and return a list of `AnkiCard` objects.\
[Read more about structured output](https://ai.google.dev/gemini-api/docs/structured-output)
```python title="main.py"
def convert_to_anki_cards(mcq_text: str) -> list[AnkiCard]:
# Convert extracted MCQs to Anki card format using AI
response = client.models.generate_content(
model="gemini-2.0-flash-exp",
config=types.GenerateContentConfig(
# Detailed system prompt explaining conversion rules
system_instruction="""
I am converting multiple-choice questions (MCQs) into Anki cloze deletion cards for MBBS students.
Instructions:
- The output should be in JSON array format, where each MCQ is converted into a JSON object.
- Each JSON object must contain:
1. "Text" – A well-structured cloze deletion statement ensuring the key concept from the MCQ is retained.
2. "Extra" – A concise (1-2 sentences) explanation providing relevant contextual or anatomical details.
- You can also restructure the MCQ itself if needed to improve readability and make the cloze deletion card more effective and to ensure the card communicates the concept effectively.
- Convert questions into assertive statements where feasible to enhance clarity and learning.
- For MCQs that ask “Which of the following is true/false,” convert them into assertive statements and use multiple cloze deletions if necessary to retain all relevant information.
- When multiple correct answers exist, use separate cloze deletions for each.
- If there are more than one key information points, use more than one cloze (but a maximum of three clozes.)
- Avoid negative (with never/no) cloze statements.
MCQs:
A young boy suffering from inflammation of parotid gland complained of severe pain in the region of the gland, in the auricle and external acoustic meatus. The accompanied pain in the ear is due to common nerve supply by:
(A) Auriculotemporal & greater auricular
(B) Auriculotemporal & chorda tympani
(C) Auriculotemporal & superior alveolar
(D) Posterior auricular & greater auricular
Which nerve does not supply the presulcal part of the tongue?
A. Facial nerve
B. Trigeminal nerve
C. Hypoglossal nerve
D. Vagus nerve
Generated JSON:
[
{
"Extra": "The auriculotemporal nerve and greater auricular nerve share sensory innervation of the parotid gland, auricle, and external acoustic meatus. Inflammation can cause referred pain.",
"Text": "Inflammation of the {{c1::parotid gland}} can cause {{c2::ear pain}} due to common nerve supply by the {{c3::auriculotemporal}} and {{c3::greater auricular}} nerves."
},
{
"Text": "The presulcal part of the tongue is supplied by {{c1::trigeminal}}, {{c2::facial}}, and {{c3::hypoglossal}} nerves.",
"Extra": "The anterior two-thirds of the tongue receives general sensation from the mandibular division of the trigeminal nerve (V3) and taste sensation from the facial nerve (via the chorda tympani). The hypoglossal nerve controls tongue movements."
}
]
Ensure that the generated cloze deletion cards clearly communicate the concept from the MCQ while maintaining accuracy and readability.
""",
temperature=0.7, # Balanced creativity vs consistency
response_mime_type="application/json",
response_schema=list[AnkiCard] # Enforce response structure
),
contents=[mcq_text]
)
# Parse JSON response into AnkiCard objects
return [AnkiCard(**card) for card in json.loads(response.text)]
```
### Understanding Cloze Deletions [#understanding-cloze-deletions]
Anki cloze deletions are fill-in-the-blank style cards where parts of text are hidden for testing. In our code, they are marked with `{{c1::text}}`, where:
* `c1`, `c2`, `c3` etc. indicate different cloze groups
* The text between `::` is what gets hidden
* Multiple clozes with the same number will be hidden simultaneously
The conversion prompt used in this tutorial is specifically engineered for
basic medical sciences MCQs. If you're working with questions from other
subjects, you'll need to modify the prompt to better suit your domain and
desired card structure.
### Conversion prompt [#conversion-prompt]
Let's break down the conversion prompt.
First, we define the context:
```rst
I am converting multiple-choice questions (MCQs) into Anki cloze deletion cards for MBBS students.
```
Then, we provide detailed instructions:
```rst
- The output should be in JSON array format, where each MCQ is converted into a JSON object.
- Each JSON object must contain:
1. "Text" – A well-structured cloze deletion statement ensuring the key concept from the MCQ is retained.
2. "Extra" – A concise (1-2 sentences) explanation providing relevant contextual or anatomical details.
- You can also restructure the MCQ itself if needed to improve readability and make the cloze deletion card more effective and to ensure the card communicates the concept effectively.
- Convert questions into assertive statements where feasible to enhance clarity and learning.
- For MCQs that ask “Which of the following is true/false,” convert them into assertive statements and use multiple cloze deletions if necessary to retain all relevant information.
- When multiple correct answers exist, use separate cloze deletions for each.
- If there are more than one key information points, use more than one cloze (but a maximum of three clozes.)
- Avoid negative (with never/no) cloze statements.
```
Finally, we provide examples of the input and expected output:
```rst
MCQs:
A young boy suffering from inflammation of parotid gland complained of severe pain in the region of the gland, in the auricle and external acoustic meatus. The accompanied pain in the ear is due to common nerve supply by:
(A) Auriculotemporal & greater auricular
(B) Auriculotemporal & chorda tympani
(C) Auriculotemporal & superior alveolar
(D) Posterior auricular & greater auricular
Which nerve does not supply the presulcal part of the tongue?
A. Facial nerve
B. Trigeminal nerve
C. Hypoglossal nerve
D. Vagus nerve
Generated JSON:
[
{
"Extra": "The auriculotemporal nerve and greater auricular nerve share sensory innervation of the parotid gland, auricle, and external acoustic meatus. Inflammation can cause referred pain.",
"Text": "Inflammation of the {{c1::parotid gland}} can cause {{c2::ear pain}} due to common nerve supply by the {{c3::auriculotemporal}} and {{c3::greater auricular}} nerves."
},
{
"Text": "The presulcal part of the tongue is supplied by {{c1::trigeminal}}, {{c2::facial}}, and {{c3::hypoglossal}} nerves.",
"Extra": "The anterior two-thirds of the tongue receives general sensation from the mandibular division of the trigeminal nerve (V3) and taste sensation from the facial nerve (via the chorda tympani). The hypoglossal nerve controls tongue movements."
}
]
```
## Adding Cards to Anki [#adding-cards-to-anki]
Now, we will define a function `add_cards_to_anki` to add the generated Anki cards to an Anki collection.
```python title="main.py"
def add_cards_to_anki(notes, deck_name="Default", subdeck_name=None, model_name="Basic", anki_path=None, tags=None):
"""
Add cards to an Anki deck or subdeck
Args:
notes (list): List of dictionaries with "Text", "Extra" keys
deck_name (str): Name of the target deck
subdeck_name (str): Name of the subdeck (optional)
model_name (str): Name of the note type/model to use
anki_path (str): Path to Anki collection (optional)
tags (list): List of tags to add to notes
"""
# Check if we're running inside Anki
RUNNING_IN_ANKI = False
try:
import aqt
from aqt import mw
from aqt.operations.note import add_note
from aqt.utils import showInfo, tooltip
# Only set to True if mw is properly initialized
if mw and hasattr(mw, 'col') and mw.col is not None:
RUNNING_IN_ANKI = True
except ImportError:
# aqt not available, definitely not running in Anki
pass
except Exception:
# Something else went wrong with Anki imports
pass
# Use default Anki collection path if not provided
if anki_path is None and not RUNNING_IN_ANKI:
if os.name == 'nt': # Windows
anki_path = os.path.expanduser("~/AppData/Roaming/Anki2/User 1/collection.anki2")
elif os.name == 'posix': # macOS/Linux
if os.path.exists(os.path.expanduser("~/Library/Application Support/")): # macOS
anki_path = os.path.expanduser("~/Library/Application Support/Anki2/User 1/collection.anki2")
else: # Linux
anki_path = os.path.expanduser("~/.local/share/Anki2/User 1/collection.anki2")
try:
# Handle differently based on whether we're in Anki or not
if RUNNING_IN_ANKI:
# Use the Anki main window's collection
col = mw.col
else:
# Running standalone - open the collection directly
try:
col = Collection(anki_path)
except Exception as e:
logger.error(f"Could not open Anki collection. Is Anki running? Error: {str(e)}")
return False
# Retrieve the specified note type (model)
model = col.models.by_name(model_name)
if not model:
error_msg = f"Model '{model_name}' not found"
logger.error(error_msg)
return False
# Construct full deck name including subdeck if provided
full_deck_name = deck_name
if subdeck_name:
full_deck_name = f"{deck_name}::{subdeck_name}"
# Get or create the deck
deck_id = col.decks.id(full_deck_name)
# Associate model with deck
col.models.set_current(model)
# Process and add each note to the deck
added_count = 0
# Add notes in batches to avoid blocking the main thread for too long
batch_size = 5
for i in range(0, len(notes), batch_size):
batch_notes = notes[i:i+batch_size]
for note_data in batch_notes:
try:
# Create new note with selected model
note = Note(col, model)
# Set front and back of card
note.fields[0] = note_data["Text"]
note.fields[1] = note_data["Extra"]
# Add tags if provided
if tags:
note.tags.extend(tags)
# Set the deck for this note
note.note_type()["did"] = deck_id
if RUNNING_IN_ANKI:
# Add note using the Anki operation when inside Anki
add_note(
parent=mw,
note=note,
target_deck_id=deck_id
).run_in_background()
else:
# Direct addition when outside Anki
col.add_note(note, deck_id)
added_count += 1
except Exception as e:
logger.error(f"Error adding note: {str(e)}")
# Log progress for large batches
if len(notes) > batch_size and i + batch_size < len(notes):
logger.info(f"Added batch {i//batch_size + 1}/{(len(notes) + batch_size - 1)//batch_size}...")
logger.info(f"Successfully added {added_count} cards to deck '{full_deck_name}'")
return True
except Exception as e:
logger.error(f"Error working with Anki collection: {str(e)}")
return False
finally:
# Ensure collection is properly closed if we opened it
if not RUNNING_IN_ANKI and 'col' in locals() and col:
try:
col.close(save=True)
except TypeError:
# Older versions might not accept the save parameter
col.close()
```
## Putting It All Together [#putting-it-all-together]
Finally, we will define a main function to orchestrate the entire process.
```python title="main.py"
def main():
# Configuration
pdf_path = pathlib.Path("pdfs/your_mcqs.pdf")
deck_name = "UHS_MS2"
subdeck_name = "GIT::Anatomy"
try:
# Create output directory if it doesn't exist
output_dir = pathlib.Path("output")
output_dir.mkdir(exist_ok=True)
# Step 1: Extract MCQs
mcq_text = extract_mcqs(pdf_path)
# Save extracted text to output directory
txt_path = output_dir / f"{pdf_path.stem}.txt"
txt_path.write_text(mcq_text, encoding='utf-8')
logger.info(f"MCQs saved to {txt_path}")
# Step 2: Convert to Anki cards
anki_cards = convert_to_anki_cards(mcq_text)
# Step 3: Save as JSON to output directory
json_path = output_dir / f"{pdf_path.stem}.json"
json_path.write_text(
json.dumps([vars(card) for card in anki_cards],
indent=2, ensure_ascii=False),
encoding='utf-8'
)
# Step 4: Add to Anki
add_cards_to_anki(
notes=anki_cards,
deck_name=deck_name,
subdeck_name=subdeck_name,
model_name="AnKingOverhaul (AnKing / AnKingMed)",
tags=["Past_Papers", f"#{deck_name}::{subdeck_name}"]
)
except Exception as e:
logger.error(f"Error processing MCQs: {e}")
if __name__ == "__main__":
main()
```
## Usage [#usage]
1. Save your pdf file having MCQs in the `pdfs` directory.
2. Set your environment variable in `.env` file.
```txt title=".env"
GOOGLE_AI_STUDIO_API_KEY=your_api_key_here
```
3. Update the `main()` function with your PDF path and desired deck name. If your PDF files are in the same directory as the script, as shown below:
```python title="main.py"
pdf_path = pathlib.Path("pdfs/your_mcqs.pdf")
```
If your PDF files are in a different directory, you can specify the full path to the file:
```python title="main.py"
pdf_path = pathlib.Path("path/to/your_mcqs.pdf")
```
4. Run the script:
```bash title="Terminal"
python main.py
```
After running the script, we will have a new `output` folder with `.txt` file containing extracted MCQs, a `.json` file containing generated Anki cards, and the cards will be added to the specified Anki deck.
1) **ImportError: No module found**
* Make sure you've activated the virtual environment
* Verify all dependencies are installed: `pip list`
2) **API Key errors**
* Ensure the `GOOGLE_AI_STUDIO_API_KEY` is set correctly in `.env`
* Ensure the `.env` file is in the same directory as `main.py`
3) **Invalid PDF errors**
* Ensure the PDF file exists at the specified path
* Verify the PDF file is not corrupted and follows the expected format
4) **Anki Collection errors**
* Ensure Anki is closed when running the script
* Verify the collection path exists
* Check if you have proper permissions to access the collection file
## Complete Code [#complete-code]
```txt title=".env"
GOOGLE_AI_STUDIO_API_KEY=your_api_key_here
```
```txt title="requirements.txt"
google-genai
aqt
python-dotenv
pydantic
```
```python title="main.py"
import logging
import pathlib
import json
import os
from pydantic import BaseModel
from google import genai
from google.genai import types
from anki.collection import Collection
from anki.notes import Note
from aqt.operations.note import add_note
from dotenv import load_dotenv
# Load environment variables from .env file
load_dotenv()
# Configure basic logging with INFO level for debugging and tracking
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class AnkiCard(BaseModel):
Text: str # Front side of the card containing cloze deletions
Extra: str # Back side of the card with explanations
# Initialize the Gemini client
client = genai.Client(api_key=os.getenv("GOOGLE_AI_STUDIO_API_KEY"))
def extract_mcqs(filepath: pathlib.Path) -> str:
# Validate if file exists and is PDF format
if not filepath.exists():
raise FileNotFoundError(f"PDF file not found: {filepath}")
if filepath.suffix.lower() != '.pdf':
raise ValueError(f"File must be a PDF: {filepath}")
# Configure generation parameters for MCQ extraction
prompt = "Extract MCQs"
response = client.models.generate_content(
model="gemini-2.0-flash-exp", # Using Gemini 2.0 flash model for fast processing
config=types.GenerateContentConfig(
# Set system instruction for the AI model
system_instruction="Extract all the MCQs from the following pdf file. Make sure the text is grammatically correct and structured according to MCQ",
temperature=1, # Maximum creativity in responses
top_p=0.95, # High diversity in token selection
top_k=40, # Consider top 40 tokens for each step
max_output_tokens=8192, # Maximum length of generated response
response_mime_type="text/plain",
),
contents=[
# Convert PDF to bytes for API consumption
types.Part.from_bytes(
data=filepath.read_bytes(),
mime_type='application/pdf',
),
prompt
]
)
return response.text
def convert_to_anki_cards(mcq_text: str) -> list[AnkiCard]:
# Convert extracted MCQs to Anki card format using AI
response = client.models.generate_content(
model="gemini-2.0-flash-exp",
config=types.GenerateContentConfig(
# Detailed system prompt explaining conversion rules
system_instruction="""
I am converting multiple-choice questions (MCQs) into Anki cloze deletion cards for MBBS students.
Instructions:
- The output should be in JSON array format, where each MCQ is converted into a JSON object.
- Each JSON object must contain:
1. "Text" – A well-structured cloze deletion statement ensuring the key concept from the MCQ is retained.
2. "Extra" – A concise (1-2 sentences) explanation providing relevant contextual or anatomical details.
- You can also restructure the MCQ itself if needed to improve readability and make the cloze deletion card more effective and to ensure the card communicates the concept effectively.
- Convert questions into assertive statements where feasible to enhance clarity and learning.
- For MCQs that ask “Which of the following is true/false,” convert them into assertive statements and use multiple cloze deletions if necessary to retain all relevant information.
- When multiple correct answers exist, use separate cloze deletions for each.
- If there are more than one key information points, use more than one cloze (but a maximum of three clozes.)
- Avoid negative (with never/no) cloze statements.
MCQs:
A young boy suffering from inflammation of parotid gland complained of severe pain in the region of the gland, in the auricle and external acoustic meatus. The accompanied pain in the ear is due to common nerve supply by:
(A) Auriculotemporal & greater auricular
(B) Auriculotemporal & chorda tympani
(C) Auriculotemporal & superior alveolar
(D) Posterior auricular & greater auricular
Which nerve does not supply the presulcal part of the tongue?
A. Facial nerve
B. Trigeminal nerve
C. Hypoglossal nerve
D. Vagus nerve
Generated JSON:
[
{
"Extra": "The auriculotemporal nerve and greater auricular nerve share sensory innervation of the parotid gland, auricle, and external acoustic meatus. Inflammation can cause referred pain.",
"Text": "Inflammation of the {{c1::parotid gland}} can cause {{c2::ear pain}} due to common nerve supply by the {{c3::auriculotemporal}} and {{c3::greater auricular}} nerves."
},
{
"Text": "The presulcal part of the tongue is supplied by {{c1::trigeminal}}, {{c2::facial}}, and {{c3::hypoglossal}} nerves.",
"Extra": "The anterior two-thirds of the tongue receives general sensation from the mandibular division of the trigeminal nerve (V3) and taste sensation from the facial nerve (via the chorda tympani). The hypoglossal nerve controls tongue movements."
}
]
Ensure that the generated cloze deletion cards clearly communicate the concept from the MCQ while maintaining accuracy and readability.
""",
temperature=0.7, # Balanced creativity vs consistency
response_mime_type="application/json",
response_schema=list[AnkiCard] # Enforce response structure
),
contents=[mcq_text]
)
# Parse JSON response into AnkiCard objects
return [AnkiCard(**card) for card in json.loads(response.text)]
def add_cards_to_anki(notes, deck_name="Default", subdeck_name=None, model_name="Basic", anki_path=None, tags=None):
"""
Add cards to an Anki deck or subdeck
Args:
notes (list): List of dictionaries with "Text", "Extra" keys
deck_name (str): Name of the target deck
subdeck_name (str): Name of the subdeck (optional)
model_name (str): Name of the note type/model to use
anki_path (str): Path to Anki collection (optional)
tags (list): List of tags to add to notes
"""
# Check if we're running inside Anki
RUNNING_IN_ANKI = False
try:
import aqt
from aqt import mw
from aqt.operations.note import add_note
from aqt.utils import showInfo, tooltip
# Only set to True if mw is properly initialized
if mw and hasattr(mw, 'col') and mw.col is not None:
RUNNING_IN_ANKI = True
except ImportError:
# aqt not available, definitely not running in Anki
pass
except Exception:
# Something else went wrong with Anki imports
pass
# Use default Anki collection path if not provided
if anki_path is None and not RUNNING_IN_ANKI:
if os.name == 'nt': # Windows
anki_path = os.path.expanduser("~/AppData/Roaming/Anki2/User 1/collection.anki2")
elif os.name == 'posix': # macOS/Linux
if os.path.exists(os.path.expanduser("~/Library/Application Support/")): # macOS
anki_path = os.path.expanduser("~/Library/Application Support/Anki2/User 1/collection.anki2")
else: # Linux
anki_path = os.path.expanduser("~/.local/share/Anki2/User 1/collection.anki2")
try:
# Handle differently based on whether we're in Anki or not
if RUNNING_IN_ANKI:
# Use the Anki main window's collection
col = mw.col
else:
# Running standalone - open the collection directly
try:
col = Collection(anki_path)
except Exception as e:
logger.error(f"Could not open Anki collection. Is Anki running? Error: {str(e)}")
return False
# Retrieve the specified note type (model)
model = col.models.by_name(model_name)
if not model:
error_msg = f"Model '{model_name}' not found"
logger.error(error_msg)
return False
# Construct full deck name including subdeck if provided
full_deck_name = deck_name
if subdeck_name:
full_deck_name = f"{deck_name}::{subdeck_name}"
# Get or create the deck
deck_id = col.decks.id(full_deck_name)
# Associate model with deck
col.models.set_current(model)
# Process and add each note to the deck
added_count = 0
# Add notes in batches to avoid blocking the main thread for too long
batch_size = 5
for i in range(0, len(notes), batch_size):
batch_notes = notes[i:i+batch_size]
for note_data in batch_notes:
try:
# Create new note with selected model
note = Note(col, model)
# Set front and back of card
note.fields[0] = note_data["Text"]
note.fields[1] = note_data["Extra"]
# Add tags if provided
if tags:
note.tags.extend(tags)
# Set the deck for this note
note.note_type()["did"] = deck_id
if RUNNING_IN_ANKI:
# Add note using the Anki operation when inside Anki
add_note(
parent=mw,
note=note,
target_deck_id=deck_id
).run_in_background()
else:
# Direct addition when outside Anki
col.add_note(note, deck_id)
added_count += 1
except Exception as e:
logger.error(f"Error adding note: {str(e)}")
# Log progress for large batches
if len(notes) > batch_size and i + batch_size < len(notes):
logger.info(f"Added batch {i//batch_size + 1}/{(len(notes) + batch_size - 1)//batch_size}...")
logger.info(f"Successfully added {added_count} cards to deck '{full_deck_name}'")
return True
except Exception as e:
logger.error(f"Error working with Anki collection: {str(e)}")
return False
finally:
# Ensure collection is properly closed if we opened it
if not RUNNING_IN_ANKI and 'col' in locals() and col:
try:
col.close(save=True)
except TypeError:
# Older versions might not accept the save parameter
col.close()
def main():
# Configuration
pdf_path = pathlib.Path("pdfs/your_mcqs.pdf")
deck_name = "UHS_MS2"
subdeck_name = "GIT::Anatomy"
try:
# Create output directory if it doesn't exist
output_dir = pathlib.Path("output")
output_dir.mkdir(exist_ok=True)
# Step 1: Extract MCQs
mcq_text = extract_mcqs(pdf_path)
# Save extracted text to output directory
txt_path = output_dir / f"{pdf_path.stem}.txt"
txt_path.write_text(mcq_text, encoding='utf-8')
logger.info(f"MCQs saved to {txt_path}")
# Step 2: Convert to Anki cards
anki_cards = convert_to_anki_cards(mcq_text)
# Step 3: Save as JSON to output directory
json_path = output_dir / f"{pdf_path.stem}.json"
json_path.write_text(
json.dumps([vars(card) for card in anki_cards],
indent=2, ensure_ascii=False),
encoding='utf-8'
)
# Step 4: Add to Anki
add_cards_to_anki(
notes=anki_cards,
deck_name=deck_name,
subdeck_name=subdeck_name,
model_name="AnKingOverhaul (AnKing / AnKingMed)",
tags=["Past_Papers", f"#{deck_name}::{subdeck_name}"]
)
except Exception as e:
logger.error(f"Error processing MCQs: {e}")
if __name__ == "__main__":
main()
```
# From Classroom to Global Stage: My Journey to Represent Pakistan at the IBO 2022 (/writing/ibo2022)
When I was a kid, someone told me that doing well in school was the most important thing. It seemed that nothing else mattered as long as you had the highest marks in your class. However, I always believed that there was more to life than just getting good grades.
When I found out in 10th class that I could represent Pakistan at the International Biology Olympiad, I immediately recognized it as an avenue to delve deeper into my passion for biology. Unfortunately, many students prioritize chasing after marks and overlook the abundance of opportunities within reach. Upon sharing the Olympiad news with my peers, they regarded it as a mere "distraction" that should be reserved for the university phase. Nevertheless, life presents us with opportunities at all stages, and it's our responsibility to capitalize on them.
To go to International Biology Olympiad, I had to participate in the National Biology Talent Contest (NBTC). I worked hard and passed the first exam, making it into the top 50 contestants. My journey, however, was not without obstacles. Juggling my NBTC preparations alongside my F.Sc studies was a challenging feat. To complicate matters further, I had only three months to prepare a massive book of 1493 pages, Campbell Biology. However, I was determined to make the most of this opportunity and chose to prioritize the contest over my F.Sc studies.
Following three rigorous selection camps, I was chosen to be part of the team that would represent Pakistan in the International Biology Olympiad 2022 in Armenia. The team comprised me, three other competitors, and two Principal Scientists from NIBGE, a public-sector research institute.
During this contest, I also had the opportunity to work in state-of-the-art laboratories and use a transmission electron microscope at NIBGE - an experience that not many students can boast of. The practical tasks like DNA extractions, protein separation, chlorophyll activity, etc. were an incredible experience for a student at the F.Sc level.
On July 9, 2022, we left for Armenia, where the Olympiad was held. The experience of representing my country in a global competition with more than 70 other nations was truly unforgettable. Meeting students from all over the world and learning about their diverse interests, scientific developments, and education systems was undoubtedly one of the highlights of the trip.
The Olympiad proved to be quite demanding as each competitor was evaluated individually rather than in teams. The theoretical and practical assessments spanned two arduous days, during which I exerted myself to the fullest. I was awarded the Certificate of Merit, which was a significant accomplishment for both myself and my country.
It was an experience that I will never forget. Participating in this Olympiad has sparked within me a deep enthusiasm for research and a strong affection for the fields of genetics and molecular biology. As a medical student, I am aware of the opportunities available to gain experience in the field. Research projects, volunteering, and attending conferences are just a few of the ways in which students can enhance their knowledge and skills. However, Pakistan is lagging behind in providing such opportunities to medical students. Medical institutions must take the lead in promoting such activities to their students. I aspire that my participation in the Olympiad will motivate young students in Pakistan to aim for excellence in other aspects of their lives and not just focus on academic marks.
## Images [#images]
All the images (except the certificate) are publically available and taken from the [IBO 2022 official website](https://ibo2022.ysu.am/en/mediabox/photo-gallery).
* **Image 1:** A performance at the opening ceremony of the Olympiad.
* **Image 2:** Team Pakistan stepping down from the stage at the opening ceremony.
* **Image 3:** Taking oath at the opening ceremony.
* **Image 4:** Certificate of Merit.
# MDCAT Study Guide (/writing/mdcat-study-guide)
## My Academic Timeline [#my-academic-timeline]
* **2019:** 9th (490/505)
* **2020:** 10th (1066/1100)
* **2021:** 11th (500/505)
* **2022:** 12th (1041/1100)
* **2022:** [Represented Pakistan in IBO 2022 in Armenia](/writing/ibo2022)
* **2022:** UHS MDCAT (181/200)
* **2022:** NUMS MDCAT (95.367%)
* **2022:** Admission to Allama Iqbal Medical College
## Let's start... [#lets-start]
Few important points before I start this preparation guide:
1. I am not going to discuss whether you should follow the PMC 2022 syllabus or the whole book. I am **not making any predictions about the syllabus**.
2. I'll divide this guide into **five sections**: Biology, Physics, Chemistry, English, and finally Logical Reasoning
3. In each section, first I'll discuss how I prepared for MDCAT. My way of preparation was surely NOT ideal; there is always room for improvement. Therefore, in the end, I will also tell you about "the ideal way" (in my opinion) to make your preparations extraordinary.
## 1. Subject-wise Preparation [#1-subject-wise-preparation]
### 1.1. BIOLOGY [#11-biology]
Biology always has the **highest number of MCQs**, therefore is of paramount importance. The most important source for the preparation of biology is your textbook. As MDCAT 2022 was supposed to hold on the national level; therefore, I also consulted the biology books of other provinces, especially federal books.
I also made a separate [list of numerical values](https://drive.google.com/file/d/1vYt1-K7vLsPXsL7OiBi2RIt1jWTYOqL0/view) given in the book; e.g. percentage of water in the brain and bones. Similarly, I made a list of scientists and their contributions (with dates) mentioned in the book. These lists will be worth your time even if you correctly answer only one MCQ in the MDCAT.
I also made comprehensive [tabular notes of Kingdom Animalia](https://drive.google.com/file/d/1uD5MG36UXgP6ggDtxQsCBLHzKryzPEil/view) during my 11th class. These notes also helped me a lot with comparative study during MDCAT. Biology was not a major problem for me due to my participation in IBO.
Many people suggest cramming every line of the book. This technique may work for many people; but, it didn't work for me. I only knew the meaning and concepts of topics, not the exact wording.
You should know the meaning and concept behind every line. You should also focus on figures. Mostly, figures are used to summarize a topic. You should be able to explain the entire topic by looking at the figure.
### 1.2. PHYSICS [#12-physics]
In physics, contrary to other subjects, the class notes are equally important as your textbook. The shortcut formulae and tricks are essential for numerical and other questions. Book reading is important also in the case of physics.
* You should take the lectures on topics you find difficult.
* You should revise the figure tables regularly (e.g. hearing frequency ranges of different animals). You may make a separate list of these numerical figures. (It will help you in last-minute revision.)
* You should also have a grip on the exercise question and the concepts behind their answers.
* You must solve the numerical questions and examples at least one time without a calculator.
Practicing questions of physics is most important than all other subjects. I practiced from KIPS Practice Book. You may consult other books as well. During practice, you will come across many new concepts not explained in the textbook. Mark these "difficult and new" questions. Take a look at these new concepts regularly to make them a part of your long-term memory.
### 1.3. CHEMISTRY [#13-chemistry]
The unparalleled source of preparation for any subject is your textbook. The class notes are also helpful for some topics of chemistry. Also, focus on the conditions of reactions; compare these conditions with other reactions. The comparative study is very important in organic chemistry. With comparative study, you can learn maximum topics in minimum time.
Exercise MCQs of chemistry have infinite importance for MDCAT. Don't skip them. If you find time, you should also go through the exercise questions you skipped during your F.Sc.
You should practice numerical questions (both examples and exercise) at least one time. You should know the method of solution of numerical questions which require a calculator because the examiner may change the values in the question. These techniques should also be applied for physics.
Figures are also important in chemistry. For example, an examiner may ask about the direction of the current in a galvanic cell (Cu with SHE/ Zn with SHE/ Zn with Cu).
Practice is essential as it helps you to gain confidence. I practiced the KIPS Practice Book. You may consult other books (STEP and STARS). The main point of practice is to enhance your understanding and test your knowledge. Practice techniques are the same as explained above in the physics section.
### 1.4. ENGLISH [#14-english]
I prepared grammar rules from the KIPS Prep Book. Lecture notes from your teacher (or online lectures) are essential because they are concise and helpful for quick revision.
Revising rules, their exceptions and deviations, again and again, is the key to making your preparation exceptional. Again, a comparative study is also crucial in English. You should compare different examples (such as in the case of prepositions).
You should first practice UHS past paper than any other practice book. You can find the Past Papers (2008-2019) by Prof. Salman ul Waheed [here](https://img1.wsimg.com/blobby/go/05fb1474-1a8b-4249-9386-e60231b88938/downloads/2008-2019%20Past%20Papers%20MDCAT%20English%20by%20Prof.%20S.pdf?ver=1670495692870).
### 1.5. LOGICAL REASONING [#15-logical-reasoning]
Logical reasoning is not problematic at all. One of its questions, "Logical Problem" is somehow technical because you have to make Venn Diagrams to solve these questions.
Practice always makes your preparation remarkable. I practiced the KIPS Practice Book for the preparation of LR. It was more than enough. You may go for any other book.
## 2. Books and Sources for MDCAT [#2-books-and-sources-for-mdcat]
### 2.1. Biology [#21-biology]
1. First, read and thoroughly understand your textbook, then go for any other book. In my opinion, your textbook is enough for most of the topics.
2. If you want to read a higher-order and concise explanation of a topic, you may consult this online textbook: Lumen Learning Biology ([Part I](https://courses.lumenlearning.com/wm-biology1/) and [Part II](https://courses.lumenlearning.com/wm-biology2/)). Remember, don't get stuck in these extensive texts of biology as you are already short in time during MDCAT.
### 2.2. Physics [#22-physics]
Again, your textbook is the ultimate source of preparation. Your physics concepts should be clear. I didn't take any lectures in physics except for a few topics. I only followed my academy teacher. Taking notes during physics class is essential; it will help you a lot in the end. Let's move to some preparation sources:
1. The formula sheet of the "Physics in Seconds" app ([available on Play Store](https://play.google.com/store/apps/details?id=com.physics.physicsinseconds)) may come in handy for last-moment revision.
2. I took a few lectures of STEP and STARS. You may easily find these lectures on YouTube.
3. I have heard a lot about other YouTube channels for physics; like Physics with [Muhammad Arafat Khan](https://www.youtube.com/@PhysicswithMuhammadArafatkhan) and [Physics Wallah](https://www.youtube.com/@PhysicsWallah). I didn't take any of their lectures so can't comment on them.
4. For practice, I only used past papers and KIPS Practice Book.
5. You can also consult KIPS Prep Book for comparison tables, other tricks, etc.
### 2.3. Chemistry [#23-chemistry]
1. Once more, the unrivaled sources for preparation are your textbooks.
2. I listened to online lectures of the complete chemistry syllabus of MDCAT. These lectures were delivered by a very talented and experienced teacher, Prof. Wajid Ali Komboh on his [YouTube channel](https://www.youtube.com/@wakacademy). These lectures are must-watch for MDCAT preparation.
3. For practice, I only used past papers and the KIPS Practice Book.
### 2.4. English [#24-english]
1. I used the KIPS Prep Book for grammar rules. I have also heard a lot about Chemical Grammar. As I didn't read it, I can't comment on it.
2. For practice, I used KIPS Practice Book (though couldn't solve it completely).
3. KIPS English lectures by Prof. Ali Shan Rao are a masterpiece for MDCAT preparation. Although his lectures are lengthy, they are very comprehensive and will be worth your time.
### 2.5. Logical Reasoning [#25-logical-reasoning]
1. I only solved the KIPS Logical Reasoning book; it was enough.
2. You may listen to the lectures on "Logical Problems." You have to solve these questions through Venn Diagrams.
## 3. Focus, Anxiety, and Burden during MDCAT [#3-focus-anxiety-and-burden-during-mdcat]
MDCAT is stressful, and it is natural to feel a burden when preparing for it. Here are some tips that may help you manage the burden and anxiety during MDCAT:
1. Create a study schedule: Plan out your study sessions in advance and stick to a schedule. This will help you allocate your time effectively and avoid last-minute cramming.
2. Set realistic goals: Establish specific, achievable goals for your study sessions. For example, you cannot revise the whole biology syllabus in one day.
3. Prepare in advance: Make sure you have reviewed all the material and practiced your skills before the test. This will help you feel more confident and better prepared.
4. Avoid multitasking: Try to avoid multitasking while studying. It is usually more effective to focus on one task at a time.
5. Focus on the present: Stay focused on the present moment and avoid worrying about things that are out of your control.
6. Stay organized: Keep your all books and MDCAT helping material organized and in one place. This will save you time and help you stay focused.