Is it possible to transclude code blocks somehow?

So, over here I have a question about fixing my book database, which I’ve mostly solved.

But now I’ve come to the point where the code is a little too inflexible, since I have to add a new Medium to an ever growing list of Media to each single instance of that code.

Example:

let pagesum = 0;
   for(let i = 0; i < dv.pages('"Book Log/2022"').where(p => p.Medium == "Book" || p.Medium == "eBook").length; i++) {
     if(dv.pages('"Book Log/2022"').where(p => p.Medium == "Book" || p.Medium == "eBook")[i].Length) {
       pagesum += dv.pages('"Book Log/2022"').where(p => p.Medium == "Book" || p.Medium == "eBook")[i].Length;
         }
   }

I just read a graphic novel, and would like to add that as a medium. This way, I need to go through each time I have all the media listed, and add a p.Medium == "GraphicNovel", since I have different Dataview “notes” for the different years I’ve been keeping track of my reading.

I’ve been playing around with transcluding parts of this codeblock, and while the code is shown as transcluded, dataview can’t handle it.

Ideally, that way I would have one note where all the media are listed that way, and then when I add a new one, I only have to change it there and the code would be applied to the other instances of that part of the code being used as well.

Is this possible somehow?

Thanks!

Might I ask why you hardcode your medium at all? Wouldn’t it be better to do your summation for whatever medium is entered in your notes?

Or do you need to sum up various medium into the same category? In that case, maybe you rather should subdivide your markup to have a main category, and a subcategory. That way your summation stuff could use the main category, but you could display/use the subcategory for other purposes.

It’s possible to have javascript include definitions from other parts through includes and imports, but I’m not sure that would be the best solution to your use case. But then again, I’m not entirely sure what your use case (or even if you know what your use case is (Sorry…)).

I’ll try explaining my use case a bit:
This is a book database, with each note representing one book, audiobook, podcast, eBook, graphic novel, etc.

And then I have a Gallery note, that shows all the books I’ve read. The Gallery note includes several dv.paragraph lines that say things like dv.paragraph("# This year, I've read " + BookCount + " books and I've listened to " + AudioCount + " audiobooks (of which " + dv.pages('"Book Log/2022"').where(p => p.Medium == "Podcast").length + " were podcasts), for an overall " + (BookCount + AudioCount) + " books and audiobooks read.")

So far no problem. But if I want to do this: dv.paragraph("# I've read " + pagesum + " pages this year. And I've listened to " + (hoursum.toFixed(1)) + " hours.") I need to have a function for pagesum that somehow differentiates between the mediums with pages (i.e. books, ebooks, graphic novels, etc.) and those with hours (i.e. audiobooks and podcasts).

So far this would have been solved because the two categories had different length indicators, i.e. the ones with pages had the variable Pages: and the ones with hours had Hours:. Now I streamlined it so it all works with Length (which is a little confusing since length with a lower case l is a built in function, I’m seeing, which is bad on my part).

This works. However, I just started reading graphic novels again, and would like to create a new medium type for that. And here comes my question, because the only solution for this is to go through all my Gallery notes, there are several (one for each year, plus some others), and add something like .where(p => p.Medium == "GraphicNovel") for it.

My idea for transcluding would have made it easier since I’d only have to add the .where(p => p.Medium == "Podcast") in one note and it would be transferred to the other instances.

I’m not sure if I explained it better this time, or just reiterated the same thing over again. Let me know if this helps.

I’m open to suggestions on how to make it better :slight_smile:

This is the whole codeblock:

```dataviewjs
let BookCount = (dv.pages('"Book Log/2022"').where(p => p.Medium == "Book" || p.Medium == "eBook").length);

let AudioCount = (dv.pages('"Book Log/2022"').where(p => p.Medium == "Audiobook" || p.Medium == "Podcast").length);

let pagesum = 0;
   for(let i = 0; i < dv.pages('"Book Log/2022"').where(p => p.Medium == "Book" || p.Medium == "eBook").length; i++) {
     if(dv.pages('"Book Log/2022"').where(p => p.Medium == "Book" || p.Medium == "eBook")[i].Length) {
       pagesum += dv.pages('"Book Log/2022"').where(p => p.Medium == "Book" || p.Medium == "eBook")[i].Length;
         }
   }

let hoursum = 0;
	for(let i = 0; i < dv.pages('"Book Log/2022"').where(p => p.Medium == "Audiobook" || p.Medium == "Podcast").length;i++) {
     if(dv.pages('"Book Log/2022"').where(p => p.Medium == "Audiobook" || p.Medium == "Podcast")[i].Length) {
       hoursum += dv.pages('"Book Log/2022"').where(p => p.Medium == "Audiobook" || p.Medium == "Podcast")[i].Length;
         }
   }

let Grade = 0;
	for(let i = 0; i < dv.pages('"Book Log/2022"').length;i++) {
     if(dv.pages('"Book Log/2022"')[i].Rating) {
       Grade += dv.pages('"Book Log/2022"')[i].Rating;
         }
   }
let AvgRating = (Grade / (dv.pages('"Book Log/2022"').where(p => p.Medium == "Book" || p.Medium == "eBook" || p.Medium == "Audiobook" || p.Medium == "Podcast").length))

let today = DateTime.now().toFormat("ooo");

dv.paragraph("# This year, I've read " + BookCount + " books and I've listened to " + AudioCount + " audiobooks (of which " + dv.pages('"Book Log/2022"').where(p => p.Medium == "Podcast").length + " were podcasts), for an overall " + (BookCount + AudioCount) + " books and audiobooks read.")

dv.paragraph("# I've read " + pagesum + " pages this year. And I've listened to " + (hoursum.toFixed(1)) + " hours.")

dv.paragraph("# The average rating for the overall " + (BookCount + AudioCount) + " books and audiobooks is " + AvgRating.toFixed(1) + " out of 10.")

dv.paragraph("# Average book length: " + (pagesum / BookCount).toFixed() + " pages")

dv.paragraph("# Average audiobook length: " + (hoursum / AudioCount).toFixed(1) + " hours")

dv.paragraph("# Average pages read per day: " + (pagesum / today).toFixed(1) + " pages")

Any ideas and advice appreciated,
Thanks!

@Craig I saw your helpful comments over here: Can I import dataviewjs from other file? - #5 by Craig

I feel like I’m almost getting it, but not quite.
I think I just don’t understand the documentation well enough.

I have a file called scriptbits.js in a folder (in my vault) called Script, so I’ve tried putting in await dv.view("Script/scriptbits") in my attempted code.

This is what my code looks like:

let BookCount = (dv.pages('"Book Log/2022"').where(p => p.Medium == "Book" || p.Medium == "eBook").length);

await dv.view ("Script/scriptbits/")



dv.paragraph("# I've read  " + (pagesum) + " pages.")

And this is what’s currently in scriptbits.js:

let pagesum = 0;
   for(let i = 0; i < dv.pages('"Book Log/2022"').where(p => p.Medium == "Book" || p.Medium == "eBook").length; i++) {
     if(dv.pages('"Book Log/2022"').where(p => p.Medium == "Book" || p.Medium == "eBook")[i].Length) {
       pagesum += dv.pages('"Book Log/2022"').where(p => p.Medium == "Book" || p.Medium == "eBook")[i].Length;
         }
   }

But then I still get the error pagesum not defined. What am I missing?

Thanks! (@holroy )

I’m not sure if you’re being sarcastic here, but variables declared in another script is not in scope outside of that script, unless you’re able to return it to the outer scope (which I don’t think dv.view() can do )

So to get rid of error message, you would typically move the output into the script.

Absolutely not being sarcastic, sorry about the confusion, just wanted to let you know I had found something. Thanks for that info. So I guess that means that I can only import functions then, i.e. not what I was going for?

Hi @ReaderGuy42, thanks for your question.

The dv.view() is for passing parameters into another script which is then executed in the Dataview context. Like @holroy said, variables defined there aren’t visible to the calling script.

What you can do though, is define functions in a reusable script, and then use the return values from those functions. You can see a working example of that here:

Then it won’t work I guess, but thanks anyway!