ungana

Unnamed repository; edit this file 'description' to name the repository.
Info | Log | Files | Refs | README

commit b9f347945212f6b371401ba46459b3a2565f8866
parent a3dd50c7fdbcd21aa744406c06b48226a747a707
Author: Carlosokumu <carlosokumu254@gmail.com>
Date:   Wed, 13 Aug 2025 17:07:50 +0300

add an argument parser

Diffstat:
Acalendarapp/cmd/args_parser.py | 93+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
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