Weekly Journal Pages & auto overview of the week

Demo

2024-06-15 18.41.12

There are two ways of usage the Weekly Pages:

Option A: Via plugin :classical_building: Full House Templates

:+1: could be used with the Days plugin (the calendar in the sidebar in demo)
:+1: nice UI for selecting templates
:+1: written in javascript, so it is easy to adapt to your case

:-1: doesn’t work on mobile

:arrow_right: Setup instructions are here

Option B: Without a plugin

:+1: works on mobile
:+1: build with Logseq features only

:-1: written in clojure, so it is hard to adapt to your own needs

Let’s concentrate on Option B here

  1. To prepare the queries create anywhere in Logseq (ex: in «Queries» page) the following queries. I’ll use 2 queries for #podcast and #diary records. But you can setup your own ones:
    reference:: #podcast
    #+BEGIN_QUERY
    ...
    #+END_QUERY
    
    reference:: #diary
    #+BEGIN_QUERY
    ...
    #+END_QUERY
    
    Fill the body of queries with the same one code. Insert it between #+BEGIN_QUERY and #+END_QUERY with replacing the «…»:
    {
      :inputs [:current-page :current-block]
      :query [:find (pull ?b [*])
      :in $ ?week ?self %
      :where
        [?page :block/name ?week]
        (get-week ?page ?weekStart ?weekEnd)
        (between ?b ?weekStart ?weekEnd)
    
        [?self :block/page ?self-page]
        (tagged-with-property ?b ?self :reference)
      ]
      :rules [
        [(get-week ?block ?start ?end)
          [?block :block/properties ?props]
          [(get ?props :week-start) ?start]
          [(get ?props :week-end) ?end]
       ],
       [(tagged-with-property ?block ?props-block ?prop)
         [?props-block :block/properties-text-values ?props]
         [(get ?props ?prop) ?tag]
         [(subs ?tag 1) ?tag-name]
    
         [?block :block/refs ?ref]
         [?ref :block/name ?name]
         [(= ?name ?tag-name)]
       ]
     ]
    }
    
  2. Create template anywhere in Logseq (ex: in «Templates» page):
    - template:: weekly page
      template-including-parent:: false
        -
            - #+BEGIN_QUERY
              {
               :query [
                  :find ?cp ?cb-uuid
                  :in $ ?cp ?cb
                  :where
                     [?cb :block/uuid ?cb-uuid]
               ]
               :inputs [:current-page :current-block]
               :result-transform (fn [[page-name block-uuid]]
                  (def weekStartsFrom (get (js->clj (call-api "get_user_configs")) "preferredStartOfWeek"))
              
                  (def zerosToWidth (fn [num width]
                      (let [
                          num-s (str num)
                          zeros (repeat (- width (count num-s)) "0")
                        ]
                        (clojure.string/join (concat zeros num-s))
                      )
                    ))
              
                  (def parseWeek (fn [isoWeek]
                        (let [
                            regex (re-pattern "^(\\d{4}).*?(?:w|W)(\\d+)$")
                            [_ year weekn] (re-matches regex (str isoWeek))
                          ]
                          [(int year) (int weekn)]
                        )
                    ))
              
                  ;; source: [](https://discuss.logseq.com/t/add-query-input-or-function-day-of-week/18361/12)
                  (def days {0 "Saturday" 1 "Sunday" 2 "Monday" 3 "Tuesday" 4 "Wednesday" 5 "Thursday" 6 "Friday"})
                  (def weekDayN (fn [date]
                        (def month (quot (mod date 10000) 100))
                        (def month6 (quot (- month 8) 6))
                        (def year6 (+ (quot date 10000) month6))
                        (def yearnum (mod year6 100))
                        (def century (quot year6 100))
                        (def d (mod (+ (mod date 100) (quot (* 13 (inc (- month (* month6 12)))) 5) yearnum (quot   yearnum 4)
                          (quot century 4) (* 5 century)) 7))
                        d
                    ))
                  (def weekDay (fn [date]
                        (get days (weekDayN date))
                    ))
              
                  ;; naming and algorithm according to [ISO week date](https://www.wikiwand.com/en/ISO_week_date#Algorithms)
                  ;; day of week: 1..7 for Monday..Sunday
                  (def dow-mon (fn [date]
                        (def wd (- (weekDayN date) 1))
                        (if (<= wd 0) (+ wd 7) wd)
                    ))
                  ;; day of week: 1..7 for Sunday..Saturday
                  (def dow-sun (fn [date]
                        (def wd (weekDayN date))
                        (if (= wd 0) 7 wd)
                    ))
                  ;; day of week: 1..7 for Saturday..Friday
                  (def dow-sat (fn [date]
                        (+ 1 (weekDayN date))
                    ))
                  (def dow (case weekStartsFrom
                        5 dow-sat
                        6 dow-sun
                        dow-mon
                    ))
              
                  (def first-week-belongs-to-prev-year (fn [dow-1st-jan]
                        (case weekStartsFrom
                                5 (= dow-1st-jan 7)  ;; is Fri
                                6 (or (= dow-1st-jan 6) (= dow-1st-jan 7))  ;; is Fri/Sat
                                (or (= dow-1st-jan 5) (= dow-1st-jan 6) (= dow-1st-jan 7))  ;; is Fri/Sat/Sun
                                 )
                    ))
              
                  ;; day of year:
                  ;;   1..365 or 366
                  ;;   -6..0 when shifts to the previous year
                  ;; weekn: 1-52 or 53
                  (def doy (fn [year weekn]
                        (let [
                            dow-1st-jan (dow (str year "0101"))
                            week (if (first-week-belongs-to-prev-year dow-1st-jan) (+ weekn 1) weekn)
                            days-1st-week (+ 1 (- 7 dow-1st-jan))
                            days-for-weeks (* (- week 2) 7)
                            doy (+ days-1st-week days-for-weeks 1)
                          ]
                          doy
                        )
                    ))
              
                  (def leap? (fn [year]
                        (or (= 0 (mod year 400))
                            (and (= 0 (mod year 4))
                                 (not (= 0 (mod year 100)) ) )
                        )
                    ))
              
                  ;;                Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
                  (def doys-common [0   31  59  90  120 151 181 212 243 273 304 334])
                  (def doys-leap   [0   31  60  91  121 152 182 213 244 274 305 335])
              
                  (def dateFromOrdinal (fn [year doy]
                        (if (< doy 1)
                            [(dec year) 12 (+ 31 doy)]  ;; December of the prev year
                            (let [
                                doys (if (leap? year) doys-leap doys-common)
                                index (->>
                                        doys
                                        (map-indexed vector)
                                        (filter (fn [[i x]] (< x doy) ) )
                                        last
                                        first
                                      )
                                month (inc index)
                                day (- doy (get doys index))
                              ]
                              (if (and (= month 12) (> day 31))
                                [(inc year) 1 (- day 31)]  ;; January of the next year
                                [year month day]
                              )
                            )
                        )
                    ))
              
                  (def weekDates (fn [year weekn]
                        (if (= year 0) [nil nil]
                            (let [
                                doy (doy year weekn)
                                [year1 month1 day1] (dateFromOrdinal year doy)
                                [year2 month2 day2] (dateFromOrdinal year (+ doy 6))
                              ]
                                  [
                                    (clojure.string/join [year1 (zerosToWidth month1 2) (zerosToWidth day1 2)])
                                    (clojure.string/join [year2 (zerosToWidth month2 2) (zerosToWidth day2 2)])
                                  ]
                            )
                        )
                    ))
              
                  (let [ [year weekn] (parseWeek page-name) ]
                        (when (not (= year 0))
                            (let [
                                        [weekStart weekEnd] (weekDates year weekn)
                                        uuid (->
                                                 (call-api "get_page_blocks_tree" page-name)
                                                 js->clj
                                                 first
                                                 (get "uuid")
                                             )
                                     ]
                                            (call-api "upsert_block_property" uuid "week-start" (int weekStart))
                                            (call-api "upsert_block_property" uuid "week-end" (int weekEnd))
                                            (call-api "remove_block" block-uuid)
                               )
                        )
                  )
               )
              }
              #+END_QUERY
        - Overview
          heading:: true
            - INSERT HERE embed ref to #diary query
            - INSERT HERE embed ref to #podcast query
    
  3. Copy reference to both of queries via right clicking on corresponding bullet and selecting «Copy block embed»:

    …and insert them under «Overview» heading. It should look like:
     ...
     - Overview
       heading:: true
         - {{embed ((666cd068-133c-4b30-9df4-1dd8a94179cb))}}
         - {{embed ((666cd1b2-f50d-4d88-859b-04f2f9e2efb2))}}
    
  4. Use this template on every weekly page to setup it.

Notes

  • Week start & end dates based on ISO 8601.
  • Embedding used to achieve the same looks for every week page.
  • Every query has reference property for easy configuration.
  • Value of reference property is a tag. This is intentional: to auto rename the value with renaming whole reference tag.
  • Currently there is no way in Logseq to avoid copying of query body. Please vote for this feature here and like this comment.

Enjoy!

PS And there is…

Option C: via plugin Show Weekday and Week-number