Source code for sabbath_school_reproducer.downloader

"""
GitHub Content Downloader for Sabbath School Lessons

This module handles downloading lesson content from the GitHub repository.
"""

import json
import requests
from urllib.parse import urljoin
import re


[docs] class GitHubDownloader: """Downloads Sabbath School lesson content from GitHub repository.""" def __init__(self, github_paths, config=None): """ Initialize with GitHub paths Args: github_paths (dict): Dictionary with GitHub URLs config (Config, optional): Configuration object for reproduction settings """ self.github_paths = github_paths self.config = config
[docs] def download_json(self, url): """ Download and parse JSON from a URL Args: url (str): URL to download JSON from Returns: dict: Parsed JSON, or None if download failed """ try: response = requests.get(url) response.raise_for_status() return response.json() except requests.RequestException as e: print(f"Error downloading JSON from {url}: {e}") return None except json.JSONDecodeError: print(f"Error parsing JSON from {url}") return None
[docs] def download_markdown(self, url): """ Download markdown content from a URL Args: url (str): URL to download markdown from Returns: str: Markdown content, or empty string if download failed """ try: response = requests.get(url) response.raise_for_status() return response.text except requests.RequestException as e: print(f"Error downloading markdown from {url}: {e}") return ""
[docs] def download_lesson_data(self): """ Download all lesson data from GitHub repository Returns: dict: Dictionary containing all downloaded content Raises: Exception: If download of contents.json fails """ # Download contents.json first contents = self.download_json(self.github_paths['contents_url']) if not contents: raise Exception(f"Failed to download lesson contents from {self.github_paths['contents_url']}") # Download front matter front_matter = self.download_markdown(self.github_paths['front_matter_url']) # Download back matter back_matter = self.download_markdown(self.github_paths['back_matter_url']) # Get reproduction settings if available start_lesson = 1 stop_lesson = float('inf') # Default to all lessons if self.config and 'reproduce' in self.config.config: reproduce = self.config.config['reproduce'] if 'start_lesson' in reproduce and reproduce['start_lesson']: start_lesson = int(reproduce['start_lesson']) if 'stop_lesson' in reproduce and reproduce['stop_lesson'] is not None: stop_lesson = int(reproduce['stop_lesson']) # Download each lesson lessons = {} base_url = self.github_paths['base_url'] # Filter and sort week IDs filtered_week_ids = [] for week_id in contents: # Extract lesson number from week_id (e.g., "week-01" -> 1) lesson_num_match = re.search(r'week-(\d+)', week_id) if lesson_num_match: lesson_num = int(lesson_num_match.group(1)) if start_lesson <= lesson_num <= stop_lesson: filtered_week_ids.append(week_id) # Sort the filtered week IDs filtered_week_ids.sort() for week_id in filtered_week_ids: week_url = urljoin(base_url + "/", f"{week_id}.md") week_content = self.download_markdown(week_url) if week_content: # Store with metadata from contents.json lessons[week_id] = { 'content': week_content, 'title': contents[week_id].get('title', ''), 'date': contents[week_id].get('date', '') } print(f"Downloaded {week_id}: {contents[week_id].get('title', '')}") else: print(f"Failed to download {week_id}") return { 'contents': contents, 'front_matter': front_matter, 'back_matter': back_matter, 'lessons': lessons }
[docs] @staticmethod def get_lesson_range_filename(config): """ Generate a filename that includes the lesson range information Args: config (Config): Configuration object Returns: str: Filename with lesson range information """ year = config['year'] quarter = config['quarter'] language = config['language'] # Get lesson range from reproduction settings start_lesson = 1 stop_lesson = "null" if 'reproduce' in config.config: reproduce = config.config['reproduce'] if 'start_lesson' in reproduce and reproduce['start_lesson']: start_lesson = reproduce['start_lesson'] if 'stop_lesson' in reproduce and reproduce['stop_lesson'] is not None: stop_lesson = reproduce['stop_lesson'] # Create filename with lesson range return f"combined_lessons_{year}_{quarter}_{language}_{start_lesson}_{stop_lesson}.md"
[docs] @staticmethod def check_existing_file(filename, force_overwrite=False): """ Check if the file already exists and prompt for overwrite Args: filename (str): Path to the file force_overwrite (bool): Whether to force overwrite without prompting Returns: bool: True if should proceed with download, False otherwise """ import os if not os.path.exists(filename): return True # File doesn't exist, proceed with download if force_overwrite: print(f"File {filename} exists, will overwrite (force mode)") return True # Prompt user for confirmation response = input(f"File {filename} already exists. Overwrite? (y/n): ") return response.lower() in ['y', 'yes']