================================================================================ PRACTICAL PYTHON SCRIPTING - CHALLENGE 1 SOLUTION Chapter 6: Building Your Own Utilities Challenge: Create a Blog Post Importer ================================================================================ PROBLEM: Build a utility that: 1. Finds all blog-*.md files 2. Parses title and date from front matter 3. Inserts into database 4. Reports count SOLUTION: ================================================================================ import sys from pathlib import Path import re from datetime import datetime # Import database utilities sys.path.insert(0, str(Path(__file__).parent)) from db_utils import DatabaseConnection, get_or_create_subtopic, insert_chapter_with_content SCRIPT_DIR = Path(__file__).resolve().parent POSTS_DIR = SCRIPT_DIR / "blog" def find_posts(): """Find all blog-*.md files.""" if not POSTS_DIR.exists(): print(f"❌ Blog directory not found: {POSTS_DIR}") return [] md_files = sorted(POSTS_DIR.glob("blog-*.md")) if not md_files: print(f"⚠️ No blog files found in {POSTS_DIR}") return [] print(f"✅ Found {len(md_files)} blog posts\n") return md_files def parse_front_matter(content): """Extract YAML front matter from markdown.""" # Match YAML front matter between --- delimiters match = re.match(r'^---\n(.*?)\n---', content, re.DOTALL) if not match: return None front_matter = match.group(1) data = {} # Parse YAML-like format for line in front_matter.split('\n'): if ':' in line: key, value = line.split(':', 1) data[key.strip()] = value.strip().strip('"\'') return data def parse_post(md_file): """Extract post data from markdown file.""" try: content = md_file.read_text(encoding='utf-8') # Parse front matter front_matter = parse_front_matter(content) if not front_matter: return None # Extract body (after front matter) body_match = re.match(r'^---\n.*?\n---(.*)', content, re.DOTALL) body = body_match.group(1).strip() if body_match else "" return { 'title': front_matter.get('title', 'Untitled'), 'date': front_matter.get('date', ''), 'body': body, 'filename': md_file.name } except Exception as e: print(f"❌ Error parsing {md_file.name}: {e}") return None def main(): """Main blog import workflow.""" print("=" * 60) print("🚀 Blog Post Importer") print("=" * 60 + "\n") # Step 1: Find posts print("📚 Step 1: Finding blog posts...") md_files = find_posts() if not md_files: print("❌ No posts found.") return False # Step 2: Connect to database print("📚 Step 2: Connecting to database...") db = DatabaseConnection() if not db.connect(): print("❌ Database connection failed") return False try: # Step 3: Create or get blog subtopic print(f"\n📚 Step 3: Creating 'Blog' section...\n") subtopic_id = get_or_create_subtopic( db, subject_id=1, # News/Blog subtopic_name="Blog Posts", description="Published blog articles" ) if subtopic_id is None: print("❌ Failed to create blog section") return False # Step 4: Import posts print(f"\n📚 Step 4: Importing {len(md_files)} posts...\n") posts_imported = 0 for md_file in md_files: post = parse_post(md_file) if not post: continue # Insert post page_id = insert_chapter_with_content( db, subtopic_id=subtopic_id, chapter_title=post['title'], html_content=post['body'], sort_order=posts_imported * 10 ) if page_id: posts_imported += 1 print(f"✅ Imported: {post['title']}") # Step 5: Report results print("\n" + "=" * 60) print("✅ IMPORT COMPLETE!") print("=" * 60) print(f"\n📊 Results:") print(f" Posts Imported: {posts_imported}") print(f" Total Found: {len(md_files)}") return True finally: db.disconnect() if __name__ == "__main__": success = main() sys.exit(0 if success else 1) ================================================================================ HOW IT WORKS: ================================================================================ 1. FIND POSTS - Use glob("blog-*.md") to find markdown files - Same pattern as chapter discovery in Chapter 5 2. PARSE FRONT MATTER - Extract YAML between --- delimiters - Parse key: value pairs - This is how Jekyll and other blogs store metadata 3. PARSE BODY - Extract content after front matter - This becomes the page content 4. INSERT INTO DATABASE - Reuse db_utils functions - Different domain (blog posts), same database patterns ================================================================================ EXAMPLE BLOG FILE: ================================================================================ blog-1.md: --- title: Getting Started with Python date: 2026-06-29 author: Philip --- # Getting Started with Python Python is an amazing language... ## Section 1 More content here. ================================================================================ KEY PATTERNS APPLIED: ================================================================================ ✅ File Discovery (Chapter 1) → glob("blog-*.md") ✅ Configuration (Chapter 2) → Load from .env ✅ Database (Chapter 3) → DatabaseConnection ✅ DB Utilities (Chapter 4) → get_or_create_subtopic() ✅ Automation (Chapter 5) → Complete workflow ✅ Best Practices (Chapter 6) → Error handling, type hints Same patterns, different domain! ================================================================================