├── .gitignore ├── picture.jpg ├── logseq_timeline.Rproj ├── README.md └── logseq_timeline.R /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | .RData 4 | .Ruserdata 5 | -------------------------------------------------------------------------------- /picture.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nickriches/logseq_timeline/HEAD/picture.jpg -------------------------------------------------------------------------------- /logseq_timeline.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: Default 4 | SaveWorkspace: Default 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: Sweave 13 | LaTeX: pdfLaTeX 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # logseq_timeline 2 | 3 | An R Shiny App for visualising timelines in Logseq 4 | 5 | # Design 6 | 7 | The script scans your entire vault and reads every file. It uses Regexes to identify any event which is scheduled / has a deadline, i.e. date is in the format <2021-08-07>. It is thus computationally quite intensive, but seems to be very quick for small vaults. 8 | 9 | # Usage 10 | 11 | Place the .R file in your logseq vault and run. For this to work you need "R" installed, and preferably "RStudio". You need to install the following packages in R for this to work: 12 | 13 | `shiny` `timevis` `tidyverse` and `tools` 14 | 15 | # Output 16 | 17 | The output will look like this... 18 | 19 | ![](picture.jpg) 20 | 21 | You can zoom in and scroll from left to right. Labels will adapt to fit the window. 22 | 23 | # Possible roadmap 24 | 25 | - Categorising events according to backlinks, and showing different categories in different lines 26 | - Colouring events according to categories 27 | - Making it more interactive, so that the user can select events from different categories. -------------------------------------------------------------------------------- /logseq_timeline.R: -------------------------------------------------------------------------------- 1 | --- 2 | output: html_document 3 | runtime: shiny 4 | --- 5 | 6 | # Load relevant packages 7 | 8 | library(shiny) 9 | library(timevis) 10 | library(tidyverse) 11 | library(tools) 12 | 13 | # Obtain list of markdown files in directory 14 | 15 | files <- file.info(list.files(full.names = TRUE, recursive = TRUE, pattern = "*.md")) 16 | files$path <- row.names(files) 17 | files$filename <- basename(files$path) 18 | 19 | # Build database of scheduled strings 20 | 21 | scheduled_task_string <- "[\t]?-[^\x2d]+<[0-9a-zA-Z\x20\x2d]+>" 22 | 23 | df <- data.frame(content = as.character()) 24 | 25 | for(i in 1:nrow(files)){ 26 | x <- read_file(files$path[i]) 27 | content <- unlist(str_extract_all(x, scheduled_task_string)) 28 | content.df <- as.data.frame(content) 29 | df <- rbind(df, content.df) 30 | } 31 | 32 | # Create various functions for manipulating strings 33 | 34 | remove_time_stamps <- function(x){ # removes time stamps 35 | return(str_replace_all(x, "[a-zA-Z]+::[\x20]+[0-9]+", "")) 36 | } 37 | 38 | remove_multiple_spaces <- function(x){ # Removes multiples spaaces 39 | return(str_replace_all(x, "[\x20]{2,10}", "")) 40 | } 41 | 42 | extract_date <- function(x){ # Extracts the date (i.e. returns a string with all characgers except date removed) 43 | return(str_extract(x, "[0-9]{4}-[0-9]{2}-[0-9]{2}")) 44 | } 45 | 46 | remove_dates <- function(x){ # removes dates 47 | return(str_replace(x, "[a-zA-Z]+:[\x20]<.+>", "")) 48 | } 49 | 50 | has_DONE <- function(x){ # Returns TRUE is task is marked "DONE" 51 | return(str_detect(x, "- DONE")) 52 | } 53 | 54 | 55 | remove_links <- function(x){ # Removes all links in a string 56 | return(str_replace(x, "\\[\\[.+\\]\\]", "")) 57 | } 58 | 59 | extract_links <- function(x){ # Extracts links (returns a string with all characters removed except links) 60 | result <- str_extract(x, "\\[\\[.+\\]\\]") 61 | result <- str_replace_all(result, "\\/[a-zA-Z\\/]+", "") 62 | return(result) 63 | } 64 | 65 | remove_dash <- function(x){ # removes dashes from a string 66 | return(str_replace(x, "^[^a-zA-Z]*", "")) 67 | } 68 | 69 | insert_regular_return_characters <- function(x){ # Adds a return character every four words 70 | words <- unlist(str_extract_all(x, "[^\x20]+")) # This prevents labels being too wide 71 | num_blocks <- as.integer(length(words)/4) # NB return once every four words 72 | if(num_blocks > 0){ 73 | seq <- seq(4, num_blocks*4, 4) 74 | words[seq] <- str_replace_all(words[seq], "(.+)", "\\1
") 75 | } 76 | return(paste(words, collapse = " ")) 77 | } 78 | 79 | 80 | today <- Sys.Date() 81 | 82 | df %>% 83 | mutate(has_DONE = sapply(content, has_DONE)) %>% 84 | filter(has_DONE == FALSE) %>% 85 | select(-has_DONE) -> df 86 | df$content <- sapply(df$content, remove_time_stamps) 87 | df$content <- sapply(df$content, remove_multiple_spaces) 88 | 89 | df$start <- sapply(df$content, extract_date) 90 | 91 | df$start <- as.Date(df$start) 92 | 93 | df %>% filter(start >= (today-14)) -> df # Only shows events after two weeks ago 94 | 95 | df$content <- sapply(df$content, remove_dates) 96 | df$content <- sapply(df$content, remove_dash) 97 | df$content <- sapply(df$content, insert_regular_return_characters) 98 | 99 | df$end <- NA 100 | 101 | df$groups <- sapply(df$content, extract_links) 102 | 103 | groups <- df %>% select(groups) 104 | 105 | # Uncoment this line if you want links removed from output. 106 | # df$content <- sapply(df$content, remove_links) 107 | 108 | df$id = 1:nrow(df) 109 | 110 | # Produce timelines as timeline.html file 111 | 112 | ui <- fluidPage( 113 | timevisOutput("timeline") 114 | ) 115 | 116 | server <- function(input, output, session) { 117 | output$timeline <- renderTimevis({ 118 | timevis(df, options = list(editable = TRUE)) 119 | }) 120 | 121 | 122 | } 123 | 124 | shinyApp(ui = ui, server = server) 125 | 126 | 127 | 128 | 129 | 130 | --------------------------------------------------------------------------------