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

Hey there!
As a mobile user i found your work very valuable.
I don’t know if it is user error, API change or whatever, but i cannot seem to get it to work correctly.
The template query (to my understanding) sets the week-start and week-end attribute per calendar. However, no matter how i setup the template, no attributes get created. If i manually add the attributes to the page, then run the template, they get set correctly- but if i add the attributes with the template they stay empty. (If i don’t add the attributes in the template they don’t get added at all)
The query embed then also doesn’t show anything, this can either be a separate problem or just the attributes not working as intended.
Here are my queries and templates: pastebin [dot] com/nJEUAtq7

Also i found the calendar you used pretty cool, but cannot find it? The “days” plugin only allows /today /tomorrow and such?

Hello! I didn’t test it on mobile. It should work. But if not — I don’t know the reason. Did you try it on desktop?

Unfortunately, it was removed from Marketplace. You can try fork.

1 Like

The “days” plugin failed to load, could you please help me take a look at this issue?
error log: This plugin #logseq-plugin-days takes too long to load, affecting the application startup time and potentially causing other plugins to fail to load.

It is not longer available :man_shrugging:
You can use the fork.

Thank you very much for your reply. I downloaded the file using your link and manually installed the plugin, but it cannot be loaded and an error message is displayed.

Well, to manually install the plugin you need to build it from the source code.
It requires some tech skills, unfortunately. I guess, the steps is look like the following. But you’ll need to figure it out on your own:

  1. Install npm tool
  2. Download the source code
  3. Run npm install in command line
  4. Run npm run build to build the plugin
  5. Manually add the plugin from dist folder to Logseq
1 Like

I was able to achieve success by adhering to the instructions you shared. I truly appreciate your help! :smile:

1 Like