bulletin_tools/bulletin-input/all_in_one_bulletin.py
Benjamin Slingo 1eb1fc9909 Initial commit with major improvements
- Fixed Sabbath School parser to handle API data with extra line breaks
- Cleaned up project structure and removed nested directories
- Organized output to single directory structure
- Removed YouTube link from contact section for cleaner layout
- Improved parser robustness for multi-line content between labels
- Added proper .gitignore for Rust project
2025-08-21 20:17:54 -04:00

674 lines
29 KiB
Python

#!/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"<p>[Could not parse scripture reference: {scripture_ref}]</p><p>{scripture_ref} KJV</p>"
# 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"<p>[Verse {verse} not found]</p><p>{scripture_ref} KJV</p>"
return f"<p>{scripture_text}</p><p>{scripture_ref} KJV</p>"
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"<p>[Verse {verse} not found]</p><p>{scripture_ref} KJV</p>"
# Return with HTML formatting
return f"<p>{scripture_text}</p><p>{scripture_ref} KJV</p>"
return f"<p>[Scripture not found: {scripture_ref}]</p><p>{scripture_ref} KJV</p>"
except FileNotFoundError:
return f"<p>[KJV.json file not found]</p><p>{scripture_ref} KJV</p>"
except Exception as e:
return f"<p>[Error reading scripture: {e}]</p><p>{scripture_ref} KJV</p>"
"""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:<br>
{song_leader}<br><br>
Lesson Study:<br>
{ss_teacher}<br><br>
Leadership:<br>
{ss_leader}<br><br>
Mission Story:<br>
{mission_story}<br><br>
Closing Hymn:<br>
{ss_leader}"""
# Format Divine Worship as HTML with br tags for tighter spacing
divine_worship_info = f"""Announcements:<br>
{announcements_person}<br><br>
Call To Worship:<br>
{responsive_reading}<br><br>
Opening Hymn:<br>
{opening_song}<br><br>
Prayer & Praises:<br>
{announcements_person}<br><br>
Prayer Song:<br>
#671 "As We Come To You in Prayer"<br><br>
Offering:<br>
{bulletin_data.get('offering_focus', 'Local Church Budget')}<br>
{offering_person}<br><br>
Children's Story:<br>
{childrens_story_person}<br><br>
Special Music:<br>
{special_music}<br><br>
Scripture Reading:<br>
{scripture_reading}<br>
{scripture_reader}<br><br>
Sermon:<br>
{parsed_sermon.get('sermon_title', 'TBA')}<br><br>
{speaker}<br><br>
Closing Hymn:<br>
{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()