#!/usr/bin/env python3 import csv import datetime import json import os import re import requests import getpass from typing import Dict, List, Optional # PocketBase config POCKETBASE_URL = "https://pocketbase.rockvilletollandsda.church" ADMIN_EMAIL = "av@rockvilletollandsda.org" # Get password from environment or prompt ADMIN_PASSWORD = os.environ.get('POCKETBASE_PASSWORD') if not ADMIN_PASSWORD: ADMIN_PASSWORD = getpass.getpass("Enter PocketBase admin password: ") # Default times DEFAULT_SABBATH_SCHOOL_START_TIME = "09:30:00" DEFAULT_SABBATH_SCHOOL_END_TIME = "10:45:00" DEFAULT_DIVINE_WORSHIP_START_TIME = "11:00:00" DEFAULT_DIVINE_WORSHIP_END_TIME = "12:30:00" def get_upcoming_saturday() -> datetime.date: """Get next Saturday date""" today = datetime.date.today() days_until_saturday = (5 - today.weekday() + 7) % 7 return today + datetime.timedelta(days=days_until_saturday) def parse_sermon_details_from_text(text: str) -> Dict[str, str]: """Parse sermon details from text input""" details = {} print(f"DEBUG: Raw sermon input text: '{text}'") # Your exact input format: # Sermon Title: "A Revelation of Jesus Christ" Scripture Reading: Revelation 1:8 # Opening Song: 623 - I Will Follow Thee Closing Song: 330 - Take My Life and Let It Be # Responsive Reading: 800 - Union with Christ # Fixed regex patterns for your exact format patterns = [ (r'Sermon Title:\s*"([^"]+)"', 'sermon_title'), (r'Scripture Reading:\s*([^A-Za-z]*[A-Za-z][^A-Za-z0-9]*[0-9:]+[^A-Za-z]*)', 'scripture_reading'), (r'Opening Song:\s*([0-9]+\s*-[^C]*?)(?=\s*Closing Song)', 'opening_song'), (r'Closing Song:\s*([0-9]+\s*-[^R]*?)(?=\s*Responsive Reading)', 'closing_song'), (r'Responsive Reading:\s*([0-9]+\s*-[^$]*)', 'responsive_reading') ] # Simpler approach - split by keywords if 'Sermon Title:' in text: # Look for Sermon Title: "anything in quotes" - handle smart quotes too title_match = re.search(r'Sermon Title:\s*["\'""]([^"\'""]+)["\'""]', text) if title_match: details['sermon_title'] = title_match.group(1).strip() print(f"DEBUG: Found sermon_title: '{details['sermon_title']}'") else: # Try without quotes title_match = re.search(r'Sermon Title:\s*([^S]+?)(?=\s+Scripture Reading)', text) if title_match: details['sermon_title'] = title_match.group(1).strip().strip('"').strip('"').strip('"') print(f"DEBUG: Found sermon_title (no quotes): '{details['sermon_title']}'") else: print(f"DEBUG: Sermon Title found in text but regex failed") print(f"DEBUG: Text around Sermon Title: '{text[text.find('Sermon Title:'):text.find('Sermon Title:')+100]}'") else: print(f"DEBUG: 'Sermon Title:' not found in text") if 'Scripture Reading:' in text: scripture_match = re.search(r'Scripture Reading:\s*([^\n\r]+?)(?=\s+Opening Song|$)', text) if scripture_match: details['scripture_reading'] = scripture_match.group(1).strip() print(f"DEBUG: Found scripture_reading: '{details['scripture_reading']}'") if 'Opening Song:' in text: opening_match = re.search(r'Opening Song:\s*([^\n\r]+?)(?=\s+Closing Song|$)', text) if opening_match: song = opening_match.group(1).strip() # Format as #XXX "Title" if ' - ' in song: num, title = song.split(' - ', 1) details['opening_song'] = f"#{num.strip()} \"{title.strip()}\"" else: details['opening_song'] = song print(f"DEBUG: Found opening_song: '{details['opening_song']}'") if 'Closing Song:' in text: closing_match = re.search(r'Closing Song:\s*([^\n\r]+?)(?=\s+Responsive Reading|$)', text) if closing_match: song = closing_match.group(1).strip() # Format as #XXX "Title" if ' - ' in song: num, title = song.split(' - ', 1) details['closing_song'] = f"#{num.strip()} \"{title.strip()}\"" else: details['closing_song'] = song print(f"DEBUG: Found closing_song: '{details['closing_song']}'") if 'Responsive Reading:' in text: responsive_match = re.search(r'Responsive Reading:\s*([^\n\r]+?)(?=\s*$)', text) if responsive_match: reading = responsive_match.group(1).strip() # Format as Hymnal XXX "Title" if ' - ' in reading: num, title = reading.split(' - ', 1) details['responsive_reading'] = f"Hymnal {num.strip()} \"{title.strip()}\"" else: details['responsive_reading'] = reading print(f"DEBUG: Found responsive_reading: '{details['responsive_reading']}'") return details def parse_schedule_csv(csv_filepath: str, target_date: datetime.date) -> Optional[Dict[str, str]]: """Parse CSV for target date schedule""" try: with open(csv_filepath, 'r', encoding='utf-8') as f: reader = csv.reader(f) lines = list(reader) except FileNotFoundError: print(f"ERROR: Schedule CSV file not found at {csv_filepath}") return None for i, row in enumerate(lines): if not any(row): continue if "DATE" in row and "SERMON" in row: header = [cell.replace("\\n", " ").strip() for cell in row] sub_header = [] if i + 1 < len(lines): sub_header = [cell.replace("\\n", " ").strip() for cell in lines[i+1]] # Look for data rows after this header for j in range(i+2, len(lines)): data_row = lines[j] if not data_row or not data_row[0].strip(): continue date_str = data_row[0].replace("\\n", " ").strip() try: day_str, month_str = date_str.split() month_num = datetime.datetime.strptime(month_str.strip(), "%b").month current_row_date = datetime.date(target_date.year, month_num, int(day_str.strip())) except ValueError: continue if current_row_date == target_date: combined_header = [] for idx, h in enumerate(header): sh = sub_header[idx] if idx < len(sub_header) else "" if sh and sh not in h: combined_header.append(f"{h} {sh}".strip()) else: combined_header.append(h.strip()) values = [data_row[k].replace("\\n", " ").strip() if k < len(data_row) else "" for k in range(len(combined_header))] return dict(zip(combined_header, values)) return None def parse_scripture_reference(ref: str) -> tuple: """Parse scripture reference like 'Revelation 1:8' into book, chapter, verse""" try: # Handle cases like "Revelation 1:8" or "1 John 2:3-5" parts = ref.strip().split() # Find where the chapter:verse part starts chapter_verse_idx = -1 for i, part in enumerate(parts): if ':' in part: chapter_verse_idx = i break if chapter_verse_idx == -1: return None, None, None # Book name is everything before the chapter:verse book = ' '.join(parts[:chapter_verse_idx]) # Parse chapter:verse chapter_verse = parts[chapter_verse_idx] if ':' in chapter_verse: chapter, verse_part = chapter_verse.split(':', 1) # Handle verse ranges like "8-10" if '-' in verse_part: start_verse, end_verse = verse_part.split('-', 1) return book, int(chapter), (int(start_verse), int(end_verse)) else: return book, int(chapter), int(verse_part) return None, None, None except: return None, None, None def get_scripture_text(scripture_ref: str) -> str: """Get scripture text from KJV.json file""" try: kjv_path = os.path.join(os.path.dirname(__file__), "KJV.json") with open(kjv_path, 'r') as f: kjv_data = json.load(f) book, chapter, verse = parse_scripture_reference(scripture_ref) if not book or not chapter or not verse: return f"

[Could not parse scripture reference: {scripture_ref}]

{scripture_ref} KJV

" # KJV.json structure might be different - let's handle both formats if isinstance(kjv_data, dict): # Format: {"Genesis": {"1": ["In the beginning...", "And the earth..."]}} book_data = kjv_data.get(book) if book_data and str(chapter) in book_data: verses = book_data[str(chapter)] if isinstance(verse, tuple): start_verse, end_verse = verse selected_verses = verses[start_verse-1:end_verse] scripture_text = ' '.join(selected_verses) else: if verse <= len(verses): scripture_text = verses[verse-1] # Remove reference from beginning if it exists (like "Revelation 1:8 ") scripture_text = re.sub(r'^[A-Za-z0-9\s]+\s+\d+:\d+\s+', '', scripture_text).strip() else: return f"

[Verse {verse} not found]

{scripture_ref} KJV

" return f"

{scripture_text}

{scripture_ref} KJV

" else: # Format: [{"name": "Genesis", "chapters": [["In the beginning...", "And the earth..."]]}] for book_entry in kjv_data: if book_entry.get('name', '').lower() == book.lower(): chapters = book_entry.get('chapters', []) if chapter <= len(chapters): chapter_verses = chapters[chapter - 1] if isinstance(verse, tuple): start_verse, end_verse = verse verses_text = [] for v in range(start_verse, end_verse + 1): if v <= len(chapter_verses): verses_text.append(chapter_verses[v - 1]) scripture_text = ' '.join(verses_text) else: if verse <= len(chapter_verses): scripture_text = chapter_verses[verse - 1] # Remove reference from beginning if it exists (like "Revelation 1:8 ") scripture_text = re.sub(r'^[A-Za-z0-9\s]+\s+\d+:\d+\s+', '', scripture_text).strip() else: return f"

[Verse {verse} not found]

{scripture_ref} KJV

" # Return with HTML formatting return f"

{scripture_text}

{scripture_ref} KJV

" return f"

[Scripture not found: {scripture_ref}]

{scripture_ref} KJV

" except FileNotFoundError: return f"

[KJV.json file not found]

{scripture_ref} KJV

" except Exception as e: return f"

[Error reading scripture: {e}]

{scripture_ref} KJV

" """Authenticate with PocketBase""" auth_url = f"{POCKETBASE_URL}/api/collections/_superusers/auth-with-password" auth_data = { 'identity': ADMIN_EMAIL, 'password': ADMIN_PASSWORD } response = requests.post(auth_url, json=auth_data) response.raise_for_status() return response.json().get('token') def parse_conference_chart_txt(txt_filepath: str, target_date: datetime.date, location: str) -> Dict[str, Optional[str]]: """Parse the conference chart TXT for offering and sunset time.""" try: with open(txt_filepath, 'r', encoding='utf-8') as f: lines = f.readlines() except FileNotFoundError: print(f"ERROR: Conference chart TXT file not found at {txt_filepath}") return {"offering_focus": None, "sunset_time": None} except Exception as e: print(f"ERROR: Could not read conference chart TXT file: {e}") return {"offering_focus": None, "sunset_time": None} # Mapping of locations to column indices (Springfield is index 1) location_columns = { "south lancaster": 0, "springfield": 1, "stoneham": 2, "bridgeport": 3, "new haven": 4, "new london": 5, "providence": 6, } location_col_idx = location_columns.get(location.lower()) if location_col_idx is None: print(f"ERROR: Location '{location}' not found. Using Springfield as default.") location_col_idx = 1 # Springfield target_data = {"offering_focus": None, "sunset_time": None} current_month_str = None print(f"DEBUG: Looking for date {target_date}") for i, line in enumerate(lines): line = line.strip() if not line: continue # Look for month headers like "June 7" month_match = re.match(r"^(January|February|March|April|May|June|July|August|September|October|November|December)\s+(\d+)", line, re.IGNORECASE) if month_match: current_month_str = month_match.group(1) print(f"DEBUG: Found month: {current_month_str}") continue # Look for day lines like "14. Women's Ministries 8:26 8:29..." or "21 Local Church Budget 8:29 8:31..." if current_month_str: day_match = re.match(r"^(\d+)\.?\s+(.+)", line) if day_match: day_str = day_match.group(1) rest_of_line = day_match.group(2).strip() # Split into offering and times - look for where times start (first time pattern) time_start_match = re.search(r'\d:\d{2}', rest_of_line) if time_start_match: time_start_pos = time_start_match.start() offering_str = rest_of_line[:time_start_pos].strip() times_str = rest_of_line[time_start_pos:].strip() else: offering_str = rest_of_line times_str = "" try: month_num = datetime.datetime.strptime(current_month_str, "%B").month current_line_date = datetime.date(target_date.year, month_num, int(day_str)) print(f"DEBUG: Checking date {current_line_date} vs target {target_date}") if current_line_date == target_date: print(f"DEBUG: MATCH! Found line: '{line}'") print(f"DEBUG: Offering: '{offering_str}'") print(f"DEBUG: Times: '{times_str}'") target_data["offering_focus"] = offering_str # Extract times - look for patterns like "8:26" times = re.findall(r'\d:\d{2}', times_str) print(f"DEBUG: Extracted times: {times}") if location_col_idx < len(times): sunset_time = times[location_col_idx] target_data["sunset_time"] = f"{sunset_time} pm" print(f"DEBUG: {location} sunset: {target_data['sunset_time']}") return target_data except ValueError as e: print(f"DEBUG: Could not parse date from {current_month_str} {day_str}: {e}") continue print(f"ERROR: Could not find data for date {target_date.strftime('%Y-%m-%d')} in conference chart") return target_data def authenticate_pocketbase(): """Authenticate with PocketBase""" auth_url = f"{POCKETBASE_URL}/api/collections/_superusers/auth-with-password" auth_data = { 'identity': ADMIN_EMAIL, 'password': ADMIN_PASSWORD } response = requests.post(auth_url, json=auth_data) response.raise_for_status() return response.json().get('token') def create_bulletin_in_pocketbase(bulletin_data, events_data, weekly_schedule, parsed_sermon): """Create bulletin record in PocketBase""" print("Authenticating with PocketBase...") token = authenticate_pocketbase() # Extract data for PocketBase fields sabbath_school_info = "" divine_worship_info = "" scripture_reading = "" # Extract people from weekly schedule and prompt for missing values ss_leader = weekly_schedule.get("S. S. LEADER", "").strip() if not ss_leader: ss_leader = input("S.S. Leader not found in CSV. Enter name (or press Enter for TBA): ").strip() or "TBA" ss_teacher = weekly_schedule.get("S. S. TEACHER", "").strip() if not ss_teacher: ss_teacher = input("S.S. Teacher not found in CSV. Enter name (or press Enter for TBA): ").strip() or "TBA" mission_story = weekly_schedule.get("MISSION STORY", "").strip() if not mission_story: mission_story = input("Mission Story person not found in CSV. Enter name (or press Enter for TBA): ").strip() or "TBA" song_leader = weekly_schedule.get("Song LEADER", "").strip() if not song_leader: song_leader = input("Song Leader not found in CSV. Enter name (or press Enter for TBA): ").strip() or "TBA" announcements_person = weekly_schedule.get("SCRIPTURE", "").strip() if not announcements_person: announcements_person = input("Announcements person not found in CSV. Enter name (or press Enter for TBA): ").strip() or "TBA" offering_person = weekly_schedule.get("OFFERING", "").strip() if not offering_person: offering_person = input("Offering person not found in CSV. Enter name (or press Enter for TBA): ").strip() or "TBA" special_music = weekly_schedule.get("SPECIAL MUSIC", "").strip() if not special_music: special_music = input("Special Music person not found in CSV. Enter name (or press Enter for TBA): ").strip() or "TBA" speaker = weekly_schedule.get("SERMON SPEAKER", "").strip() if not speaker: speaker = input("Sermon Speaker not found in CSV. Enter name (or press Enter for TBA): ").strip() or "TBA" print(f"DEBUG: Parsed sermon details: {parsed_sermon}") print(f"DEBUG: Sermon title from parsing: '{parsed_sermon.get('sermon_title', 'NOT FOUND')}'") # Pastor Joseph Piresson special logic is_pastor_speaking = "pastor joseph piresson" in speaker.lower() # If Pastor is speaking, he also does scripture reading and children's story if is_pastor_speaking: print("Pastor Joseph Piresson is speaking - assigning him scripture reading and children's story") scripture_reader = speaker childrens_story_person = speaker else: scripture_reader = announcements_person childrens_story_person = announcements_person opening_song = parsed_sermon.get('opening_song', '').strip() if not opening_song: opening_song = input("Opening Song not found in sermon details. Enter song (or press Enter for TBA): ").strip() or "TBA" closing_song = parsed_sermon.get('closing_song', '').strip() if not closing_song: closing_song = input("Closing Song not found in sermon details. Enter song (or press Enter for TBA): ").strip() or "TBA" scripture_reading = parsed_sermon.get('scripture_reading', '').strip() if not scripture_reading: scripture_reading = input("Scripture Reading not found in sermon details. Enter reference (or press Enter for TBA): ").strip() or "TBA" responsive_reading = parsed_sermon.get('responsive_reading', '').strip() if not responsive_reading: responsive_reading = input("Responsive Reading not found in sermon details. Enter reading (or press Enter for TBA): ").strip() or "TBA" print(f"DEBUG: SS Leader: {ss_leader}") print(f"DEBUG: SS Teacher: {ss_teacher}") print(f"DEBUG: Mission Story: {mission_story}") print(f"DEBUG: Song Leader: {song_leader}") print(f"DEBUG: Announcements: {announcements_person}") print(f"DEBUG: Offering: {offering_person}") print(f"DEBUG: Special Music: {special_music}") print(f"DEBUG: Speaker: {speaker}") print(f"DEBUG: Opening Song: {parsed_sermon.get('opening_song', 'TBA')}") print(f"DEBUG: Closing Song: {parsed_sermon.get('closing_song', 'TBA')}") print(f"DEBUG: Scripture: {parsed_sermon.get('scripture_reading', 'TBA')}") # Check what keys are actually in the weekly_schedule print(f"DEBUG: Weekly schedule keys: {list(weekly_schedule.keys())}") print(f"DEBUG: Parsed sermon keys: {list(parsed_sermon.keys())}") # Format Sabbath School as HTML with br tags for tighter spacing sabbath_school_info = f"""Song Service:
{song_leader}

Lesson Study:
{ss_teacher}

Leadership:
{ss_leader}

Mission Story:
{mission_story}

Closing Hymn:
{ss_leader}""" # Format Divine Worship as HTML with br tags for tighter spacing divine_worship_info = f"""Announcements:
{announcements_person}

Call To Worship:
{responsive_reading}

Opening Hymn:
{opening_song}

Prayer & Praises:
{announcements_person}

Prayer Song:
#671 "As We Come To You in Prayer"

Offering:
{bulletin_data.get('offering_focus', 'Local Church Budget')}
{offering_person}

Children's Story:
{childrens_story_person}

Special Music:
{special_music}

Scripture Reading:
{scripture_reading}
{scripture_reader}

Sermon:
{parsed_sermon.get('sermon_title', 'TBA')}

{speaker}

Closing Hymn:
{closing_song}""" # Scripture reading field - get actual text from KJV.json scripture_full_text = get_scripture_text(scripture_reading) if scripture_reading != 'TBA' else f"TBA\n\n{scripture_reading}" # Create PocketBase record api_url = f"{POCKETBASE_URL}/api/collections/bulletins/records" headers = {'Authorization': token} record_data = { "date": f"{bulletin_data.get('date')} 00:00:00", "title": parsed_sermon.get('sermon_title', 'Saturday Worship Service'), # Proper Saturday default "url": "", "pdf_url": "", "is_active": True, "sabbath_school": sabbath_school_info, "divine_worship": divine_worship_info, "scripture_reading": scripture_full_text, "sunset": f"Sunset Tonight: {bulletin_data.get('sunset_time') or 'TBA'}\n\nSunset Next Friday: {bulletin_data.get('next_friday_sunset') or 'TBA'}" } print(f"Creating bulletin: {record_data['title']}") response = requests.post(api_url, headers=headers, json=record_data) if response.status_code in [200, 201]: result = response.json() print(f"SUCCESS! Created bulletin ID: {result.get('id')}") return True else: print(f"FAILED! Status: {response.status_code}") print(f"Error: {response.text}") return False def main(): target_saturday = get_upcoming_saturday() target_saturday_str = target_saturday.strftime("%Y-%m-%d") print(f"--- Creating bulletin for Saturday: {target_saturday_str} ---") # Get file paths script_dir = os.path.dirname(os.path.abspath(__file__)) project_root = os.path.dirname(script_dir) schedule_csv_file = os.path.join(project_root, "Quarterly schedule2021 - 2025.csv") # Get sermon details from user print("\n--- Enter Sermon Details ---") print("Paste sermon details and press Enter twice when done:") sermon_lines = [] empty_line_count = 0 while True: try: line = input() if not line.strip(): empty_line_count += 1 if empty_line_count >= 2: break else: empty_line_count = 0 sermon_lines.append(line) except EOFError: break sermon_text = "\n".join(sermon_lines).strip() if not sermon_text: print("No sermon details provided. Exiting.") return parsed_sermon = parse_sermon_details_from_text(sermon_text) # Parse CSV print(f"\nReading schedule from CSV...") weekly_schedule = parse_schedule_csv(schedule_csv_file, target_saturday) if not weekly_schedule: print("Could not get weekly schedule. Exiting.") return # Parse conference chart for offering and sunset conference_chart_file = os.path.join(project_root, "2025 Offering and Sunset Times Chart.txt") print(f"\nReading conference chart from: {conference_chart_file}") # Use Springfield as default location (you can change this) sunset_location = "springfield" conference_data = parse_conference_chart_txt(conference_chart_file, target_saturday, sunset_location) # Get next Friday's sunset too next_friday = target_saturday + datetime.timedelta(days=6) # 6 days after Saturday = next Friday # But sunset chart only has Saturdays, so get the closest Saturday (June 21) next_saturday = target_saturday + datetime.timedelta(days=7) # Next Saturday next_friday_data = parse_conference_chart_txt(conference_chart_file, next_saturday, sunset_location) print(f"Found conference data for {target_saturday}: {conference_data}") print(f"Found conference data for next Saturday {next_saturday} (for Friday sunset): {next_friday_data}") # If next Saturday data failed, try to get it manually from the chart if not next_friday_data.get('sunset_time') and conference_data.get('sunset_time'): # Use a slightly later time as approximation current_time = conference_data.get('sunset_time', '8:29 pm') if ':' in current_time: time_part = current_time.replace(' pm', '').replace(' am', '') hour, minute = map(int, time_part.split(':')) # Add a few minutes for next Friday minute += 2 if minute >= 60: hour += 1 minute -= 60 next_friday_data['sunset_time'] = f"{hour}:{minute:02d} pm" print(f"Approximated next Friday sunset: {next_friday_data['sunset_time']}") # Create bulletin data bulletin_data = { "date": target_saturday_str, "title": parsed_sermon.get("sermon_title", "Saturday Worship Service"), # Use proper Saturday default "offering_focus": conference_data.get("offering_focus"), "sunset_time": conference_data.get("sunset_time"), "next_friday_sunset": next_friday_data.get("sunset_time"), "cover_image_path": None } # Create events data events_data = [] # Sabbath School ss_desc_parts = [] if weekly_schedule.get("S. S. LEADER"): ss_desc_parts.append(f"Leader: {weekly_schedule['S. S. LEADER']}") if weekly_schedule.get("S. S. TEACHER"): ss_desc_parts.append(f"Teacher: {weekly_schedule['S. S. TEACHER']}") if weekly_schedule.get("MISSION STORY"): ss_desc_parts.append(f"Mission Story: {weekly_schedule['MISSION STORY']}") events_data.append({ "title": "Sabbath School", "description": ", ".join(ss_desc_parts), "start_time": f"{target_saturday_str} {DEFAULT_SABBATH_SCHOOL_START_TIME}", "end_time": f"{target_saturday_str} {DEFAULT_SABBATH_SCHOOL_END_TIME}", "type": "recurring" }) # Divine Worship dw_desc_parts = [] if weekly_schedule.get("SERMON SPEAKER"): dw_desc_parts.append(f"Speaker: {weekly_schedule['SERMON SPEAKER']}") if weekly_schedule.get("Song LEADER"): dw_desc_parts.append(f"Song Leader: {weekly_schedule['Song LEADER']}") if weekly_schedule.get("SCRIPTURE"): dw_desc_parts.append(f"Announcements: {weekly_schedule['SCRIPTURE']}") dw_desc_parts.append(f"Prayer & Praises: {weekly_schedule['SCRIPTURE']}") dw_desc_parts.append(f"Scripture Reading: {weekly_schedule['SCRIPTURE']} ({parsed_sermon.get('scripture_reading', 'TBA')})") if weekly_schedule.get("OFFERING"): dw_desc_parts.append(f"Offering: {weekly_schedule['OFFERING']}") if parsed_sermon.get("opening_song"): dw_desc_parts.append(f"Opening Song: {parsed_sermon['opening_song']}") if parsed_sermon.get("closing_song"): dw_desc_parts.append(f"Closing Song: {parsed_sermon['closing_song']}") if weekly_schedule.get("SPECIAL MUSIC"): dw_desc_parts.append(f"Special Music: {weekly_schedule['SPECIAL MUSIC']}") events_data.append({ "title": "Divine Worship", "description": ". ".join(dw_desc_parts), "start_time": f"{target_saturday_str} {DEFAULT_DIVINE_WORSHIP_START_TIME}", "end_time": f"{target_saturday_str} {DEFAULT_DIVINE_WORSHIP_END_TIME}", "type": "recurring" }) # Create in PocketBase print("\n--- Creating bulletin in PocketBase ---") if create_bulletin_in_pocketbase(bulletin_data, events_data, weekly_schedule, parsed_sermon): print("\nDone! Check your PocketBase - bulletin created successfully!") else: print("\nFailed to create bulletin.") if __name__ == "__main__": main()