commit 7c17d01b7abe1d5b73cc09e6cd5803772b803306
parent fa128da25e735a52d157e3c2f88ffeae83921689
Author: Carlosokumu <carlosokumu254@gmail.com>
Date: Thu, 28 Aug 2025 15:58:24 +0300
update ArgsParser:
- separate edit and create required arguments
- ensure a safe timestamp based filename is generated if not given
Diffstat:
1 file changed, 129 insertions(+), 82 deletions(-)
diff --git a/ungana/cmd/args_parser.py b/ungana/cmd/args_parser.py
@@ -18,7 +18,7 @@ class ArgsParser:
)
self.ical_manager = ICalManager()
self.attachment_manager = AttachmentManager()
- self._add_field_arguments()
+ self._add_command_arguments()
def _read_file_or_exit(self, file_path: str) -> str:
try:
@@ -51,54 +51,73 @@ class ArgsParser:
)
return duration_str
- def _add_field_arguments(self):
+ def _add_command_arguments(self):
subparsers = self.parser.add_subparsers(dest='command', required=True)
-
- def add_event_field_args(parser):
- parser.add_argument("-i", "--interactive",
- action="store_true",
- help="Run interactive calendar creation")
- parser.add_argument("-s", "--summary",
- help="Event summary (text or via --summary-file)")
- parser.add_argument("--sf", "--summary-file", dest="summary_file",
- help="File containing event summary")
- parser.add_argument("-d", "--description",
- help="Event description (text or via --description-file)")
- parser.add_argument("--df", "--description-file", dest="description_file",
- help="File containing event description")
- parser.add_argument("--start", type=self._validate_datetime,
- help="Event start time (ISO format or DD-MM-YYYY HH:MM)")
- parser.add_argument("-l", "--location",
- help="Event location")
- parser.add_argument("-o", "--organizer",
- help="Event organizer")
- parser.add_argument("--tzid", help="Time zone ID (e.g., 'Europe/Berlin')")
- parser.add_argument("--duration", type=self._validate_duration,
- help="Event duration (e.g., '2h' or '30m')")
- parser.add_argument("--end", type=self._validate_datetime,
- help="Event end time (ISO format or DD-MM-YYYY HH:MM)")
- parser.add_argument("-f", "--file", help="Output .ics filename (default: event_<date>.ics)")
- parser.add_argument("-p", "--poster", help="Event headline image")
- parser.add_argument("-ld", "--long", help="Exhaustive description of the event")
- parser.add_argument("-c", "--contact", help="Contact details")
-
create_event_parser = subparsers.add_parser('create', help='Create a new calendar event')
+ self.add_create_args(create_event_parser)
self._add_logging_arguments(create_event_parser)
- create_event_parser.add_argument("ics_filename",help="Output .ics filename (default: event_<date>.ics")
- add_event_field_args(create_event_parser)
edit_event_parser = subparsers.add_parser('edit', help='Edit a calendar ical file')
+ self.add_edit_args(edit_event_parser)
self._add_logging_arguments(edit_event_parser)
edit_event_parser.add_argument("ics_file", help="Path to calendar .ics file")
edit_event_parser.add_argument("-a", "--all", action="store_true",
help="If your ical file has more than one event, interactively choose one to edit")
- add_event_field_args(edit_event_parser)
+
+
+ def add_common_args(self, parser, required=False):
+ parser.add_argument("-i", "--interactive", action="store_true",
+ help="Run interactive calendar creation")
+
+ parser.add_argument("-s", "--summary", required=required, help="Event summary")
+ parser.add_argument("--start", type=self._validate_datetime, required=required,help="Event start time (ISO format or DD-MM-YYYY HH:MM)")
+ parser.add_argument("-d", "--description",required=required, help="Event description")
+ parser.add_argument("-l", "--location",required=required,help="Event location")
+ parser.add_argument("-o", "--organizer",required=required,help="Event organizer")
+
+ parser.add_argument("--sf", "--summary-file", dest="summary_file", help="File containing event summary")
+ parser.add_argument("--df", "--description-file", dest="description_file", help="File containing event description")
+ parser.add_argument("--tzid", help="Time zone ID")
+ parser.add_argument("--duration", type=self._validate_duration, help="Event duration")
+ parser.add_argument("--end", type=self._validate_datetime, help="Event end time")
+
+
+ def add_create_args(self, parser):
+ mode_group = parser.add_mutually_exclusive_group(required=False)
+ mode_group.add_argument(
+ "-i", "--interactive",
+ action="store_true",
+ help="Run interactive calendar creation"
+ )
+
+ non_interactive = parser.add_argument_group("non-interactive arguments")
+ non_interactive.add_argument("-s", "--summary", help="Event summary")
+ non_interactive.add_argument("--start", type=self._validate_datetime, help="Event start time (ISO format or DD-MM-YYYY HH:MM)")
+ non_interactive.add_argument("-d", "--description", help="Event description")
+ non_interactive.add_argument("-l", "--location", help="Event location")
+ non_interactive.add_argument("-o", "--organizer", help="Event organizer")
+
+ non_interactive.add_argument("--sf", "--summary-file", dest="summary_file", help="File containing event summary")
+ non_interactive.add_argument("--df", "--description-file", dest="description_file", help="File containing event description")
+ non_interactive.add_argument("--tzid", help="Time zone ID")
+ non_interactive.add_argument("--duration", type=self._validate_duration, help="Event duration")
+ non_interactive.add_argument("--end", type=self._validate_datetime, help="Event end time")
+ parser.add_argument("ics_filename", nargs="?", help="Output .ics filename (default: event_<date>.ics)")
+
+
+
+ def add_edit_args(self, parser):
+ self.add_common_args(parser, required=False)
+ parser.add_argument("-p", "--poster", help="Event headline image")
+ parser.add_argument("-ld", "--long", help="Exhaustive description of the event")
+ parser.add_argument("-c", "--contact", help="Contact details")
+
+
def _add_logging_arguments(self, parser):
- parser.add_argument("-v", "--verbose", action="store_true",
- help="Enable verbose debug output")
+ parser.add_argument("-v", "--verbose", action="store_true",help="Enable verbose debug output")
parser.add_argument("-q", "--quiet", action="store_true",
help="Suppress all non-error output")
@@ -153,64 +172,42 @@ class ArgsParser:
def handle_create(self, args):
if args.interactive:
if args.ics_filename:
- cal = self.ical_manager.load_ical_file(args.ics_filename)
- if cal:
+ ics_filename = args.ics_filename
+ else:
+ ics_filename = f"event_{datetime.now().strftime('%Y%m%d_%H%M%S')}.ics"
+
+ cal = self.ical_manager.load_ical_file(ics_filename)
+ if cal:
event_data = self.prompt_compulsory_event_fields()
event = self.ical_manager.create_event(event_data)
- self.ical_manager.save_ical_file(event, args.ics_filename)
+ self.ical_manager.save_ical_file(event, ics_filename)
+
return
else:
- 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")
+ event_data = self._validate_and_get_event_args(args)
- args.start_dt = datetime.fromisoformat(args.start)
-
- 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"
-
- if args.tzid:
- tzinfo = tz.gettz(args.tzid)
- if tzinfo is None:
- self.parser.error(f"Invalid timezone ID: {args.tzid}")
- else:
- # Fallback to UTC
- tzinfo = tz.UTC
- args.tzid = "UTC"
-
- event_data = {
- 'start': args.start_dt,
- 'duration': args.duration,
- 'summary': args.summary,
- 'location': args.location,
- 'description': args.description,
- 'organizer': args.organizer,
- 'tzid': args.tzid
- }
if args.ics_filename:
filename = args.ics_filename
cal = self.ical_manager.load_ical_file(filename)
- exists = self.ical_manager.check_existing_event(cal,event_data)
+ exists = self.ical_manager.check_existing_event(cal, event_data)
if exists:
details = (
- f"Summary='{event_data.get('summary')}', "
- f"Start={event_data.get('start')}, "
- f"Tzid={event_data.get('tzid')}, "
- f"Location='{event_data.get('location')}'"
- )
- self.parser.error( f"Duplicate event detected: An event with these details already exists: {details}. "f"Try using the 'edit' command instead.")
- return
+ f"Summary='{event_data['summary']}', "
+ f"Start={event_data['start']}, "
+ f"Tzid={event_data['tzid']}, "
+ f"Location='{event_data['location']}'"
+ )
+ self.parser.error(
+ f"Duplicate event detected: An event with these details already exists: {details}. "
+ f"Try using the 'edit' command instead."
+ )
else:
filename = f"event_{datetime.now().strftime('%Y%m%d_%H%M%S')}.ics"
-
+
event = self.ical_manager.create_event(event_data)
self.ical_manager.save_ical_file(event, filename)
+
+
def handle_edit(self, args):
if not args.ics_file:
@@ -283,8 +280,6 @@ class ArgsParser:
logging.info("No changes made to calendar file")
-
-
def _get_user_event_updates(self, event) -> dict:
updates = {}
editable_fields = [
@@ -372,6 +367,58 @@ class ArgsParser:
return raw_contact.strip(), {}
+ def _validate_and_get_event_args(self, args):
+ 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.summary:
+ self.parser.error("Event summary must be specified (use -s or --sf).")
+ if not args.description:
+ self.parser.error("Event description must be specified (use -d or --df).")
+ if not args.location:
+ self.parser.error("Event location must be specified (-l).")
+ if not args.organizer:
+ self.parser.error("Event organizer must be specified (-o).")
+
+ if not args.start:
+ self.parser.error("Start time must be specified (--start).")
+ try:
+ args.start_dt = datetime.fromisoformat(args.start)
+ except ValueError:
+ self.parser.error(f"Invalid start time format: {args.start}")
+
+ if not args.end and not args.duration:
+ self.parser.error("Either --end or --duration must be specified.")
+
+ if args.end:
+ try:
+ end_dt = datetime.fromisoformat(args.end)
+ except ValueError:
+ self.parser.error(f"Invalid end time format: {args.end}")
+ duration = end_dt - args.start_dt
+ args.duration = f"{duration.seconds//3600}h{(duration.seconds%3600)//60}m"
+
+ if args.tzid:
+ tzinfo = tz.gettz(args.tzid)
+ if tzinfo is None:
+ self.parser.error(f"Invalid timezone ID: {args.tzid}")
+ else:
+ tzinfo = tz.UTC
+ args.tzid = "UTC"
+
+ event_data = {
+ 'summary': args.summary,
+ 'description': args.description,
+ 'location': args.location,
+ 'organizer': args.organizer,
+ 'start': args.start_dt,
+ 'duration': args.duration,
+ 'tzid': args.tzid,
+ }
+
+ return event_data
def run(self):