Skip to content

Commit

Permalink
EventHelpers v0.22
Browse files Browse the repository at this point in the history
  • Loading branch information
jgclark committed Sep 6, 2024
1 parent ca9cd48 commit 98a0b53
Show file tree
Hide file tree
Showing 10 changed files with 139 additions and 95 deletions.
4 changes: 2 additions & 2 deletions helpers/NPdateTime.js
Original file line number Diff line number Diff line change
Expand Up @@ -734,7 +734,7 @@ export function getNPWeekData(dateIn: string | Date = new Date(), offsetIncremen

// This might be run from React side, where Calendar.* is not available.
// If this happens, then instead offer the ISO week number.
if (typeof Calendar !== 'function') {
if (!Calendar || typeof Calendar !== 'object') {
logInfo('NPdateTime::getNPWeekData', `NP's Calendar API functions are not available, so I will use moment instead. This doesn't know what your chosen first day of week is.`)
const weekNumber = newMom.week() // uses moment locale
const startDate = newMom.startOf('week').toDate()
Expand Down Expand Up @@ -952,7 +952,7 @@ export function getRelativeDates(): Array<Object> {
const relativeDates = []
const todayMom = moment()

if (typeof DataStore !== 'function') {
if (!DataStore || typeof DataStore !== 'object') {
logDebug('NPdateTime::getRelativeDates', `NP DataStore functions are not available, so returning an empty set.`)
return [{}]
}
Expand Down
2 changes: 1 addition & 1 deletion helpers/dateTime.js
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ export const isYearlyNote = (note: CoreNoteFields): boolean => new RegExp(RE_YEA
* Note: also see getNoteType in note.js to get the type of a note in a more conversational way (e.g. -> 'Monthly')
* @author @jgclark
* @param {TNote} note - the note to look at
* @returns false | 'Daily' | 'Weekly' | 'Monthly' | 'Quarterly' | 'Yearly' | 'Project'
* @returns false | 'day' | 'week' | 'month' | 'quarter' | 'year'
*/
export function getCalendarNoteTimeframe(note: TNote): false | 'day' | 'week' | 'month' | 'quarter' | 'year' {
if (note.type === 'Calendar') {
Expand Down
4 changes: 4 additions & 0 deletions jgclark.EventHelpers/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

See [website README for more details](https://github.com/NotePlan/plugins/tree/main/jgclark.EventHelpers), and how to configure.

## [0.22.0] - 2024-09-06 @jgclark
- can now use `events()` and `matchingEvents()` calls from Templates running on Weekly notes and other non-daily Calendar notes (for @gdrn).
- refactored documentation.

## [0.21.3] - 2024-06-04 @jgclark
- fix bug when adding time blocks to calendar
- better handle template `<%- events() %>`, which has no formatter string
Expand Down
73 changes: 38 additions & 35 deletions jgclark.EventHelpers/README.md

Large diffs are not rendered by default.

12 changes: 8 additions & 4 deletions jgclark.EventHelpers/__tests__/eventsToNotes.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ describe('eventsToNotes.js tests', () => {
})

describe('smartStringReplace()', () => {
// This function's tests use $Shape<...> to use a subset of large objects without causing errors
const config: $Shape<EventsConfig> = {
// This function's tests use Partial<...> to use a subset of large objects without causing errors
const config: Partial<EventsConfig> = {
calendarNameMappings: [],
locale: 'se-SE',
timeOptions: '',
Expand All @@ -80,7 +80,8 @@ describe('eventsToNotes.js tests', () => {
const endDT = new Date(2021, 0, 23, 22, 0, 0)
const attendeesArray: Array<string> = [' Jonathan Clark', '? James Bond', 'x Martha', '? [email protected]']
const attendeeNamesArray: Array<string> = ['Jonathan Clark', 'Martha Clark', '[email protected]']
const event1: $Shape<TCalendarItem> = {
// $FlowFixMe[prop-missing] the Type has functions
const event1: Partial<TCalendarItem> = {
calendar: 'Jonathan',
title: 'title of event1',
url: 'https://example.com/easy',
Expand All @@ -90,7 +91,8 @@ describe('eventsToNotes.js tests', () => {
attendees: attendeesArray,
attendeeNames: attendeeNamesArray,
} // simple case
const event2: $Shape<TCalendarItem> = {
// $FlowFixMe[prop-missing] the Type has functions
const event2: Partial<TCalendarItem> = {
calendar: 'Us',
title: 'title of event2 with <brackets> & more',
url: 'https://example.com/bothersomeURL/example',
Expand All @@ -100,7 +102,9 @@ describe('eventsToNotes.js tests', () => {
attendees: attendeesArray,
attendeeNames: attendeeNamesArray,
} // case with inclusion
// $FlowIgnore[incompatible-call] only sending through what we need
const replacements1 = e.getReplacements(event1, config)
// $FlowIgnore[incompatible-call] only sending through what we need
const replacements2 = e.getReplacements(event2, config)

test('event 1 format 1 easy', () => {
Expand Down
4 changes: 2 additions & 2 deletions jgclark.EventHelpers/plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
"plugin.author": "jgclark",
"plugin.url": "https://github.com/NotePlan/plugins/tree/main/jgclark.EventHelpers",
"plugin.changelog": "https://github.com/NotePlan/plugins/tree/main/jgclark.EventHelpers/CHANGELOG.md",
"plugin.version": "0.21.3",
"plugin.lastUpdateInfo": "0.21.3: bug fix adding time blocks to calendar. 0.21.2: /shiftDates now covers more cases.\n0.21.1: add 'Yes to all' to option to create time blocks. Extend '/shift dates' to work on week dates.\n0.21.0: improvements to '/shift dates' and '/process date offsets'.\n0.20.3: new 'STOPMATCHING' placeholder for '/insert matching events'.\n0.20.2: upper-case letters allowed in offsets.\n0.20.1: adds support for time blocks in Checklists (available from NP 3.8). 0.20.0: can now insert a 'MEETINGNOTE' link next to each item. Please read important note in the README file on how to enable it.\n0.19.4: can now include 'calendarSet' and 'calendarNameMappings' parameters when using event lists from Templates.\n0.19.3: de-dupes lists of attendees in event lists.",
"plugin.version": "0.22.0",
"plugin.lastUpdateInfo": "0.22.0: can now use events() template calls in Weekly notes.\n0.21.3: bug fix adding time blocks to calendar.\n0.21.2: /shiftDates now covers more cases.\n0.21.1: add 'Yes to all' to option to create time blocks. Extend '/shift dates' to work on week dates.\n0.21.0: improvements to '/shift dates' and '/process date offsets'.",
"plugin.dependencies": [],
"plugin.script": "script.js",
"plugin.isRemote": "false",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,28 +1,20 @@
// @flow
// ----------------------------------------------------------------------------
// Sort configuration for commands in the Event Helpers plugin.
// Last updated 29.9.2023 for v0.21.0, by @jgclark
// Last updated 2024-09-05 for v0.22.0, by @jgclark
// @jgclark
// ----------------------------------------------------------------------------

import pluginJson from "../plugin.json"
import {
castBooleanFromMixed,
castHeadingLevelFromMixed,
castNumberFromMixed,
castStringArrayFromMixed,
castStringFromMixed,
trimAnyQuotes,
} from '@helpers/dataManipulation'
import { type HourMinObj } from '@helpers/dateTime'
import { castStringFromMixed } from '@helpers/dataManipulation'
import { clo, log, logDebug, logWarn, logError } from "@helpers/dev"
import { type EventsConfig } from '@helpers/NPCalendar'
import { showMessage } from '@helpers/userInput'

//------------------------------------------------------------------------------
// Get settings

const configKey = 'events'
// const configKey = 'events'

/**
* Get config settings using Config V2 system. (Have now removed support for Config V1.)
Expand Down Expand Up @@ -90,7 +82,7 @@ function getLocale(tempConfig: Object): string {
// or if not available default
function getTimeOptions(tempConfig: Object): Object {
const env1224 = NotePlan?.environment ? NotePlan?.environment?.is12hFormat : false
let tempTimeOptions = tempConfig?.timeOptions ?? { hour: '2-digit', minute: '2-digit', hour12: env1224 }
const tempTimeOptions = tempConfig?.timeOptions ?? { hour: '2-digit', minute: '2-digit', hour12: env1224 }
clo(tempTimeOptions, `tempTimeOptions: `)
return tempTimeOptions
}
100 changes: 70 additions & 30 deletions jgclark.EventHelpers/src/eventsToNotes.js
Original file line number Diff line number Diff line change
@@ -1,29 +1,31 @@
// @flow
// ----------------------------------------------------------------------------
// Command to bring calendar events into notes
// Last updated 4.6.2024 for v0.21.3 by @jgclark
// Last updated 2024-09-06 for v0.22.0, by @jgclark
// @jgclark, with additions by @dwertheimer, @weyert, @m1well, @akrabat
// ----------------------------------------------------------------------------

import pluginJson from '../plugin.json'
import { getEventsSettings } from './config'
import { getEventsSettings } from './eventsHelpers'
import { getEventsForDay, type EventsConfig } from '@helpers/NPCalendar'
import {
calcOffsetDateStr,
getDateStringFromCalendarFilename,
getCalendarNoteTimeframe,
// getDateStringFromCalendarFilename,
getDateFromUnhyphenatedDateString,
getISODateStringFromYYYYMMDD,
getDateStrForStartofPeriodFromCalendarFilename,
// getISODateStringFromYYYYMMDD,
toLocaleDateString,
toLocaleTime,
unhyphenateString,
// unhyphenateString,
} from '@helpers/dateTime'
import { clo, logDebug, logError, logWarn } from '@helpers/dev'
import { getTagParamsFromString } from '@helpers/general'
import { toNPLocaleDateString } from '@helpers/NPdateTime'
import { showMessage } from '@helpers/userInput'

/**
* Return MD list of the current open Calendar note's events
* Return markdown list of the current open Calendar note's events (and potentially the days after it)
* @author @jgclark
*
* @param {string} paramString - checked for options
Expand All @@ -35,6 +37,7 @@ export async function listDaysEvents(paramStringIn: string = ''): Promise<string
await showMessage(`Please run again with a calendar note open.`, 'OK', 'List Events')
return ''
}
const openNote = Editor.note
// handle getting no parameters passed at all
let paramString = ''
if (paramStringIn == null) {
Expand All @@ -43,10 +46,11 @@ export async function listDaysEvents(paramStringIn: string = ''): Promise<string
paramString = paramStringIn
}

const baseDateStr = getDateStringFromCalendarFilename(Editor.filename)
logDebug(pluginJson, `listDaysEvents: starting for date ${baseDateStr} with paramString='${paramString}'`)
// Get config settings
const config = await getEventsSettings()
const noteTimeFrame = getCalendarNoteTimeframe(openNote)
if (!noteTimeFrame) throw new Error(`No noteTimeFrame found for note ${openNote.filename}. Stopping.`)
const startDayDateString = getDateStrForStartofPeriodFromCalendarFilename(Editor.filename)
logDebug(pluginJson, `listDaysEvents: starting for noteTimeFrame=${noteTimeFrame} / date ${startDayDateString} with paramString='${paramString}'`)

// Get a couple of other suppplied parameters, or use defaults
// Work out format for output line (from params, or if blank, a default)
Expand Down Expand Up @@ -77,14 +81,22 @@ export async function listDaysEvents(paramStringIn: string = ''): Promise<string
// For each day to cover
for (let i = 0; i < daysToCover; i++) {
// Set dateStr to the day in question (YYYYMMDD)
const isoBaseDateStr = getISODateStringFromYYYYMMDD(baseDateStr)
const dateStr = unhyphenateString(calcOffsetDateStr(isoBaseDateStr, `+${i}d`))

// const isoStartDayDateStr = getISODateStringFromYYYYMMDD(startDayDateString)
// const dateStr = unhyphenateString(calcOffsetDateStr(isoStartDayDateStr, `+${i}d`))
const dateStr = calcOffsetDateStr(startDayDateString, `+${i}d`)
logDebug('listDaysEvents', `${i}: startDayDateString=${startDayDateString}, dateStr=${dateStr}`)

// Add heading if wanted, or if doing more than 1 day
if (daysToCover > 1) {
// $FlowIgnore[incompatible-call]
const localisedDateStr = toNPLocaleDateString(getDateFromUnhyphenatedDateString(dateStr))
outputArray.push(config.eventsHeading !== '' ? `${config.eventsHeading} for ${localisedDateStr}` : `### for ${localisedDateStr}`)
const npDateStr = getDateFromUnhyphenatedDateString(dateStr)
if (!npDateStr) {
throw new Error(`Could not get valid NP date string from ${dateStr}`)
}
const localisedDateStr = toNPLocaleDateString(npDateStr)
// figure out H level to set: calc from config.eventsHeading or default to 2
const hLevel = config.eventsHeading !== '' ? config.eventsHeading.split(' ')[0].length : 2
outputArray.push(config.eventsHeading !== '' ? `${config.eventsHeading} for ${localisedDateStr}` : `${'#'.repeat(hLevel)} for ${localisedDateStr}`)
} else {
if (config.eventsHeading !== '' && includeHeadings) {
outputArray.push(config.eventsHeading)
Expand All @@ -93,14 +105,14 @@ export async function listDaysEvents(paramStringIn: string = ''): Promise<string

// Get all the events for this day, for the given calendarSet
const eArr: Array<TCalendarItem> = await getEventsForDay(dateStr, calendarSet) ?? []
logDebug('listDaysEvents', `- ${eArr.length} events found on ${dateStr} from ${calendarSet.length} calendars ${String(calendarSet)}`)
// logDebug('listDaysEvents', `- ${eArr.length} events found on ${dateStr} from ${calendarSet.length} calendars ${String(calendarSet)}`)
const mapForSorting: { cal: string, start: Date, text: string }[] = []

// Process each event
for (const e of eArr) {
logDebug('listDaysEvents', `- Processing event '${e.title}' (${typeof e})`)
// logDebug('listDaysEvents', `- Processing event '${e.title}' (${typeof e})`)
if (!includeAllDayEvents && e.isAllDay) {
logDebug('listDaysEvents', ` - skipping as event is all day and includeAllDayEvents is false`)
// logDebug('listDaysEvents', ` - skipping as event is all day and includeAllDayEvents is false`)
continue
}

Expand Down Expand Up @@ -163,26 +175,44 @@ export async function insertDaysEvents(paramString: ?string): Promise<void> {

/**
* Return string of matching events in the current day's note, from list in keys of config.addMatchingEvents, having applied placeholder formatting.
* Note: Parameters can be passed in as a JSON string, except for the complex 'format' which only comes from 'config.addMatchingEvents'.
* @author @jgclark
* @param {?string} paramString Paramaters to use (for future expansion)
* @param {?string} paramStringIn Paramaters to use
* @return {string} List of matching events, as a multi-line string
*/
export async function listMatchingDaysEvents(
paramString: string = '', // NB: the parameter isn't currently used, but is provided for future expansion.
paramStringIn: string = '', // NB: the parameter isn't currently used, but is provided for future expansion.
): Promise<string> {
try {
// $FlowIgnore[incompatible-call] - called by a function that checks Editor is valid.
const baseDateStr = getDateStringFromCalendarFilename(Editor.filename)
logDebug(pluginJson, `listMatchingDaysEvents: starting for date ${baseDateStr} with paramString=${paramString}`)
if (Editor.note == null || Editor.filename == null || Editor.type !== 'Calendar') {
await showMessage(`Please run again with a calendar note open.`, 'OK', 'List Events')
return ''
}
const openNote = Editor.note

// handle getting no parameters passed at all
let paramString = ''
if (paramStringIn == null) {
logWarn('listMatchingDaysEvents', `No parameters passed (from template), so will use defaults.`)
} else {
paramString = paramStringIn
}

// Get config settings
const config = await getEventsSettings()
// const baseDateStr = getDateStringFromCalendarFilename(Editor.filename)
// logDebug(pluginJson, `listMatchingDaysEvents: starting for date ${baseDateStr} with paramString=${paramString}`)
const noteTimeFrame = getCalendarNoteTimeframe(openNote)
if (!noteTimeFrame) throw new Error(`No noteTimeFrame found for note ${openNote.filename}. Stopping.`)
const startDayDateString = getDateStrForStartofPeriodFromCalendarFilename(Editor.filename)
logDebug(pluginJson, `listMatchingDaysEvents: starting for noteTimeFrame=${noteTimeFrame} / date ${startDayDateString} with paramString='${paramString}'`)

if (config.addMatchingEvents == null) {
await showMessage(`Error: Empty 'addMatchingEvents' setting in Config. Stopping`, 'OK', 'List Matching Events')
return `**Error: found no 'Add matching events' in plugin settings.**`
}
const textToMatchArr = Object.keys(config.addMatchingEvents)

const formatArr = Object.values(config.addMatchingEvents)
const textToMatchArr = Object.keys(config.addMatchingEvents)
logDebug('listMatchingDaysEvents', `- from settings found ${textToMatchArr.length} match strings to look for`)

// Get a couple of other supplied parameters, or use defaults
Expand All @@ -199,13 +229,20 @@ export async function listMatchingDaysEvents(
// For each day to cover
for (let i = 0; i < daysToCover; i++) {
// Set dateStr to the day in question (YYYYMMDD)
const dateStr = unhyphenateString(calcOffsetDateStr(getISODateStringFromYYYYMMDD(baseDateStr), `+${i}d`))
// const dateStr = unhyphenateString(calcOffsetDateStr(getISODateStringFromYYYYMMDD(baseDateStr), `+${i}d`))
const dateStr = calcOffsetDateStr(startDayDateString, `+${i}d`)
logDebug('listDaysEvents', `${i}: startDayDateString=${startDayDateString}, dateStr=${dateStr}`)

// Add heading if wanted, or if doing more than 1 day
if (daysToCover > 1) {
// $FlowIgnore[incompatible-call]
const localisedDateStr = toNPLocaleDateString(getDateFromUnhyphenatedDateString(dateStr))
outputArray.push(config.matchingEventsHeading !== '' ? `${config.matchingEventsHeading} for ${localisedDateStr}` : `### for ${localisedDateStr}`)
const npDateStr = getDateFromUnhyphenatedDateString(dateStr)
if (!npDateStr) {
throw new Error(`Could not get valid NP date string from ${dateStr}`)
}
const localisedDateStr = toNPLocaleDateString(npDateStr)
// figure out H level to set: calc from config.eventsHeading or default to 2
const hLevel = config.matchingEventsHeading !== '' ? config.matchingEventsHeading.split(' ')[0].length : 2
outputArray.push(config.matchingEventsHeading !== '' ? `${config.matchingEventsHeading} for ${localisedDateStr}` : `${'#'.repeat(hLevel)} for ${localisedDateStr}`)
} else {
if (config.matchingEventsHeading !== '' && includeHeadings) {
outputArray.push(config.matchingEventsHeading)
Expand All @@ -216,12 +253,15 @@ export async function listMatchingDaysEvents(
const eArr: Array<TCalendarItem> = await getEventsForDay(dateStr, calendarSet) ?? []
const mapForSorting: { cal: string, start: Date, text: string }[] = []

// for each event, check each of the strings we want to match
// Process each event
for (const e of eArr) {
logDebug('listMatchingDaysEvents', `- Processing event '${e.title}' (${typeof e})`)
if (!includeAllDayEvents && e.isAllDay) {
logDebug('listMatchingDaysEvents', ` - skipping as event is all day and includeAllDayEvents is false`)
continue
}

// for each event, check each of the strings we want to match
for (let j = 0; j < textToMatchArr.length; j++) {
const thisFormat: string = String(formatArr[j])
const withCalendarName = thisFormat.includes('CAL')
Expand Down Expand Up @@ -249,7 +289,7 @@ export async function listMatchingDaysEvents(
}
}

// If there are matching events
// Sort the events (if there are matching events)
if (mapForSorting.length > 0) {
// Sort the matched events
if (config.sortOrder === 'calendar') {
Expand Down
Loading

0 comments on commit 98a0b53

Please sign in to comment.