icebergh Home / Archive /


┌┤ icebergh:pts/801.35 ├┤ ~/blog 
└┤ 05/10 13:51  1      $ mdcat "Tips and Tricks with 'Obsidian'.md"

Many people have done many interesting things whith making their Obsidian environment have less friction for how they take their notes. I've been using the tool more and more for both my use as a repository for note from my time playing and running role playing games. And many of those neat features I built for that purpose have made my professional use for taking notes during customer engagements smoother as well.

One of my biggest usecases is utilizing the dataview add on to display sessions or calls in a list with some key details. I'm going to include some of that code below. First is a table that lists all the sessions that haven't been archived (previous contracts). I have a similar table on the customer page directly after it, listing all the archived sessions as well, but I'll leave that as an exercise for the reader.


// ```dataviewjs```
const curCustomer = dv.current().customer;
const meetings = dv.pages('#meeting')
	.where(p => p.type == "meeting" && p.customer == curCustomer && !p.archived)
	.sort(p => p.date,"desc");

const hoursAvail = dv.current().hoursAvailable
const hoursUsed = meetings.hours.array().reduce( (hU,h) => hU+parseFloat(h), 0)
const hoursLeft = (hoursAvail - hoursUsed).toFixed(2)
dv.paragraph(`> [!info] Hours\n> <table><tr><td>Contract Hours:</td><th>${hoursAvail}</th></tr><tr><td>Services Hours worked</td><th>${hoursUsed}</th></tr><tr><td>Contract Hours remaining:</td><th>${hoursLeft}</th></tr></table>`)

dv.table(
	["Meeting","Hours","Tags","Summary"],
	meetings.map( m => [

		m.file.link,
		m.hours,
		m.tags.filter(t => t!=='meeting').map( t => `#${t}`).join(' '),
		m.shortSummary])
	);

I also have a button, built with a combo of plugins including Templater, Meta Bind (for buttons), and JS Engine (for the InlineJS button). This one creates a new note page for a customer, but I've used similar for creating a new RPG campaign. It assumes that you have a template defined in your templates directory called 'work-customer' that Templater will use to generate the initial page content.


#```meta-bind-button
style: primary
label: Create Customer
hidden: false
actions:

    - type: inlineJS
      code: >
        const tp = app.plugins.plugins['templater-obsidian'].templater.current_functions_object;
        const template = tp.file.find_tfile('work-customer');
        tp.system.prompt('Customer Name').then((customer) => {
          if (customer !== null)
            tp.file.create_new(template,customer,true,`customers/${customer}`)
        })

I have a similar pair of buttons for creating a new session (whether RPG or customer). Here's the customer one:


#```meta-bind-button
style: primary
label: Start Meeting
hidden: false
actions:
    - type: inlineJS

      code: >
        const tp = app.plugins.plugins['templater-obsidian'].templater.current_functions_object;
        const template = tp.file.find_tfile('work-session');
        const path = `customers/${tp.file.title}/meetings`;
        tp.file.create_new(template,tp.date.now(),true,path);

Oh, one last thing I should share. It's another dataview (pair of) tables listing all of my customers (or campaigns). I put this on my appropriate MOC pages. But figured it would be handy to give the overall view, and someplace for you all to start from.


// ```dataviewjs```
const customers = dv.pages('#customer')

	.where(c => c.employer === dv.current().employer)
	.where(c => !c.file.path.includes("template"))
const meetings  = dv.pages('#meeting').where(p=>!p.archived)

function renderTable(title,customers) {
	dv.header(2,title)
	dv.table(
		["Customer","Start","Hours Left", "Meetings", "Tags", "Last Summary"],

		customers.sort(c => c.customer)
			.map( c => { 
				const cMeetings = meetings
					.where(m => m.customer === c.customer).sort(m => m.date)
				const cTags = cMeetings.array()
					.flatMap( m => m.tags)
					.reduce((a,t) => { t!=='meeting' && !a.includes(t) && a.push(t); return a}, [])
					.sort()
					.map(t => `#${t}`)
					.join(' ')
				return [
					c.file.link,
					c.date,
					c.hoursAvailable - cMeetings
						.hours.array()

						.reduce( (hu,h) => hu+parseFloat(h),0),
					cMeetings.length,
					cTags,
					cMeetings.length>0?cMeetings.last().shortSummary||"unspecified" : "none"

				
				]
			})
	);

}

renderTable("Active Customers", customers.where(c => c.active))

renderTable("Inactive Customers", customers.where(c => !c.active))

That's all for now, I'll add/update this page in the future if I find a good reason to.


EOF% _

©2024 Orien Vandenbergh
All words, opinions and representations made herein upon this website are not, in any way, affiliated with my current employer, and are indeed merely a representation of whatever the hell is rattling around in my brain pan.