Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Look into konsolekalendar for creating events for KDE PIM calendars. #43

Open
Zren opened this issue Oct 4, 2018 · 8 comments
Open

Comments

@Zren
Copy link
Owner

Zren commented Oct 4, 2018

https://docs.kde.org/trunk5/en/pim/korganizer/other-features.html#other-features-commandline-options

A nice program to access a KDE calendar from the command line is konsolekalendar.

https://userbase.kde.org/KonsoleKalendar

@hsantanna
Copy link

@Zren
Copy link
Owner Author

Zren commented Jul 28, 2020

So... there doesn't seem to be a simple way of getting the Personal Calendar id. It also appears to print option not defined: "create" all the time so parsing stdout will be annoying.

Edit: That warning is from stderr, not stdout.

$ konsolekalendar --list-calendars
QCommandLineParser: option not defined: "create"
--------------------------

$ konsolekalendar --list-calendars 2>/dev/null
--------------------------

However if I add --allow-gui it will just create the event in the personal calendar. Also, it appears you cannot create a duplicate event at the same time with the same summary (thus the randomly generated summary example).

konsolekalendar --add --allow-gui --summary "TestEvent"
konsolekalendar --add --allow-gui --date 2020-07-27 --time 22:00 --summary "TestEvent"
konsolekalendar --add --allow-gui --date 2020-07-27 --time 22:00 --summary "TestEvent $(date +%s)"

@Zren
Copy link
Owner Author

Zren commented Jul 28, 2020

The PlasmaCalendar API doesn't get an event id, so I'd need to parse konsolekalendar's output to find an event if I ever wanted to create a edit event GUI.

// KHolidays
qml: [eventcalendar:debug] PlasmaCalendar "2020-07-02T00:00:00.000Z" [
        {
                "objectName": "",
                "startDateTime": "2020-07-01T04:00:00.000Z",
                "endDateTime": "2020-07-01T04:00:00.000Z",
                "isAllDay": true,
                "isMinor": false,
                "title": "Canada Day",
                "description": "",
                "eventColor": "",
                "eventType": "Holidays"
        }
]
// KDEPIM-Addons
qml: [eventcalendar:debug] PlasmaCalendar "2020-07-28T00:00:00.000Z" [
        {
                "objectName": "",
                "startDateTime": "2020-07-27T04:00:00.000Z",
                "endDateTime": "2020-07-27T04:00:00.000Z",
                "isAllDay": true,
                "isMinor": false,
                "title": "Test Todo",
                "description": "Descritpion",
                "eventColor": "#d0e7c1",
                "eventType": "Todo"
        },
        {
                "objectName": "",
                "startDateTime": "2020-07-28T00:30:00.000Z",
                "endDateTime": "2020-07-28T01:30:00.000Z",
                "isAllDay": false,
                "isMinor": false,
                "title": "Test Event",
                "description": "Description",
                "eventColor": "#d0e7c1",
                "eventType": "Events"
        }
]

Here's where the "Personal Calendar" data is stored. So we might be able to manipulate the ical itself.

$ qdbus org.freedesktop.Akonadi.Agent.akonadi_ical_resource_0 /Settings path
/home/zren/.local/share/apps/korganizer/std.ics

@Zren
Copy link
Owner Author

Zren commented Jul 28, 2020

If we add --verbose we get:

$ konsolekalendar --add --allow-gui --verbose --summary "TestEvent $(date +%s)" --date 2020-07-27 --time 22:00
QCommandLineParser: option not defined: "create"
Insert Event <Verbose>:
  What:  TestEvent 1595898505
  Begin: Mon Jul 27 22:00:00 2020
  End:   Mon Jul 27 23:00:00 2020
  Desc:  
  Location:  Default location
Success: "TestEvent 1595898505" inserted

By using --allow-gui we skip this bit of code:
https://github.com/KDE/akonadi-calendar-tools/blob/master/konsolekalendar/konsolekalendaradd.cpp#L107

if (!m_variables->allowGui()) {
    Akonadi::IncidenceChanger *changer = calendar->incidenceChanger();
    changer->setShowDialogsOnError(false);
    Akonadi::Collection collection = m_variables->collectionId() != -1 ? Akonadi::Collection(m_variables->collectionId())
                                     : Akonadi::Collection(CalendarSupport::KCalPrefs::instance()->defaultCalendarId());

    if (!collection.isValid()) {
        cout << i18n("Calendar is invalid. Please specify one with --calendar").toLocal8Bit().data() << "\n";
    }

    changer->setDefaultCollection(collection);
    changer->setDestinationPolicy(Akonadi::IncidenceChanger::DestinationPolicyNeverAsk);
}

I can add QT_LOGGING_RULES="org.kde.pim.konsolekalendar=true" to get:

$ QT_LOGGING_RULES="org.kde.pim.konsolekalendar=true" konsolekalendar --add --allow-gui --verbose --summary "TestEvent $(date +%s)" --date 2020-07-27 --time 22:00
org.kde.pim.konsolekalendar: main | parse options | use Events (Default)
org.kde.pim.konsolekalendar: main | parse options | Mode: (Add incidence)
QCommandLineParser: option not defined: "create"
org.kde.pim.konsolekalendar: main | parse options | Summary: ( "TestEvent 1595898989" )
org.kde.pim.konsolekalendar: main | parse options | Start date before conversion: ( "2020-07-27" )
org.kde.pim.konsolekalendar: main | parse options | Start date after conversion: ( "Mon Jul 27 2020" )
org.kde.pim.konsolekalendar: main | parse options | Start time before conversion : ( "22:00" )
org.kde.pim.konsolekalendar: main | parse options | Start time after conversion: ( "22:00:00" )
org.kde.pim.konsolekalendar: Starting to load calendar
org.kde.pim.konsolekalendar: Calendar loaded in 68 ms; success= true ; num incidences= 10
org.kde.pim.konsolekalendar: main | datetimestamp | setting enddate to startdate
org.kde.pim.konsolekalendar: main | datetimestamp | setting endtime 1 hour after starttime
org.kde.pim.konsolekalendar: main | datetimestamp | setting startdatetime from startdate and starttime
org.kde.pim.konsolekalendar: main | datetimestamp | setting enddatetime from enddate and endtime
org.kde.pim.konsolekalendar: main | datetimestamp | StartDate= "Mon Jul 27 22:00:00 2020"
org.kde.pim.konsolekalendar: main | datetimestamp | EndDate= "Mon Jul 27 23:00:00 2020"
org.kde.pim.konsolekalendar: main | modework | calling addEvent()
org.kde.pim.konsolekalendar: konsolecalendar.cpp::addEvent() | Create Adding
org.kde.pim.konsolekalendar: konsolecalendar.cpp::addEvent() | Adding Event now!
org.kde.pim.konsolekalendar: konsolekalendaradd.cpp::addEvent()
Insert Event <Verbose>:
  What:  TestEvent 1595898989
  Begin: Mon Jul 27 22:00:00 2020
  End:   Mon Jul 27 23:00:00 2020
  Desc:  
  Location:  Default location
org.kde.pim.konsolekalendar: Creation took  19 ms.
Success: "TestEvent 1595898989" inserted
org.kde.pim.konsolekalendar: konsolekalendaradd.cpp::addEvent() | Done
org.kde.pim.konsolekalendar: main | exiting

@Zren
Copy link
Owner Author

Zren commented Jul 28, 2020

Looking at the QML code for the kdepim-addons config tab, it appears the calendar model is a public qml module.

import org.kde.plasma.PimCalendars 1.0 as  PimCalendars

CalendarManager {
	id: plasmaCalendarManager

	calendarManagerId: "plasma"

	PimCalendars.PimCalendarsModel {
		id: calendarModel
	}
	function getCalendarList() {
		console.log('calendarModel', calendarModel)
		console.log('calendarModel.count', calendarModel.rowCount())
		var DataRole = Qt.UserRole + 1
		var calendarList = []
		for (var i = 0; i < calendarModel.rowCount(); i++) {
			var index = calendarModel.index(i, 0)
			var calendarName = calendarModel.data(index, Qt.DisplayRole)
			var calendarData = calendarModel.data(index, DataRole)
			console.log('PimCalendarsModel', i, calendarName, JSON.stringify(calendarData))

			var calendarId = "plasma_Events_" + calendarData['id']
			calendarList.push({
				"id": calendarId,
				"summary": calendarName,
				"backgroundColor": "#9a9cff",
				"accessRole": "owner",
			})
		}
		return calendarList
	}

This sucessfully lists the calendars as:

qml: calendarModel PimCalendarsModel(0x5638ceb7cdf0)
qml: calendarModel.count 3
qml: PimCalendarsModel 0 Birthdays & Anniversaries {"checked":false,"enabled":true,"iconName":"view-calendar-birthday","id":6,"name":"Birthdays & Anniversaries"}
qml: PimCalendarsModel 1 Personal Calendar {"checked":false,"enabled":true,"iconName":"office-calendar","id":12,"name":"Personal Calendar"}
qml: PimCalendarsModel 2 Search {"checked":false,"enabled":false,"iconName":"","id":1,"name":"Search"}

Using --calendar 12 in konsolekalendar without the --allow-gui field worked!

$ QT_LOGGING_RULES="org.kde.pim.konsolekalendar=true" konsolekalendar --add --calendar 12 --verbose --summary "TestEnvent $(date +%s)" --date 2020-07-27 --time 22:00         
org.kde.pim.konsolekalendar: main | parse options | use Events (Default)
org.kde.pim.konsolekalendar: main | parse options | Mode: (Add incidence)
QCommandLineParser: option not defined: "create"
org.kde.pim.konsolekalendar: main | parse options | Summary: ( "TestEvent 1595901827" )
org.kde.pim.konsolekalendar: main | parse options | Start date before conversion: ( "2020-07-27" )
org.kde.pim.konsolekalendar: main | parse options | Start date after conversion: ( "Mon Jul 27 2020" )
org.kde.pim.konsolekalendar: main | parse options | Start time before conversion : ( "22:00" )
org.kde.pim.konsolekalendar: main | parse options | Start time after conversion: ( "22:00:00" )
org.kde.pim.konsolekalendar: Starting to load calendar
org.kde.pim.konsolekalendar: Calendar loaded in 109 ms; success= true ; num incidences= 11
org.kde.pim.konsolekalendar: main | datetimestamp | setting enddate to startdate
org.kde.pim.konsolekalendar: main | datetimestamp | setting endtime 1 hour after starttime
org.kde.pim.konsolekalendar: main | datetimestamp | setting startdatetime from startdate and starttime
org.kde.pim.konsolekalendar: main | datetimestamp | setting enddatetime from enddate and endtime
org.kde.pim.konsolekalendar: main | datetimestamp | StartDate= "Mon Jul 27 22:00:00 2020"
org.kde.pim.konsolekalendar: main | datetimestamp | EndDate= "Mon Jul 27 23:00:00 2020"
org.kde.pim.konsolekalendar: main | modework | calling addEvent()
org.kde.pim.konsolekalendar: konsolecalendar.cpp::addEvent() | Create Adding
org.kde.pim.konsolekalendar: konsolecalendar.cpp::addEvent() | Adding Event now!
org.kde.pim.konsolekalendar: konsolekalendaradd.cpp::addEvent()
Insert Event <Verbose>:
  What:  TestEvent 1595901827
  Begin: Mon Jul 27 22:00:00 2020
  End:   Mon Jul 27 23:00:00 2020
  Desc:  
  Location:  Default location
org.kde.pim.konsolekalendar: Creation took  25 ms.
Success: "TestEvent 1595901827" inserted
org.kde.pim.konsolekalendar: konsolekalendaradd.cpp::addEvent() | Done
org.kde.pim.konsolekalendar: main | exiting

Edit: I forgot that "Search" has child calendars. I assume that PIM Google Calendar accounts also have this nested behavior so we'll need to parse these sub rows.

if (calendarModel.hasChildren(index)) {
	var parentIndex = index
	for (var j = 0; j < calendarModel.rowCount(parentIndex); j++) {
		var childIndex = calendarModel.index(j, 0, parentIndex)
		var calendarName = calendarModel.data(childIndex, Qt.DisplayRole)
		var calendarData = calendarModel.data(childIndex, DataRole)
		console.log('PimCalendarsModel', i, j, calendarName, JSON.stringify(calendarData))

		var calendarId = "plasma_Events_" + calendarData['id']
		calendarList.push({
			"id": calendarId,
			"summary": calendarName,
			"backgroundColor": "#9a9cff",
			"accessRole": "owner",
		})
	}
}
qml: calendarModel PimCalendarsModel(0x558379203a00)
qml: calendarModel.count 3
qml: PimCalendarsModel 0 Birthdays & Anniversaries {"checked":false,"enabled":true,"iconName":"view-calendar-birthday","id":6,"name":"Birthdays & Anniversaries"}
qml: PimCalendarsModel 1 Personal Calendar {"checked":false,"enabled":true,"iconName":"office-calendar","id":12,"name":"Personal Calendar"}
qml: PimCalendarsModel 2 Search {"checked":false,"enabled":false,"iconName":"","id":1,"name":"Search"}
qml: PimCalendarsModel 2 0 Declined Invitations {"checked":false,"enabled":true,"iconName":"","id":16,"name":"Declined Invitations"}
qml: PimCalendarsModel 2 1 Open Invitations {"checked":false,"enabled":true,"iconName":"","id":15,"name":"Open Invitations"}

Edit2: I can also filter out "Search" with calendar.data['enabled'] != false.

Zren added a commit that referenced this issue Jul 28, 2020
Populated PIM calendars in NewEventForm.
Using `plasma_Events_12` for calendarId is fairly rough but works.
Added the CalendarManager.refresh() signal so we can refetch events
after creating the event. There's not much point in attempting to
parse konsolekalendar's output.
@Zren
Copy link
Owner Author

Zren commented Jul 28, 2020

It sorta works in git master for "Personal Calendar".

I accidentally added events to the Birthday Calendar and found out it's read only in KOrganizer so I couldn't easily delete it.

$ konsolekalendar
QCommandLineParser: option not defined: "create"
Date:   Tuesday, July 28, 2020

Summary:
        werwerw
Location:
        Default location
Description:
        (no description available)
UID:
        33dc904f-18eb-4906-924c-c388df584a19

I had to run the following. I'm not sure if --calendar 6 was required (Edit: it's not). 6 is the calendar.id from the PlasmaCalendar API.

$ konsolekalendar --delete --calendar 6 --uid 33dc904f-18eb-4906-924c-c388df584a19
QCommandLineParser: option not defined: "create"
0x55dfd57c0578

@Zren
Copy link
Owner Author

Zren commented Jul 30, 2020

I've written a python script to first fetch the events, then parse them to find an event with the same start date/time, summary, and description. It'll error if there's multiple events that match, as it would be a bad idea to modify or delete the wrong event.

https://github.com/Zren/plasma-applet-eventcalendar/blob/master/package/contents/scripts/konsolekalendar.py

Deleting events works fine.

Unfortunately, editing/changing an event doesn't appear to work right. It's changing an "all day" event to an event with both startTime and endTime set at midnight.

2020-07-29___23-01-46

@Zren
Copy link
Owner Author

Zren commented Jul 30, 2020

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants