commit b9f347945212f6b371401ba46459b3a2565f8866
parent a3dd50c7fdbcd21aa744406c06b48226a747a707
Author: Carlosokumu <carlosokumu254@gmail.com>
Date: Wed, 13 Aug 2025 17:07:50 +0300
add an argument parser
Diffstat:
1 file changed, 93 insertions(+), 0 deletions(-)
diff --git a/calendarapp/cmd/args_parser.py b/calendarapp/cmd/args_parser.py
@@ -0,0 +1,92 @@
+import argparse
+from pathlib import Path
+from datetime import datetime
+import re
+
+class ArgsParser:
+ def __init__(self):
+ self.parser = argparse.ArgumentParser(
+ description="Create a customized iCalendar (.ics) event"
+ )
+ self._add_arguments()
+
+ def _read_file_or_exit(self, file_path: str) -> str:
+ try:
+ return Path(file_path).read_text(encoding="utf-8").strip()
+ except Exception as e:
+ raise SystemExit(f"Error: Could not read file '{file_path}': {e}")
+
+ def _validate_datetime(self, dt_str: str) -> str:
+ try:
+ dt = datetime.fromisoformat(dt_str)
+ return dt.isoformat()
+ except ValueError:
+ pass
+ try:
+ dt = datetime.strptime(dt_str, "%d-%m-%Y %H:%M")
+ return dt.isoformat()
+ except ValueError:
+ pass
+
+ raise argparse.ArgumentTypeError(
+ f"Invalid datetime format: '{dt_str}'. "
+ "Expected ISO format (YYYY-MM-DDTHH:MM:SS+ZZ:ZZ) or DD-MM-YYYY HH:MM"
+ )
+
+ def _validate_duration(self, duration_str: str) -> str:
+ """Validate duration format (e.g., '2h' or '30m')."""
+ if not re.match(r'^(\d+h)?(\d+m)?$', duration_str):
+ raise argparse.ArgumentTypeError(
+ f"Invalid duration format: '{duration_str}'. Expected format like '2h' or '30m'"
+ )
+ return duration_str
+
+ def _add_arguments(self):
+ subparsers = self.parser.add_subparsers(dest='command', required=True)
+
+ create_event_parser = subparsers.add_parser('create', help='Create a new calendar event')
+
+ required_event_fields = create_event_parser.add_argument_group('required arguments')
+ required_event_fields.add_argument("-s", "--summary", required=True,
+ help="Event summary (text or via --summary-file)")
+ required_event_fields.add_argument("--sf", "--summary-file", dest="summary_file",
+ help="File containing event summary")
+ required_event_fields.add_argument("-d", "--description", required=True,
+ help="Event description (text or via --description-file)")
+ required_event_fields.add_argument("--df", "--description-file", dest="description_file",
+ help="File containing event description")
+ required_event_fields.add_argument("--start", required=True, type=self._validate_datetime,
+ help="Event start time (ISO format or DD-MM-YYYY HH:MM)")
+ required_event_fields.add_argument("-l", "--location", required=True, help="Event location")
+ required_event_fields.add_argument("-o", "--organizer", required=True, help="Event organizer")
+ required_event_fields.add_argument("--tzid", required=True,
+ help="Time zone ID (e.g., 'Europe/Berlin')")
+
+ event_duration = create_event_parser.add_argument_group('time specification (use one)')
+ event_duration.add_argument("--end", type=self._validate_datetime,
+ help="Event end time (ISO format or DD-MM-YYYY HH:MM)")
+ event_duration.add_argument("--duration", type=self._validate_duration,
+ help="Event duration (e.g., '2h' or '30m')")
+
+ def parse_args(self):
+ """Parses CLI args and resolves file-based inputs."""
+ args = self.parser.parse_args()
+
+ if args.command == 'create':
+ if args.summary_file:
+ args.summary = self._read_file_or_exit(args.summary_file)
+ if args.description_file:
+ args.description = self._read_file_or_exit(args.description_file)
+
+ if not args.end and not args.duration:
+ self.parser.error("Either --end or --duration must be specified")
+
+ args.start_dt = datetime.fromisoformat(args.start)
+
+ # Calculate duration if endtime is provided
+ if args.end:
+ end_dt = datetime.fromisoformat(args.end)
+ duration = end_dt - args.start_dt
+ args.duration = f"{duration.seconds//3600}h{(duration.seconds%3600)//60}m"
+
+ return args
+\ No newline at end of file