| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117 |
- #!/usr/bin/env python3
- """Rename a field in x_data.source for CalDAV events within a date range.
- Basic mode (no --domain/--attr): matches events where source.domain == old_key
- and rewrites source.domain to new_key. Identical to rename_domain.py.
- Extended mode (--domain and --attr both supplied): matches events where
- source.domain == DOMAIN and rewrites source.ATTR from old_key to new_key.
- --domain and --attr must be supplied together or not at all.
- Usage:
- python scripts/rename_source.py OLD_KEY NEW_KEY START_DATE END_DATE [--calendar NAME] [--dry-run]
- python scripts/rename_source.py OLD_KEY NEW_KEY START_DATE END_DATE --domain DOMAIN --attr ATTR [--calendar NAME] [--dry-run]
- Examples:
- # Rename source.domain from airbnb to vrbo
- python scripts/rename_source.py airbnb vrbo 2026-01-01 2026-12-31
- # For all events belonging to domain airbnb, rename source.id from abc to xyz
- python scripts/rename_source.py abc xyz 2026-01-01 2026-12-31 --domain airbnb --attr id
- """
- import argparse
- import asyncio
- import json
- import sys
- from datetime import date
- sys.path.insert(0, str(__import__('pathlib').Path(__file__).parent.parent))
- from room_cal.store import Calendar
- async def run(
- old_key: str,
- new_key: str,
- start: date,
- end: date,
- calendar: str,
- dry_run: bool,
- domain: str | None,
- attr: str,
- ) -> None:
- cal = Calendar(calendar)
- events = await cal.fetch_date_range(start, end)
- updated = 0
- for event in events:
- try:
- raw = json.loads(event['x_data'] or '{}')
- except (TypeError, json.JSONDecodeError):
- continue
- source = raw.get('source')
- if not isinstance(source, dict):
- continue
- if domain is None:
- # Basic mode: match and update source.domain
- if source.get('domain') != old_key:
- continue
- else:
- # Extended mode: match on source.domain, update source.<attr>
- if source.get('domain') != domain:
- continue
- if source.get(attr) != old_key:
- continue
- new_x_data = json.dumps({**raw, 'source': {**source, attr: new_key}})
- event_date = event['start'].date() if hasattr(event['start'], 'date') else event['start']
- print(f" {event_date} [{event['title']}]: source.{attr} {old_key!r} -> {new_key!r}", end='')
- if dry_run:
- print(' [dry-run, skipped]')
- else:
- await cal.update_event(event, x_data=new_x_data)
- print(' [updated]')
- updated += 1
- if updated == 0:
- if domain is None:
- print(f"No events with source.domain='{old_key}' found in {start} – {end}.")
- else:
- print(f"No events with source.domain='{domain}' and source.{attr}='{old_key}' found in {start} – {end}.")
- else:
- action = 'would update' if dry_run else 'updated'
- print(f"\n{action} {updated} event(s).")
- def main() -> None:
- parser = argparse.ArgumentParser(description='Rename a field in x_data.source for CalDAV events.')
- parser.add_argument('old_key', help='Current value to replace')
- parser.add_argument('new_key', help='Replacement value')
- parser.add_argument('start_date', help='Start date (YYYY-MM-DD, inclusive)')
- parser.add_argument('end_date', help='End date (YYYY-MM-DD, inclusive)')
- parser.add_argument('--domain', default=None, help='Filter by source.domain (required with --attr)')
- parser.add_argument('--attr', default=None, help='source field to update (default: domain)')
- parser.add_argument('--calendar', default='Room', help='Calendar name (default: Room)')
- parser.add_argument('--dry-run', action='store_true', help='Show what would change without writing')
- args = parser.parse_args()
- if (args.domain is None) != (args.attr is None):
- parser.error('--domain and --attr must be supplied together or not at all')
- start = date.fromisoformat(args.start_date)
- end = date.fromisoformat(args.end_date)
- # In basic mode attr is the field being matched and updated
- attr = args.attr if args.attr is not None else 'domain'
- asyncio.run(run(args.old_key, args.new_key, start, end, args.calendar, args.dry_run, args.domain, attr))
- if __name__ == '__main__':
- main()
|