ATFutures / calendar

R interface to iCal (.ics files)
https://atfutures.github.io/calendar/
Other
41 stars 11 forks source link

ics_df = ic_read("Downloads/mycalendar.ics") #44

Open yinshiyi opened 1 year ago

yinshiyi commented 1 year ago

Error in if (!is.na(x) & !x == "NA" & !grepl("^\d{8}T\d{6}Z?$", x)) { : the condition has length > 1

Anyone know the possible bugs?

Robinlovelace commented 1 year ago

Not sure the cause. Can you share a minimal reproducible example and a sample of the .ics file that causes the error?

silverfoxdoc commented 1 year ago

Hi, I am getting the same error:

Error in if (!is.na(x) & !x == "NA" & !grepl("^\\d{8}T\\d{6}Z?$", x)) { : 
  the condition has length > 1

When I try to parse the following ics file with calendar::ic_read():

BEGIN:VCALENDAR
VERSION:2.0
PRODID:healthroster
X-WR-CALNAME:R
BEGIN:VEVENT
UID:45768392
DTSTAMP:20230405T230000Z
DTSTART:20230406T110000Z
DTEND:20230406T190000Z
LOCATION:Admitting
DESCRIPTION:Rest Time : 00:00\n
SUMMARY:Late shift
END:VEVENT
BEGIN:VEVENT
UID:46794060
DTSTAMP:20230410T230000Z
DTSTART:20230411T070000Z
DTEND:20230411T110000Z
LOCATION:Admitting
DESCRIPTION:Rest Time : 00:00\n
SUMMARY:Early shift
END:VEVENT
END:VCALENDAR

Wasn't sure if it was the format of the ics file, which is being produced by roster software at work, or a bug.

So I have passed this through the debugger tracing the failure sequentially: ic_read() >> ical() >> ic_dateframe()>> ic_datetime().

If you shorten the above ics file to just one event it works ok, although does not correctly identify that the times are offset by 1 hour as they are actually BST but stored at GMT:

BEGIN:VCALENDAR
VERSION:2.0
PRODID:healthroster
X-WR-CALNAME:R
BEGIN:VEVENT
UID:45768392
DTSTAMP:20230405T230000Z
DTSTART:20230406T110000Z
DTEND:20230406T190000Z
LOCATION:Admitting
DESCRIPTION:Rest Time : 00:00\n
SUMMARY:Late shift
END:VEVENT
END:VCALENDAR

Interestingly if I import an ics created with MacOS calendar with multiple events, it completes parsing it but it fails to convert the date columns to datetime class:

BEGIN:VCALENDAR
CALSCALE:GREGORIAN
PRODID:-//Apple Inc.//macOS 13.4.1//EN
VERSION:2.0
X-WR-CALNAME:Acute Med
BEGIN:VTIMEZONE
TZID:Europe/London
BEGIN:DAYLIGHT
DTSTART:19810329T010000
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
TZNAME:BST
TZOFFSETFROM:+0000
TZOFFSETTO:+0100
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:19961027T020000
RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
TZNAME:GMT
TZOFFSETFROM:+0100
TZOFFSETTO:+0000
END:STANDARD
END:VTIMEZONE
BEGIN:VEVENT
CREATED:20230320T213321Z
DESCRIPTION:Rest Time : 00:00\n
DTEND;TZID=Europe/London:20230630T160000
DTSTAMP:20230709T221931Z
DTSTART;TZID=Europe/London:20230630T120000
LAST-MODIFIED:20230320T213321Z
LOCATION:ED
SEQUENCE:0
SUMMARY:ED PM - PM - Medics - AMU Medical Staff
TRANSP:OPAQUE
UID:5C15BDCE-C77A-4A67-BBD3-8D5AD233A034
END:VEVENT
BEGIN:VEVENT
CREATED:20230320T213321Z
DESCRIPTION:Rest Time : 00:00\n
DTEND;TZID=Europe/London:20230630T120000
DTSTAMP:20230709T221931Z
DTSTART;TZID=Europe/London:20230630T080000
LAST-MODIFIED:20230320T213321Z
LOCATION:Admitting
SEQUENCE:0
SUMMARY:Take AM - Day - Medics - AMU Medical Staff
TRANSP:OPAQUE
UID:E8451B5F-FCD7-4FE8-BF8E-52B76AAAFEA1
END:VEVENT
END:VCALENDAR

At present my work around is to use the ical package which does seem to parse all the above correctly and also even corrects the times which are stored offset to GMT by the roster software but it doesn't have such comprehensive tools as your package.

Unfortunately I am not good enough at coding to get my head round what the problems are.

Robinlovelace commented 1 year ago

Thanks @silverfoxdoc for the updated info. We have limited capacity to work on this at the moment so anything to help, including a fully reproducible example, could really help.

silverfoxdoc commented 1 year ago
library(ical)
library(calendar)

# 1) example using a calendar initially produced by work health roster software
# import calendar text to object then write out to ics file in preparation for import
# calendar packages

# the double escape at line 20 included to produce a single \ which
# appears in the original file

health_roster_calendar_file_one_item <- "BEGIN:VCALENDAR
VERSION:2.0
PRODID:healthroster
X-WR-CALNAME:R
BEGIN:VEVENT
UID:45768392
DTSTAMP:20230405T230000Z
DTSTART:20230406T110000Z
DTEND:20230406T190000Z
LOCATION:Admitting
DESCRIPTION:Rest Time : 00:00\\n
SUMMARY:Late shift
END:VEVENT
END:VCALENDAR"

writeLines(health_roster_calendar_file_one_item, "health_roster_one_item.ics")

# ical package produces the correct times; the times are stored as zulu time and
# it automatically offsets this to be correct to the local time zone on my computer which
# Europe/London and at time of writing is BST (British Summer Time)

ical::ical_parse_df("health_roster_one_item.ics")
#>        uid    summary               start                 end
#> 1 45768392 Late shift 2023-04-06 12:00:00 2023-04-06 20:00:00
#>           description       last.modified status
#> 1 Rest Time : 00:00\n 1970-01-01 01:00:00     NA

# calendar parses and correctly produces columns with class dttm but it does not correctly 
# offset the time like ical does

calendar::ic_read("health_roster_one_item.ics")
#> # A tibble: 1 × 7
#>   UID      DTSTAMP  DTSTART             DTEND               LOCATION DESCRIPTION
#>   <chr>    <chr>    <dttm>              <dttm>              <chr>    <chr>      
#> 1 45768392 2023040… 2023-04-06 11:00:00 2023-04-06 19:00:00 Admitti… "Rest Time…
#> # ℹ 1 more variable: SUMMARY <chr>

# 2) example using an ics file with two events produced by health roster software
# import same process as above

health_roster_calendar_file_two_items <- "BEGIN:VCALENDAR
VERSION:2.0
PRODID:healthroster
X-WR-CALNAME:R
BEGIN:VEVENT
UID:45768392
DTSTAMP:20230405T230000Z
DTSTART:20230406T110000Z
DTEND:20230406T190000Z
LOCATION:Admitting
DESCRIPTION:Rest Time : 00:00\\n
SUMMARY:Late shift
END:VEVENT
BEGIN:VEVENT
UID:46794060
DTSTAMP:20230410T230000Z
DTSTART:20230411T070000Z
DTEND:20230411T110000Z
LOCATION:Admitting
DESCRIPTION:Rest Time : 00:00\\n
SUMMARY:Early shift
END:VEVENT
END:VCALENDAR"

writeLines(health_roster_calendar_file_two_items, "health_roster_two_items.ics")

# ical package again parses corectly
ical::ical_parse_df("health_roster_two_items.ics")
#>        uid     summary               start                 end
#> 1 45768392  Late shift 2023-04-06 12:00:00 2023-04-06 20:00:00
#> 2 46794060 Early shift 2023-04-11 08:00:00 2023-04-11 12:00:00
#>           description       last.modified status
#> 1 Rest Time : 00:00\n 1970-01-01 01:00:00     NA
#> 2 Rest Time : 00:00\n 1970-01-01 01:00:00     NA

# calendar fails to parse at all
calendar::ic_read("health_roster_two_items.ics")
#> Error in if (!is.na(x) & !x == "NA" & !grepl("^\\d{8}T\\d{6}Z?$", x)) {: the condition has length > 1

# 3) example using calendar originally exported using MacOS calendar
# import same process as above

mac_os_calendar <- "BEGIN:VCALENDAR
CALSCALE:GREGORIAN
PRODID:-//Apple Inc.//macOS 13.4.1//EN
VERSION:2.0
X-WR-CALNAME:Acute Med
BEGIN:VTIMEZONE
TZID:Europe/London
BEGIN:DAYLIGHT
DTSTART:19810329T010000
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
TZNAME:BST
TZOFFSETFROM:+0000
TZOFFSETTO:+0100
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:19961027T020000
RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
TZNAME:GMT
TZOFFSETFROM:+0100
TZOFFSETTO:+0000
END:STANDARD
END:VTIMEZONE
BEGIN:VEVENT
CREATED:20230320T213321Z
DESCRIPTION:Rest Time : 00:00\\n
DTEND;TZID=Europe/London:20230630T160000
DTSTAMP:20230709T221931Z
DTSTART;TZID=Europe/London:20230630T120000
LAST-MODIFIED:20230320T213321Z
LOCATION:ED
SEQUENCE:0
SUMMARY:ED PM - PM - Medics - AMU Medical Staff
TRANSP:OPAQUE
UID:5C15BDCE-C77A-4A67-BBD3-8D5AD233A034
END:VEVENT
BEGIN:VEVENT
CREATED:20230320T213321Z
DESCRIPTION:Rest Time : 00:00\\n
DTEND;TZID=Europe/London:20230630T120000
DTSTAMP:20230709T221931Z
DTSTART;TZID=Europe/London:20230630T080000
LAST-MODIFIED:20230320T213321Z
LOCATION:Admitting
SEQUENCE:0
SUMMARY:Take AM - Day - Medics - AMU Medical Staff
TRANSP:OPAQUE
UID:E8451B5F-FCD7-4FE8-BF8E-52B76AAAFEA1
END:VEVENT
END:VCALENDAR"

writeLines(mac_os_calendar, "mac_os_calendar.ics")

# ical package parses correctly

ical::ical_parse_df("mac_os_calendar.ics")
#>                                    uid
#> 1                                 <NA>
#> 2 5C15BDCE-C77A-4A67-BBD3-8D5AD233A034
#> 3 E8451B5F-FCD7-4FE8-BF8E-52B76AAAFEA1
#>                                      summary               start
#> 1                                       <NA> 1970-01-01 01:00:00
#> 2    ED PM - PM - Medics - AMU Medical Staff 2023-06-30 12:00:00
#> 3 Take AM - Day - Medics - AMU Medical Staff 2023-06-30 08:00:00
#>                   end         description       last.modified status
#> 1 1970-01-01 01:00:00                <NA> 1970-01-01 01:00:00     NA
#> 2 2023-06-30 16:00:00 Rest Time : 00:00\n 2023-03-20 21:33:21     NA
#> 3 2023-06-30 12:00:00 Rest Time : 00:00\n 2023-03-20 21:33:21     NA

# calendar parses but does not correctly allocate the datetime columns as dttm class

calendar::ic_read("mac_os_calendar.ics")
#> # A tibble: 2 × 11
#>   CREATED      DESCRIPTION DTEND;TZID=Europe/Lo…¹ DTSTAMP DTSTART;TZID=Europe/…²
#>   <chr>        <chr>       <chr>                  <chr>   <chr>                 
#> 1 20230320T21… "Rest Time… 20230630T160000        202307… 20230630T120000       
#> 2 20230320T21… "Rest Time… 20230630T120000        202307… 20230630T080000       
#> # ℹ abbreviated names: ¹​`DTEND;TZID=Europe/London`,
#> #   ²​`DTSTART;TZID=Europe/London`
#> # ℹ 6 more variables: `LAST-MODIFIED` <chr>, LOCATION <chr>, SEQUENCE <chr>,
#> #   SUMMARY <chr>, TRANSP <chr>, UID <chr>

Created on 2023-07-16 with reprex v2.0.2

silverfoxdoc commented 1 year ago

put together a reprex as best I can to try to highlight the three issues I have come across

Robinlovelace commented 1 year ago

Many thanks @silverfoxdoc . I will try to look at this but have limited time at the moment I'm afraid.

silverfoxdoc commented 1 year ago

thanks for adding to the, no doubt, huge do list @Robinlovelace

francoscarafia commented 5 months ago

Hi

I have just send a pull request to fix this The error occurred because the warning from function ic_datetime is not vectorized

Replacing the !is.na(x) & !x == "NA" & !grepl("^\d{8}T\d{6}Z?$", x) with any(!is.na(x) & !(x == "NA") & !grepl("^\\d{8}T\\d{6}Z?$", x)) in the Ifwill fix the issue

Thanks and regards

Robinlovelace commented 5 months ago

Amazing. Will check the PR now :pray:

silverfoxdoc commented 5 months ago

just sent a pull request to fix the zulu time zone assignment problem I was experiencing above; see what you think and if it should be included

Robinlovelace commented 5 months ago

Thank you, will check now!