[
  {
    "path": ".gitignore",
    "content": ".DS_Store\n*.html\n!FacebookFriends.html\n!FacebookPhotosOfYou.html\n!UselessScraps.html\n*.csv\n.Rproj.user\n/img/*\n*.jpeg\n*.jpg\n*.ics\n"
  },
  {
    "path": "FacebookFriends.Rmd",
    "content": "---\ntitle: \"Parsing Facebook friends HTML\"\noutput: github_document\n---\n\n```{r setup, include=FALSE}\nknitr::opts_chunk$set(echo = TRUE, eval = FALSE)\nlibrary(rvest)\nlibrary(tibble)\nlibrary(readr)\nlibrary(here)\n```\n\n## Parsing Facebook friends from HTML (not recommended)\n\nSince I downloaded my data in HTML format rather than the much easier to use JSON format, I had to parse it. Here's my code for that:\n\n```{r}\nhere(\"../Documents/facebook-ameliamcnamara/friends\")\n\nfacebookfriend_html <- read_html(here(\"../Documents/facebook-ameliamcnamara/friends\", \"friends.html\"))\n\nfriends <- html_nodes(facebookfriend_html, \"._2lel\") %>%\n  html_text() %>%\n  as.tibble()\n\n## write_csv(friends, \"facebookfriends.csv\")\n```\n\nIf you are following along, you should download your data as JSON to avoid having to do this. \n\n"
  },
  {
    "path": "FacebookFriends.md",
    "content": "Parsing Facebook friends HTML\n================\n\nParsing Facebook friends from HTML (not recommended)\n----------------------------------------------------\n\nSince I downloaded my data in HTML format rather than the much easier to use JSON format, I had to parse it. Here's my code for that:\n\n``` r\nhere(\"../Documents/facebook-ameliamcnamara/friends\")\n\nfacebookfriend_html <- read_html(here(\"../Documents/facebook-ameliamcnamara/friends\", \"friends.html\"))\n\nfriends <- html_nodes(facebookfriend_html, \"._2lel\") %>%\n  html_text() %>%\n  as.tibble()\n\n## write_csv(friends, \"facebookfriends.csv\")\n```\n\nIf you are following along, you should download your data as JSON to avoid having to do this.\n"
  },
  {
    "path": "FacebookPhotosOfYou.Rmd",
    "content": "---\ntitle: \"Scraping tagged photos\"\noutput: github_document\n---\n\n```{r setup, include=FALSE}\nknitr::opts_chunk$set(echo = TRUE, eval = FALSE)\nlibrary(tidyverse)\n```\n\n## Tagged photos of me\n\nAnother goal was to get tagged \"photos of you\" pictures backed up. Facebook really doesn't want you to do this. I tried several approaches. (Some that failed are detailed in UselessScraps.Rmd)\n\n### RSelenium\n\nSelenium is good because it drives a real browser. This is the approach the Python scripts for scraping tagged photos take. Those scripts don't work because Facebook has changed the HTML, but I figured I could debug the code better in R. \n\nSince I don't know about Docker, I just used the `rsDriver` function to navigate. \n```{r}\nlibrary(RSelenium)\nrD <- rsDriver(port = 4445L, browser = \"firefox\", version = \"3.141.59\")\nremDr <- rD$client\n```\n\nI was able to get logged in pretty easily.\n\n```{r}\nremDr$navigate(\"https://www.facebook.com/\")\n\nYourLogIN <- \"\"\nYourPassword <- \"\" # this is where your password goes\n\nremDr$findElement(\"id\", \"email\")$sendKeysToElement(list(YourLogIN))\nremDr$findElement(\"id\", \"pass\")$sendKeysToElement(list(YourPassword))\nremDr$findElement(\"id\", \"loginbutton\")$clickElement()\n```\n\nThe first step is to navigate to photos of me. I am lazy, so I just hardcoded this to my facebook URL. There is probably a way to grab this automatically, but we're aiming low here. \n\n```{r}\nremDr$navigate(\"https://www.facebook.com/amelia.mcnamara/photos_of\")\n```\n\nThen, I click on the first tagged photo of me to get the slideshow to start. \n```{r}\nwebElem <- remDr$findElement(using = \"class\", \"uiMediaThumb\")\nwebElem$clickElement()\n```\n\nIt would be handy if we could right-click with RSelenium to download the files. But, it doesn't work. So instead we're just going to grab the src links and do the downloading separately. \n\nI tried a couple approaches with while statements that did not work. So, here is another hacky solution:\n\n- make a character vector of length 5000 (seemed likely to be a much greater number than the number of tagged photos of me)\n- run a for loop to iteratively grab the src and click on the next button\n- checked to see if the current src was the same as the first (photos eventually loop back around) and if so, stop\n\n```{r}\nsrcs <- character(5000)\n\n## first two\nfor (i in 1:2) {\n  img1 <- remDr$findElement(using = \"class\", \"spotlight\")\n  srcs[i] <- img1$getElementAttribute(\"src\")[[1]]\n  nextButton <- remDr$findElement(using = \"class\", \"next\")\n  nextButton$clickElement()\n}\n\nfor (i in 3:5000) {\n  img1 <- remDr$findElement(using = \"class\", \"spotlight\")\n  srcs[i] <- img1$getElementAttribute(\"src\")[[1]]\n  if (srcs[i] == srcs[1]) {\n    stop(paste(\"done, with\", i-1, \"photos\"))\n  }\n  nextButton <- remDr$findElement(using = \"class\", \"next\")\n  nextButton$clickElement()\n}\n```\n\nThen, I made a tibble with those urls, and made sure to just grab the distinct ones. Then I made up some names for the files I was about to download. I saved this dataframe to an external file so I wouldn't lose those valuable URLs!\n\n```{r}\nsrcs_df <- tibble(url = srcs)\nsrcs_df <- srcs_df %>%\n  distinct(url)\nsrcs_df <- srcs_df %>%\n  mutate(filename = paste0(\"img/FB_download\", row_number(), \".jpg\"))\n\nwrite_csv(srcs_df, \"fb_images.csv\")\n```\n\nTo finish up with RSelenium, you need to close the session,\n```{r}\nremDr$close()\n```\n\nSomehow this never fully works, so I end up having to go into Activity Monitor and kill the Java process. \n\n## Downloading images\n\nThe final step is to download the images. I ended up with 878 tagged photos on facebook, so again I did a for loop (perhaps this could have been vectorized?). \n\n```{r}\nlibrary(curl)\nsrcs_df <- read_csv(\"fb_images.csv\")\nfor (i in 1:878) {\n  curl_download(srcs_df$url[i], srcs_df$filename[i])\n}\n```\n\n\nSuccess! I now have all the files on my local computer. And, if I want to grab them on a different computer I have the csv of URLs (which don't seem to have time-expirations in them, as some facebook URLs do) to run the `curl` stuff again. \n"
  },
  {
    "path": "FacebookPhotosOfYou.md",
    "content": "Scraping tagged photos\n================\n\n## Tagged photos of me\n\nAnother goal was to get tagged “photos of you” pictures backed up.\nFacebook really doesn’t want you to do this. I tried several approaches.\n(Some that failed are detailed in UselessScraps.Rmd)\n\n### RSelenium\n\nSelenium is good because it drives a real browser. This is the approach\nthe Python scripts for scraping tagged photos take. Those scripts don’t\nwork because Facebook has changed the HTML, but I figured I could debug\nthe code better in R.\n\nSince I don’t know about Docker, I just used the `rsDriver` function to\nnavigate.\n\n``` r\nlibrary(RSelenium)\nrD <- rsDriver(port = 4445L, browser = \"firefox\", version = \"3.141.59\")\nremDr <- rD$client\n```\n\nI was able to get logged in pretty easily.\n\n``` r\nremDr$navigate(\"https://www.facebook.com/\")\n\nYourLogIN <- \"\"\nYourPassword <- \"\" # this is where your password goes\n\nremDr$findElement(\"id\", \"email\")$sendKeysToElement(list(YourLogIN))\nremDr$findElement(\"id\", \"pass\")$sendKeysToElement(list(YourPassword))\nremDr$findElement(\"id\", \"loginbutton\")$clickElement()\n```\n\nThe first step is to navigate to photos of me. I am lazy, so I just\nhardcoded this to my facebook URL. There is probably a way to grab this\nautomatically, but we’re aiming low here.\n\n``` r\nremDr$navigate(\"https://www.facebook.com/amelia.mcnamara/photos_of\")\n```\n\nThen, I click on the first tagged photo of me to get the slideshow to\nstart.\n\n``` r\nwebElem <- remDr$findElement(using = \"class\", \"uiMediaThumb\")\nwebElem$clickElement()\n```\n\nIt would be handy if we could right-click with RSelenium to download the\nfiles. But, it doesn’t work. So instead we’re just going to grab the src\nlinks and do the downloading separately.\n\nI tried a couple approaches with while statements that did not work. So,\nhere is another hacky solution:\n\n  - make a character vector of length 5000 (seemed likely to be a much\n    greater number than the number of tagged photos of me)\n  - run a for loop to iteratively grab the src and click on the next\n    button\n  - checked to see if the current src was the same as the first (photos\n    eventually loop back around) and if so, stop\n\n<!-- end list -->\n\n``` r\nsrcs <- character(5000)\n\n## first two\nfor (i in 1:2) {\n  img1 <- remDr$findElement(using = \"class\", \"spotlight\")\n  srcs[i] <- img1$getElementAttribute(\"src\")[[1]]\n  nextButton <- remDr$findElement(using = \"class\", \"next\")\n  nextButton$clickElement()\n}\n\nfor (i in 3:5000) {\n  img1 <- remDr$findElement(using = \"class\", \"spotlight\")\n  srcs[i] <- img1$getElementAttribute(\"src\")[[1]]\n  if (srcs[i] == srcs[1]) {\n    stop(paste(\"done, with\", i-1, \"photos\"))\n  }\n  nextButton <- remDr$findElement(using = \"class\", \"next\")\n  nextButton$clickElement()\n}\n```\n\nThen, I made a tibble with those urls, and made sure to just grab the\ndistinct ones. Then I made up some names for the files I was about to\ndownload. I saved this dataframe to an external file so I wouldn’t lose\nthose valuable URLs\\!\n\n``` r\nsrcs_df <- tibble(url = srcs)\nsrcs_df <- srcs_df %>%\n  distinct(url)\nsrcs_df <- srcs_df %>%\n  mutate(filename = paste0(\"img/FB_download\", row_number(), \".jpg\"))\n\nwrite_csv(srcs_df, \"fb_images.csv\")\n```\n\nTo finish up with RSelenium, you need to close the session,\n\n``` r\nremDr$close()\n```\n\nSomehow this never fully works, so I end up having to go into Activity\nMonitor and kill the Java process.\n\n## Downloading images\n\nThe final step is to download the images. I ended up with 878 tagged\nphotos on facebook, so again I did a for loop (perhaps this could have\nbeen vectorized?).\n\n``` r\nlibrary(curl)\nsrcs_df <- read_csv(\"fb_images.csv\")\nfor (i in 1:878) {\n  curl_download(srcs_df$url[i], srcs_df$filename[i])\n}\n```\n\nSuccess\\! I now have all the files on my local computer. And, if I want\nto grab them on a different computer I have the csv of URLs (which don’t\nseem to have time-expirations in them, as some facebook URLs do) to run\nthe `curl` stuff again.\n"
  },
  {
    "path": "LeavingFacebook.txt",
    "content": "In two weeks, I will be deleting my facebook account.\n\nI’ve thought about this for years, and while I love facebook and have gotten so much out of it, as someone who teaches about data I can no longer justify having an account. For me, the final straw was facebook’s decision to exempt politicians from fact-checking. \n\nFacebook has made it clear again and again that all they value is money. Yes, I know they are a corporation. But they could choose to do the right thing and sacrifice a little profit in order to be a force for good in the world. They have tremendous power to influence people’s lives, and as far as I can tell, they are abusing it. \n\n[By the way, if you see this post in your feed I’d appreciate it if you’d “like” or “heart” it, because I’ve heard that will boost it in the algorithm.]\n\nI’ll still be active on social media. While I’m deleting Facebook, Messenger, and WhatsApp, I’m keeping Instagram for the time being, I’m on LinkedIn (maybe I’ll figure out what that’s for) and I’m very active on Twitter. \n\nI’m also starting a little newsletter about my life. My plan is to send an update maybe four times a year. Anyone who I’m friends with on here is welcome to subscribe. It doesn’t matter if we went to high school together and haven’t talked in years, if you’re a coworker from that one summer I tempted at Toyota Financial Services, or you’re one of my far-flung cousins. Somehow it feels like it’s more personal to subscribe to a newsletter than to be facebook friends, but in this transition I’m trying for more personal connection. Here’s the link: [redacted]\n\nIt’s strange that I feel something like grief as I leave facebook. I joined in 2006, and have logged in almost every day since. My relationships and friendships are documented here. Facebook has allowed me to watch the kids of Unitarian friends from Los Angeles grow up. It helped me meet up with a friend I know from the University of Cincinnati who was visiting Minneapolis from Italy. Facebook suggests events I might otherwise not have heard of. It connects me with family near and far. \n\nBut, I give talks about Algorithmic Inequality, and facebook perpetuates that. \n\nThe reason for the two-week delay is to allow me to gather my things. I’ve already downloaded my facebook data and am backing that up in a couple spots. I’m planning to go through photos I’m tagged in and save some of those. Once I have the digital files I’ll be getting favorite pictures printed out. I want to make sure I capture people’s birthdays on my calendar.\n\nIt’s also a chance for you to check in, say bye, give me your email address or cellphone number or weird newsletter subscription link. I am really going to miss the connections! So let’s stay connected."
  },
  {
    "path": "README.md",
    "content": "# Documentation of what I did to prepare to delete my Facebook account. \n\nIf you want a more narrative view of this whole thing, check out [the blogpost I wrote about the whole process](https://www.amelia.mn/blog/2019-12-29-Deleting-Facebook/). Otherwise, see the bullets below. \n\nHere are the steps I took:\n\n- [Downloaded my facebook data](https://www.facebook.com/help/1701730696756992/?helpref=hc_fnav). This comes with a lot of data. Most importantly, all the photos I had uploaded to facebook over the years, organized by album, and a list of all my facebook friends. Initially, I unchecked some boxes in the facebook data, so I hadn't downloaded the list of Apps and Websites I used facebook to log in with. I eventually went back and grabbed those. \n- [Posted on facebook](LeavingFacebook.txt) about my plan, announcing I'm starting a small (personal) newsletter, and giving a two-week countdown to get my affairs in order.\n- [Parsed the HTML file of my friends list](FacebookFriends.md). This HTML file is probably the least-useful way they could have provided the list of my friends, but with the help of [rvest](https://rvest.tidyverse.org/) and [SelectorGadget](https://selectorgadget.com/) I was able to find the right elements to get a data frame. [Edit! I missed the option to dowload your data in JSON format, which is much easier to parse than the HTML. This would be my recommended method.]\n- Made a google sheet of my entire friends list, as an accessible place for the data. \n- Went through the entire list categorizing how I knew people and how much I want to stay in contact. (This took several days, as I had over 600 facebook friends!)\n- Exported my friends' birthdays from facebook. Facebook has changed their settings, so it's no longer easy to do this. But, [mobeigi has a python script](https://github.com/mobeigi/fb2cal) that will do it for you. As he warns, using this script made facebook temporarily suspend my account because of suspected security issues. I was able to get back in.\n- [Downloaded all the photos I am tagged in](FacebookPhotosOfYou.md).  There were methods available on the internet using Python (see, [gnmerritt](https://gnmerritt.net/deletefacebook/2018/04/03/fb-photos-of-me/), [jcontini](https://github.com/jcontini/fb-photo-downloader), and [KatyaRuth](https://github.com/KatyaRuth/photos-of-you)), but none of them worked with the current version of facebook (I think Facebook likely changed their API to make it more difficult to scrape this stuff). I'm not a Python person, so it was hard for me to debug what was not working about them. I wrote [my own code using RSelenium](FacebookPhotosOfYou.md) to do the browser stuff. It is highly inefficient, but it works!  \n- Made sure the Apps and Websites I used facebook to log in to have alternate modes of access. \n- Go through my google sheet again and make sure I have other modes of contact for the folks I want to stay in touch with. [Didn't do a great job of this, but it's time to let go!]\n- **Actually deleted my account!!**\n- [Parsed the birthday .ics file](parsing_birthdays.md) to save just the birthdays of people I want to stay in contact with. (I don't need 600+ birthday reminders on my calendar!)\n- Ordered prints of favorite photos from Spoonflower, organized them into a photo album.\n- Cleared cookies from my work computer, home computer, and phone\n- Wrote [a blog post](https://www.amelia.mn/blog/2019-12-29-Deleting-Facebook/) about my entire process\n\n\n## What am I going to use instead? \n\n- I'm still going to be active on Twitter. I have a LinkedIn account. And, for right now I'm keeping Instagram. I know Instagram is owned by facebook, but it just feels like too much to delete IG right now. Baby steps!\n- I'm starting a personal newsletter to send updates to my friends and family. I'm using [TinyLetter](https://tinyletter.com/) for this.\n- To keep up on local events, I'm planning to pay closer attention to local arts publications, like the [City Pages](http://www.citypages.com/) in the Twin Cities. \n- For events I'm hosting, I'll continue to use [Paperless Post](https://www.paperlesspost.com/) to invite folks (I haven't used FB for event invites in a couple of years).\n- Mostly, I'm going to be emailing, texting, calling, and talking face-to-face with my social network! \n\n"
  },
  {
    "path": "deleting_facebook.Rproj",
    "content": "Version: 1.0\n\nRestoreWorkspace: Default\nSaveWorkspace: Default\nAlwaysSaveHistory: Default\n\nEnableCodeIndexing: Yes\nUseSpacesForTab: Yes\nNumSpacesForTab: 2\nEncoding: UTF-8\n\nRnwWeave: knitr\nLaTeX: pdfLaTeX\n"
  },
  {
    "path": "parsing_birthdays.Rmd",
    "content": "---\ntitle: \"Parsing friend's birthdays\"\noutput: github_document\n---\n\n```{r setup, include=TRUE, message=FALSE}\nknitr::opts_chunk$set(echo = TRUE, eval = FALSE)\nlibrary(calendar)\nlibrary(here)\nlibrary(dplyr)\nlibrary(readr)\n```\n\nThere is a package called `calendar`, which is supposed to parse .ics files. However, it doesn't work right out of the box on a facebook-generated .ics file.\n\n```{r, eval=FALSE}\nbirthdays_calendar_pkg <- ic_read(here(\"../Dropbox/birthdays.ics\"))\n```\n\nAll of the birthdays get set to the same date (October 3), which was the first birthday in my file. \n\nSo, now we need to debug to figure out what's going wrong. I am not a master debugger, so what I do is look at the source code for a function, and then run it step-by-step on my data to see where the error is. \n\n## ic_read\n\nThe top-level function is `ic_read`. \n\n```{r, eval=FALSE}\n## taken from the source code of ic_read\nbirthdays <- readLines(\"birthdays.ics\")\ny <- c()\n    for (line in birthdays) {\n        if (!grepl(pattern = \"[A-Z]-?[A-Z]:|[A-Z];\", x = line)) {\n            y[length(y)] <- paste0(y[length(y)], \"\\n\", line)\n        }\n        else {\n            y[length(y) + 1] <- line\n        }\n    } ## to this point, the data is still fine\nical(y) ## this is where everything becomes October 3\n```\n\n## ical\n\n```{r, eval=FALSE}\n## taken from the source code of ical\nx <- y\nis_df <- is.data.frame(x)\n    if (methods::is(x, \"character\")) {\n        ical_df <- ic_dataframe(x) ## here's where the next problem happens\n        ical_tibble <- tibble::as_tibble(ical_df)\n        if (is.null(ic_attributes)) {\n            attr(ical_tibble, \"ical\") <- ic_attributes_vec(x)\n        }\n        else {\n            attr(ical_tibble, \"ical\") <- ic_attributes\n        }\n    }\n    else {\n        if (!is_df) \n            stop(\"x must be a data frame or charcter strings\")\n        n <- names(x)\n        is_core = calendar::properties_core %in% n\n        if (!all(is_core)) {\n            stop(paste0(\"x must contain column names: \", paste0(calendar::properties_core, \n                collapse = \", \")))\n        }\n        ical_tibble <- tibble::as_tibble(x)\n        if (is.null(ic_attributes)) {\n            attr(ical_tibble, \"ical\") <- ic_attributes_vec()\n        }\n        else {\n            attr(ical_tibble, \"ical\") <- ic_attributes\n        }\n    }\n    class(ical_tibble) <- c(\"ical\", class(ical_tibble))\n    ical_tibble\n```\n\n\n## ic_dataframe\n```{r, eval=FALSE}       \n## taken from the source code of ic_dataframe\nx <- y\nif (methods::is(object = x, class2 = \"data.frame\")) {\n        return(x)\n    }\n    stopifnot(methods::is(object = x, class2 = \"character\") | \n        methods::is(object = x, class2 = \"list\"))\n    if (methods::is(object = x, class2 = \"character\")) {\n        x_list <- ic_list(x) ## seems fine here\n    }\n    else if (methods::is(object = x, class2 = \"list\")) {\n        x_list <- x\n    }\n    x_list_named <- lapply(x_list, function(x) {\n        ic_vector(x) ## fine here\n    })\n    x_df <- ic_bind_list(x_list_named) ## good here\n    date_cols <- grepl(pattern = \"VALUE=DATE\", x = names(x_df)) ## this is the right columns\n    if (any(date_cols)) {\n        tmp <- lapply(x_df[, date_cols], ic_date) ## here's the issue. I renamed the variable to tmp to debug\n        x_df <- x_df %>%\n          mutate(`DTSTART;VALUE=DATE` = ymd(`DTSTART;VALUE=DATE`)) # this works on my particular data\n    }\n    datetime_cols <- names(x_df) %in% c(\"DTSTART\", \"DTEND\")\n    if (any(datetime_cols)) {\n        x_df[datetime_cols] <- lapply(x_df[, datetime_cols], \n            ic_datetime)\n    }\n    x_df\n```\n\n## Working code\n\nSince I can't see what the issue is (so terrible at `tapply`) I didn't think I could fix the function itself. Instead, I just copied the working code into a messy long chunk. Here's what works for me: \n\n```{r}\nbirthdays <- readLines(\"birthdays.ics\")\ny <- c()\n    for (line in birthdays) {\n        if (!grepl(pattern = \"[A-Z]-?[A-Z]:|[A-Z];\", x = line)) {\n            y[length(y)] <- paste0(y[length(y)], \"\\n\", line)\n        }\n        else {\n            y[length(y) + 1] <- line\n        }\n    } ## to this point, the data is still fine\nx_list <- ic_list(y)\nx_list_named <- lapply(x_list, function(x) {\n        ic_vector(x) ## fine here\n})\nx_df <- ic_bind_list(x_list_named) \n\nx_df <- x_df %>%\n          mutate(`DTSTART;VALUE=DATE` = ymd(`DTSTART;VALUE=DATE`)) \nical_tibble <- tibble::as_tibble(x_df)\nattr(ical_tibble, \"ical\") <- ic_attributes_vec()\nclass(ical_tibble) <- c(\"ical\", class(ical_tibble))\n```\n\n## Parsing close friends\n\n```{r}\nbirthday_names <- ical_tibble %>%\n  select(SUMMARY)\nbirthday_names <- birthday_names %>%\n  separate(SUMMARY, into = \"name\", sep=\"'s Birthday\", remove=FALSE)\n```\n\n```{r}\nfacebookfriends_augmented <- read_csv(\"facebookfriends_augmented.csv\") %>%\n  filter(Rank %in% c(1,2))\nwhich(birthday_names$name %in% facebookfriends_augmented$name)\nbirthdays <- ical_tibble[which(birthday_names$name %in% facebookfriends_augmented$name),]\n```\n\n```{r, eval=FALSE}\nic_write(birthdays, \"close_friends_birthdays.ics\")\n```\n\nI was able to read the .ics file into my Calendar app and it all seems to have worked! Spot-checked birthdays are correct. \n\nOf course, not every close friend had their birthday listed on facebook. So, there is also the question of friends I should learn the birthday of:\n```{r}\nfind_birthdays <- facebookfriends_augmented[which(!facebookfriends_augmented$name %in% birthday_names$name),]\n```\n\n```{r, eval=FALSE}\nwrite_csv(find_birthdays, \"learn_birthdays.csv\")\n```"
  },
  {
    "path": "parsing_birthdays.md",
    "content": "Parsing friend’s birthdays\n================\n\n``` r\nknitr::opts_chunk$set(echo = TRUE, eval = FALSE)\nlibrary(calendar)\nlibrary(here)\nlibrary(dplyr)\nlibrary(readr)\n```\n\nThere is a package called `calendar`, which is supposed to parse .ics\nfiles. However, it doesn’t work right out of the box on a\nfacebook-generated .ics file.\n\n``` r\nbirthdays_calendar_pkg <- ic_read(here(\"../Dropbox/birthdays.ics\"))\n```\n\nAll of the birthdays get set to the same date (October 3), which was the\nfirst birthday in my file.\n\nSo, now we need to debug to figure out what’s going wrong. I am not a\nmaster debugger, so what I do is look at the source code for a function,\nand then run it step-by-step on my data to see where the error is.\n\n## ic\\_read\n\nThe top-level function is `ic_read`.\n\n``` r\n## taken from the source code of ic_read\nbirthdays <- readLines(\"birthdays.ics\")\ny <- c()\n    for (line in birthdays) {\n        if (!grepl(pattern = \"[A-Z]-?[A-Z]:|[A-Z];\", x = line)) {\n            y[length(y)] <- paste0(y[length(y)], \"\\n\", line)\n        }\n        else {\n            y[length(y) + 1] <- line\n        }\n    } ## to this point, the data is still fine\nical(y) ## this is where everything becomes October 3\n```\n\n## ical\n\n``` r\n## taken from the source code of ical\nx <- y\nis_df <- is.data.frame(x)\n    if (methods::is(x, \"character\")) {\n        ical_df <- ic_dataframe(x) ## here's where the next problem happens\n        ical_tibble <- tibble::as_tibble(ical_df)\n        if (is.null(ic_attributes)) {\n            attr(ical_tibble, \"ical\") <- ic_attributes_vec(x)\n        }\n        else {\n            attr(ical_tibble, \"ical\") <- ic_attributes\n        }\n    }\n    else {\n        if (!is_df) \n            stop(\"x must be a data frame or charcter strings\")\n        n <- names(x)\n        is_core = calendar::properties_core %in% n\n        if (!all(is_core)) {\n            stop(paste0(\"x must contain column names: \", paste0(calendar::properties_core, \n                collapse = \", \")))\n        }\n        ical_tibble <- tibble::as_tibble(x)\n        if (is.null(ic_attributes)) {\n            attr(ical_tibble, \"ical\") <- ic_attributes_vec()\n        }\n        else {\n            attr(ical_tibble, \"ical\") <- ic_attributes\n        }\n    }\n    class(ical_tibble) <- c(\"ical\", class(ical_tibble))\n    ical_tibble\n```\n\n## ic\\_dataframe\n\n``` r\n## taken from the source code of ic_dataframe\nx <- y\nif (methods::is(object = x, class2 = \"data.frame\")) {\n        return(x)\n    }\n    stopifnot(methods::is(object = x, class2 = \"character\") | \n        methods::is(object = x, class2 = \"list\"))\n    if (methods::is(object = x, class2 = \"character\")) {\n        x_list <- ic_list(x) ## seems fine here\n    }\n    else if (methods::is(object = x, class2 = \"list\")) {\n        x_list <- x\n    }\n    x_list_named <- lapply(x_list, function(x) {\n        ic_vector(x) ## fine here\n    })\n    x_df <- ic_bind_list(x_list_named) ## good here\n    date_cols <- grepl(pattern = \"VALUE=DATE\", x = names(x_df)) ## this is the right columns\n    if (any(date_cols)) {\n        tmp <- lapply(x_df[, date_cols], ic_date) ## here's the issue. I renamed the variable to tmp to debug\n        x_df <- x_df %>%\n          mutate(`DTSTART;VALUE=DATE` = ymd(`DTSTART;VALUE=DATE`)) # this works on my particular data\n    }\n    datetime_cols <- names(x_df) %in% c(\"DTSTART\", \"DTEND\")\n    if (any(datetime_cols)) {\n        x_df[datetime_cols] <- lapply(x_df[, datetime_cols], \n            ic_datetime)\n    }\n    x_df\n```\n\n## Working code\n\nSince I can’t see what the issue is (so terrible at `tapply`) I didn’t\nthink I could fix the function itself. Instead, I just copied the\nworking code into a messy long chunk. Here’s what works for me:\n\n``` r\nbirthdays <- readLines(\"birthdays.ics\")\ny <- c()\n    for (line in birthdays) {\n        if (!grepl(pattern = \"[A-Z]-?[A-Z]:|[A-Z];\", x = line)) {\n            y[length(y)] <- paste0(y[length(y)], \"\\n\", line)\n        }\n        else {\n            y[length(y) + 1] <- line\n        }\n    } ## to this point, the data is still fine\nx_list <- ic_list(y)\nx_list_named <- lapply(x_list, function(x) {\n        ic_vector(x) ## fine here\n})\nx_df <- ic_bind_list(x_list_named) \n\nx_df <- x_df %>%\n          mutate(`DTSTART;VALUE=DATE` = ymd(`DTSTART;VALUE=DATE`)) \nical_tibble <- tibble::as_tibble(x_df)\nattr(ical_tibble, \"ical\") <- ic_attributes_vec()\nclass(ical_tibble) <- c(\"ical\", class(ical_tibble))\n```\n\n## Parsing close friends\n\n``` r\nbirthday_names <- ical_tibble %>%\n  select(SUMMARY)\nbirthday_names <- birthday_names %>%\n  separate(SUMMARY, into = \"name\", sep=\"'s Birthday\", remove=FALSE)\n```\n\n``` r\nfacebookfriends_augmented <- read_csv(\"facebookfriends_augmented.csv\") %>%\n  filter(Rank %in% c(1,2))\nwhich(birthday_names$name %in% facebookfriends_augmented$name)\nbirthdays <- ical_tibble[which(birthday_names$name %in% facebookfriends_augmented$name),]\n```\n\n``` r\nic_write(birthdays, \"close_friends_birthdays.ics\")\n```\n\nI was able to read the .ics file into my Calendar app and it all seems\nto have worked\\! Spot-checked birthdays are correct.\n\nOf course, not every close friend had their birthday listed on facebook.\nSo, there is also the question of friends I should learn the birthday\nof:\n\n``` r\nfind_birthdays <- facebookfriends_augmented[which(!facebookfriends_augmented$name %in% birthday_names$name),]\n```\n\n``` r\nwrite_csv(find_birthdays, \"learn_birthdays.csv\")\n```\n"
  }
]