Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 96 additions & 13 deletions helpers/stringTransforms.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,7 @@ export function changeBareLinksToHTMLLink(original: string, addWebIcon: boolean
// clo(captures, `${String(captures.length)} results from bare URL matches:`)
for (const capture of captures) {
const linkURL = capture[3]
const URLForDisplay = (truncateIfNecessary && linkURL.length > 20)
? linkURL.slice(0, 50) + '...'
: linkURL
const URLForDisplay = truncateIfNecessary && linkURL.length > 20 ? linkURL.slice(0, 50) + '...' : linkURL
// logDebug('changeBareLinksToHTMLLink', `${linkURL} / ${URLForDisplay}`)
if (addWebIcon) {
// not displaying icon
Expand Down Expand Up @@ -359,11 +357,11 @@ export function encodeRFC3986URIComponent(input: string): string {
.replace(/\[/g, '%5B')
.replace(/\]/g, '%5D')
.replace(/!/g, '%21')
.replace(/'/g, "%27")
.replace(/'/g, '%27')
.replace(/\(/g, '%28')
.replace(/\)/g, '%29')
.replace(/\*/g, '%2A')
// .replace(/[!'()*]/g, (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`)
// .replace(/[!'()*]/g, (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`)
}

/**
Expand All @@ -374,13 +372,98 @@ export function encodeRFC3986URIComponent(input: string): string {
* @returns {string}
*/
export function decodeRFC3986URIComponent(input: string): string {
const decodedSpecials = input
.replace(/%5B/g, '[')
.replace(/%5D/g, ']')
.replace(/%21/g, '!')
.replace(/%27/g, "'")
.replace(/%28/g, '(')
.replace(/%29/g, ')')
.replace(/%2A/g, '*')
const decodedSpecials = input.replace(/%5B/g, '[').replace(/%5D/g, ']').replace(/%21/g, '!').replace(/%27/g, "'").replace(/%28/g, '(').replace(/%29/g, ')').replace(/%2A/g, '*')
return decodeURIComponent(decodedSpecials)
}

/**
* @method truncateOnWord(length, [from] = 'right', [ellipsis] = '...')
* @returns String
* @short Truncates a string without splitting up words.
* @extra [from] can be `'right'`, `'left'`, or `'middle'`. If the string is
* shorter than `length`, [ellipsis] will not be added. A "word" is
* defined as any sequence of non-whitespace characters.
*
* @example
*
* 'here we go'.truncateOnWord(5) -> 'here...'
* 'here we go'.truncateOnWord(5, 'left') -> '...we go'
*
* @param {number} length
* @param {string} [from] can be `'right'`, `'left'`, or `'middle'`.
* @param {boolean} [fromLeft] - whether to truncate from the left or not
*
* @author Sugar.js https://github.com/andrewplummer/Sugar/blob/b757c66c4e6361af710431117eadcafc5c7d42bc/lib/string.js
**/
function truncateOnWord(str: string, limit: number, fromLeft: boolean = false): string {
if (fromLeft) {
return truncateOnWord(str.split('').reverse().join(''), limit).split('').reverse().join('')
}
// WhiteSpace/LineTerminator as defined in ES5.1 plus Unicode characters in the Space, Separator category.
const TRIM_CHARS = '\u0009\u000A\u000B\u000C\u000D\u0020\u00A0\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u2028\u2029\u3000\uFEFF'
const TRUNC_REG = RegExp(`(?=[${TRIM_CHARS}])`)
const words = str.split(TRUNC_REG)
let count = 0
const result = []

for (const word of words) {
count += word.length
if (count <= limit) {
result.push(word)
} else {
break
}
}

return result.join('')
}

/**
* @method truncateString(length, [from] = 'right', [split] = true)
* @returns String
* @short Truncates a string.
* @extra [from] can be `'right'`, `'left'`, or `'middle'`. If the string is
* shorter than `length`, [ellipsis] will not be added.
*
* @example
*
* 'sittin on the dock'.truncate(10) -> 'sittin on ...'
* 'sittin on the dock'.truncate(10, 'left') -> '...n the dock'
* 'sittin on the dock'.truncate(10, 'middle') -> 'sitti... dock'
*
* @param {number} length
* @param {string} [from] can be `'right'`, `'left'`, or `'middle'`.
* @param {boolean} [split] - whether to split on words or not
*
* @author Sugar.js https://github.com/andrewplummer/Sugar/blob/b757c66c4e6361af710431117eadcafc5c7d42bc/lib/string.js
**/
export function truncateString(str: string, length: number, from: string, splitOnWord: boolean = true): string {
let str1, str2, len1, len2
if (str.length <= length) {
return str.toString()
}
const ellipsisStr = '...'
switch (from) {
case 'left':
str2 = splitOnWord ? truncateOnWord(str, length, true) : str.slice(str.length - length)
return ellipsisStr + str2
case 'middle':
len1 = Math.ceil(length / 2)
len2 = Math.floor(length / 2)
str1 = splitOnWord ? truncateOnWord(str, len1) : str.slice(0, len1)
str2 = splitOnWord ? truncateOnWord(str, len2, true) : str.slice(str.length - len2)
return str1 + ellipsisStr + str2
default:
str1 = splitOnWord ? truncateOnWord(str, length) : str.slice(0, length)
return str1 + ellipsisStr
}
}

/**
* Remove text between () inclusive
* @param {string} str
* @returns {string}
*/
export function removeTextBetweenParentheses(str: string): string {
return str.replace(/\(.*?\)/g, '')
}
11 changes: 6 additions & 5 deletions jgclark.Summaries/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,22 +57,23 @@ All notes in the special folders (@Archive, @Templates and @Trash) are **ignored

Note: **Why use `@run(...)` (mentions) rather than `#run(...)` (hashtags)**? Well, it just felt more right to use `@run(...)` as there are already `@done(...)` and `@repeat(...)` mentions in use in NotePlan that include a value in the brackets. And in NotePlan, hashtags that end with a number ignore the fractional part (e.g. `#run/5.3` ignores the `.3`) but they are not ignored inside `@run(5.3)`. However, you _can_ use a `#hashtag/value` if you don't mind this limitation.

## Tracking checklist completetion
## Tracking checklist completion

To track checklist completion you must create a referance checklist in the template folder:
To track checklist completion you must create a reference checklist in the template folder:

<img alt="Checklist completion" src="checklist_refnote.png" width="360px"/>

![alt text](checklist-1.png)

Add the title of this template to settings:

![alt text](checklist.png)
<img alt="Checklist settings" src="checklist_settings.png" width="360px"/>

If you want to use this template in another note it can be imported using
`<%- import("Daily tasks”) -%>`

Completion is tracked using the 'appendProgressUpdate' command

![alt text](checklist-2.png)
<img alt="Checklist count" src="checklist_count.png" width="360px"/>

## 'heatmap for complete tasks' command
This displays a 'heatmap' chart of many tasks you've completed on each day (see example above). It uses the `@done(...)` dates in all daily, weekly and project notes over the number of weeks you specify to look back (via the 'Chart Duration (in weeks)' setting). If you set this to 0, the plugin will generate a sensible longish period between 6 and 12 months. It also counts completed tasks without `@done(...)` dates on Calendar notes, and assumes the tasks were completed on the day or start of week in question.
Expand Down
4 changes: 2 additions & 2 deletions jgclark.Summaries/plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
"plugin.author": "Jonathan Clark",
"plugin.url": "https://github.com/NotePlan/plugins/tree/main/jgclark.Summaries/",
"plugin.changelog": "https://github.com/NotePlan/plugins/blob/main/jgclark.Summaries/CHANGELOG.md",
"plugin.version": "0.22.0",
"plugin.lastUpdateInfo": "0.22.0: Add support for checklist progress.\n0.21.0: Add Mermaid charting command. Fix to simple weekly CSV generation.\n0.20.3: Bug fix for progressUpdate() in templates.\n0.20.2: Added x-callback options for /periodStats.\n0.20.1: fix refresh after '/append progress update' command. Logging change.\n0.20.0: add new '/today progress' and '/heatmap for tag' commands, and add refresh button to /periodStats output.\n0.19.3: bug fixes on 'weekly stats generation' commands.\n0.19.2: change date library.\n0.19.1: bug fix\n. 0.19.0: adds totals and averages for hashtags as well. Improve output of averages.",
"plugin.version": "0.22.1",
"plugin.lastUpdateInfo": "0.22.1 Fix formatting with long lines.\n0.22.0: Add support for checklist progress.\n0.21.0: Add Mermaid charting command. Fix to simple weekly CSV generation.\n0.20.3: Bug fix for progressUpdate() in templates.\n0.20.2: Added x-callback options for /periodStats.\n0.20.1: fix refresh after '/append progress update' command. Logging change.\n0.20.0: add new '/today progress' and '/heatmap for tag' commands, and add refresh button to /periodStats output.\n0.19.3: bug fixes on 'weekly stats generation' commands.\n0.19.2: change date library.\n0.19.1: bug fix\n. 0.19.0: adds totals and averages for hashtags as well. Improve output of averages.",
"plugin.dependencies": [],
"plugin.script": "script.js",
"plugin.isRemote": "false",
Expand Down
22 changes: 20 additions & 2 deletions jgclark.Summaries/src/summaryHelpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ import {
isHashtagWanted,
isMentionWanted,
} from '@helpers/search'
import {
removeTextBetweenParentheses,
truncateString,
} from '@helpers/stringTransforms'
import NPTemplating from 'NPTemplating'

//------------------------------------------------------------------------------
// Get settings
Expand Down Expand Up @@ -719,11 +724,24 @@ export async function generateProgressUpdate(occObjs: Array<TMOccurrences>, peri
const daysBetween = toDateMom.diff(fromDateMom, 'days')
// Include sparklines only if this period is a month or less
const showSparklines = (requestToShowSparklines && daysBetween <= 31)

await occObjs.forEach(async (m) => {
if (m.term.includes('<%') && m.term.includes('%>')) {
m.term = await NPTemplating.render(m.term)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is returning late

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what you're trying to achieve here when you say simply "render templates".
Nor do I know the Templating code, and as it's not well documented, it would take me a very long time to figure out. So I can't help. Do you want to back out this part of the PR for now?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks pretty cool to me. You could include template code in the title. Sounds like it could be useful. @aaronpoweruser What's your use case? And you tested it I assume.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aaronpoweruser Can you pls provide some test examples that show how your new code is used, especially the new template parts? I don't see any mention of the template strings in the README. I may have missed it.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I can update the documentation, this was a for me feature. As I dynamically generate a link to my weekly note.

logError('generateProgressUpdate', `rendered term: ${m.term}`)
}
if (showSparklines) {
m.term = truncateString(removeTextBetweenParentheses(m.term), 60, 'middle')
}
})

logError('generateProgressUpdate', `this should be after the await`)

// Get length of longest progress term (to use with sparklines)
const maxTermLen = Math.max(...occObjs.map((m) => m.term.length))

let outputArray: Array<string> = []
for (let occObj of occObjs) {
const outputArray: Array<string> = []
for (const occObj of occObjs) {
// occObj.logValuesMap()
let thisOutput = ''
switch (style) {
Expand Down