Help for Managing Exams

Hi everybody,

I am a teacher in Computer Science. A particular thing for which I would like to use Obsidian for is helping me managing my exams. I am imaging something like the following illustration

Using the Community Plug-In “Admonition” which makes nice callouts I create a md-file per exam. A example for such file looks like this:

    ```ad-abstract
       title: Explain Recursion (2)
       For an explaition of recursion, please look up Recursion
    ```
  
    ```ad-abstract
       title: Explain Mergesort (3)
       It is 42
    ```
  
    ```ad-abstract
       title: Explain Iteration (2)
       It is the opposite of recursion
    ```

Now, I use this DataviewJS Code to get to the targeted overview

  ```dataviewjs
  
  const pages = dv.pages()
  
  // This regex will find the contents of a specifically formatted callout
  
  const regex = /```ad-(\w+)\r?\ntitle:(.+?)\((\d*?)\)\r?\n(.+?)```/
  
    
  const rows = []
  
  for (const page of pages) {
  
     const file = app.vault.getAbstractFileByPath(page.file.path)
    // Read the file contents
    const contents = await app.vault.read(file)
    // Extract the summary via regex
 
    for (const callout of contents.match(new RegExp(regex, 'sg')) || []) { 
         const match = callout.match(new RegExp(regex, 's'))
         rows.push([match[2], match[3], page.file.link])
     }
  
  }
  
  dv.table(['Title', 'Points', 'Link'], rows)

There are four things for which I would like to ask you for help:

  • At the moment images, which are placed inside the callout are not shown in the Dataview, even if I place a <img width="150px" src= ...> . Do you have an idea why this is the case?

  • What is the best way to perform a similarity comparison for the question text to achieve that all filenames which have the question are placed in the last column?

  • The Admonition Callout-Plugin does not support custom key:value pairs. Do you have any idea how to embedd the “area” of a question into the callout text ?

  • Is is possible to add column sorting on the Dataview Tabe, like in Excel, where you press the column to sort the table based on this selected column ?

Any help is appreciated

I’m not able to give suggestions in js side (even less with a regex). But if you asked about pairs key:value inside a callout, I guess you want to change the way you write your “data” and use fields instead of a “manual” parse of the full content.

About fields inside callouts, you can use something like:

> [!info]
> title:: Explain Recursion
> points:: 2
> For an explaition of recursion, please look up Recursion

or

> [!question]
> [title:: Explain Mergesort]
> (points:: 3)
> It is 42

But these fields are metadata in page level, not pseudo-groups of data.
Taking the example above, if multiple callouts in same file, the metadata structure is as follows:

title:
  - Explain Recursion
  - Explain Mergesort
points:
  - 2
  - 3

But if you use something like this (fields in a bullet list):

> [!question]
> - (title:: Explain Recursion) | (points:: 2)
> 
> For an explaition of recursion, please look up Recursion

> [!question]
> - (title:: Explain Mergesort) | (points:: 3)
> 
> It is 42

Then you can use a query like this:

```dataview
TABLE WITHOUT ID
	Question,
	rows.Lists.points[0] AS Points,
	rows.file.link AS Exame
FROM "your-folder-path"
WHERE file.lists
FLATTEN file.lists AS Lists
WHERE Lists.title
GROUP BY Lists.title AS Question

Hi mnvwvnm,

thanks a lot for your reply and your suggetions. Did you have any success so far in embedding images into the Dataview Table? Can you recommend a plugin for making the Dataview Table sortable?

Yes, in regular DQL.
If you use a pair key: value with an embed link like

image:: ![[something.png]]

then you just need to target the field image.

If you want only a link

image:: [[something.png]]

then you can use the function embed() - embed(image).

About the sort, once I tried the plugin Sortable - GitHub - alexandru-dinu/obsidian-sortable: Table sorting plugin for https://obsidian.md. I’ve the idea that it works for basic queries/tables but not for more complicated fields/columns. But it was a long time ago.

I made a mockup with some alternate write-up’s too further explain stuff:

Part of your issue is like @mnvwvnm says: “… these fields are metadata in page level, not pseudo-groups of data”. If you look in the rightmost part of the image, you see that even though it’s possible to make key → value pairs with Admonition style callouts (or also the newer style callouts, not shown), then these values are disconnected.

The titles are disconnected list from the points list. The only connected values are those in the frontmatter related to thoughts. Sadly, I don’t think there is a good way to connect inline fields, without doing something related to lists.

However, it seems like if you do something like the following:

- (title:: Explain quicksort) | (points:: 15)
  - Your area information

You’re able to get the information, in the list item object, and then you can also get to the area information through the children object attached to your current list item. This is parts of what leads up to the queries presented in earlier posts.

My take on such an endeavour would rather be to ditch the callouts, and use decorated tasks with sub-lists for the area information.
Then you could have data and the table looking something like the image below:

PS! I’ve not talked about gathering the filenames, as I don’t quite get your take on that. Do you want repeat the definition of the questions in multiple files, and then collect where you’ve used them?! That doesn’t really make sense to me. Wouldn’t it then be better to have the question in separate notes, and rather embed the question into different exams, and use the backlinks features (and frontmatter for field definitions) to link stuff together?

So here is a completely different take on your use case. I’ve now made 4 notes, where each is similar to the following:

---
ExamArea: Sorting
Answer: It bubbles up 
---
(Question:: Explain bubblesort) | (Points:: 5)

And then I can make exams like the following:
image

And a review of questions like this:

This is just a coarse example, but you get the drift. Here the questions are just defined once, and you embed them into whatever exam you’ve got, and you make queries based on the exam to review both the questions you’re asking, points related or hints to the answer. If you want images, then go crazy and include them into the question note, no problem.

If you want long questions, that’s no issue either. You could also consider having a longer question not as a field in the question note, and rather have a longer question in the text, and a topic summary for use when reviewing questions like in the last image above.

In short, this is an alternate way of avoiding duplications of question, whilst still having the possibilities to review when you used the questions, and build a case for later exams. If you feel like it, this scheme could also be extended for a personal grading of each question, or other extra information you would like to attach to each question. Hope this helps, and gives you some creative ideas.

PS! How to hide embed decorations

To hide various signs related to embedding documents, I added the CSS files in the following thread:

Meta Post - Common CSS Hacks - #394 by Moonbase59

And had to remove every reference to .markdown-preview-view in that file, to get it to work. (Didn’t read the thread, just copied it, and tried some stuff from another thread)

(If you use Minimal you don’t need any extra snippet: you can use a inherit cssclass in minimal - cssclass: embed-strict - or a global definition in style settings > minimal > embeds).

1 Like

Topic : Part 1/2

Summary
  • How to group each h_row of aoh_rows by h_row.Title? (DVJS20)
  • How to embed the “area” and “cover” fields into the callout box of Admonitions? (DVJS10:M13 or DVJS20:M13)

Test

Summary
  • dataview: v0.5.46
  • admonition: v7.0.4

Input

Summary

dictionary files

  • Locations: “100_Project/01_dataviewjs/01_by_example/Q27_Admonition/Q27_test_data”

folder: 03_markdown_file_outside

  • filename : dic_19680301
---
Date: 1968-03-01
---
#Project/P03


```ad-abstract
title: Explain Recursion (5)
area: General
cover: ![|150](file:///E:/Q27_test_data_images/Q27_General01.jpg)
For an explaition of recursion, please look up Recursion
```



```ad-abstract
title: Explain Mergesort (3)
area: Sorting
cover: ![|150](file:///E:/Q27_test_data_images/Q27_Sorting01.jpg)
It is 42
```



```ad-abstract
title: Explain Iteration (2)
area: Loop
cover: ![|150](file:///E:/Q27_test_data_images/Q27_Loop01.jpg)
It is the opposite of recursion
```




folder: 04_markdown_file_outside

  • filename : dic_19680401
---
Date: 1968-04-01
---
#Project/P04


```ad-abstract
title: Explain Recursion (50)
area: General
cover: ![|150](file:///E:/Q27_test_data_images/Q27_General01.jpg)
It is Q1.
```



```ad-abstract
title: Explain Mergesort (30)
area: Sorting
cover: ![|150](file:///E:/Q27_test_data_images/Q27_Sorting01.jpg)
It is Q2.
```



```ad-abstract
title: Explain Iteration (20)
area: Loop
cover: ![|150](file:///E:/Q27_test_data_images/Q27_Loop01.jpg)
It is Q3.
```




folder: 12_excluded

  • filename : dic_19681201
---
Date: 1968-12-01
---
#Project/P12




images files

  • Both the DVJS10 and DVJS20 support the following file URLs or https URLs.

A local image outside the Obsidian vault: Absolute File Paths (A file URL starting with file:///)

  • A file URL: “file:///E:/Q27_test_data_images/Q27_General01.jpg”

CASE10_markdown_file_outside: To use the markdown syntax with the file URL which is outside the Obsidian vault

```ad-abstract
title: Explain Recursion (50)
area: General
cover: ![|150](file:///E:/Q27_test_data_images/Q27_General01.jpg)
It is Q1.
```

OR

CASE20_HTML_file_outside: To use the HTML syntax with the file URL which is outside the Obsidian vault

```ad-abstract
title: Explain Recursion (50)
area: General
cover: <img width='150px' src='file:///E:/Q27_test_data_images/Q27_General01.jpg'>
It is Q1.
```

A local image inside the Obsidian vault: Absolute File Paths (A file URL starting with file:///)

  • A file URL: “file:///E:/note_a8/Q27_Admonition/Q27_test_data_images/Q27_General01.jpg”
  • Locations of the Obsidian vault: “file:///E:/note_a8/”

CASE11_markdown_file_inside: To use the markdown syntax with the file URL which is inside the Obsidian vault

```ad-abstract
title: Explain Recursion (50)
area: General
cover: ![|150](file:///E:/note_a8/Q27_Admonition/Q27_test_data_images/Q27_General01.jpg)
It is Q1.
```

OR

CASE21_HTML_file_inside: To use the HTML syntax with the file URL which is inside the Obsidian vault

```ad-abstract
title: Explain Recursion (50)
area: General
cover: <img width='150px' src='file:///E:/note_a8/Q27_Admonition/Q27_test_data_images/Q27_General01.jpg'>
It is Q1.
```

An online image


CASE30_markdown_https_online: To use the markdown syntax with the https URL which is an online image

```ad-abstract
title: Explain Recursion (50)
area: General
cover: ![|150](https://m.media-amazon.com/images/I/41aGGHu7Q-L._SX361_BO1,204,203,200_.jpg)
It is Q1.
```

OR

CASE40_HTML_https_online: To use the HTML syntax with the https URL which is an online image

```ad-abstract
title: Explain Recursion (50)
area: General
cover: <img width='150px' src='https://m.media-amazon.com/images/I/41aGGHu7Q-L._SX361_BO1,204,203,200_.jpg'>
It is Q1.
```

DVJS10_TABLE_key_value_pairs_via_RegExp_and_Obsidian_API

Summary

Main DVJS

Code Name Data type Group By Purposes Remark
DVJS10
_TABLE
_key_value_pairs
_via_RegExp
_and_Obsidian_API
callout:
a multi-line string

(A variable)
aoh_rows:
an array of JavaScript Objects
no 1.To get key-value pairs via RegExp and Obsidian API
2.To filter by h_row.Area
3.To filter by h_row.Title
4.To sort by h_row.Title in ascending order
5.To display the result as a table
1.To require the plugin admonition

Notes

Summary

Q1: What does the cover field in the callout box of Admonitions mean?

Summary_Q1
Original Example: Q1 (To be explained)

The local image lke Q27_General01.jpg is outside the Obsidian vault.
The following markdown sytax in cover field will be rendered as the following HTML systax in the A1_11.
Tips:
CASE10_markdown_file_outside: To use the markdown syntax with the file URL which is outside the Obsidian vault

---
Date: 1968-03-01
---
#Project/P03
```ad-abstract
title: Explain Recursion (5)
area: General
cover: ![|150](file:///E:/Q27_test_data_images/Q27_General01.jpg)
For an explaition of recursion, please look up Recursion
```



A1_11: a local image outside the Obsidian vault

Another Example: A1_11

The local image lke Q27_General01.jpg is outside the Obsidian vault.
The following HTML systax in cover field can be simplified as the above markdown systax in the Q1.
Tips:
CASE20_HTML_file_outside: To use the HTML syntax with the file URL which is outside the Obsidian vault

---
Date: 1968-03-01
---
#Project/P03
```ad-abstract
title: Explain Recursion (5)
area: General
cover: <img width='150px' src='file:///E:/Q27_test_data_images/Q27_General01.jpg'>
For an explaition of recursion, please look up Recursion
```



A1_12: a local image inside the Obsidian vault

Another Example: A1_12

The local image lke Q27_General01.jpg is inside the Obsidian vault which locates in the path like ''file:///E:/note_a8/".
Tips:
CASE11_markdown_file_inside: To use the markdown syntax with the file URL which is inside the Obsidian vault

---
Date: 1968-03-01
---
#Project/P03
```ad-abstract
title: Explain Recursion (5)
area: General
cover: ![|150](file:///E:/note_a8/Q27_Admonition/Q27_test_data_images/Q27_General01.jpg)
For an explaition of recursion, please look up Recursion
```



A1_13: an online image

Another Example: A1_13

The following image lke 41aGGHu7Q-L._SX361_BO1,204,203,200_.jpg is an online image.
Tips:
CASE30_markdown_https_online: To use the markdown syntax with the https URL which is an online image

---
Date: 1968-03-01
---
#Project/P03
```ad-abstract
title: Explain Recursion (5)
area: General
cover: ![|150](https://m.media-amazon.com/images/I/41aGGHu7Q-L._SX361_BO1,204,203,200_.jpg)
For an explaition of recursion, please look up Recursion
```



Q2: How not to filter by h_row.Area and /Iteration|Mergesort|Recursion/.test(h_row.Title)? (M41)

Summary_Q2
Original Example: Q2 (To be modified)
```dataviewjs
// M41. filter and sort aoh_rows:
// WHERE_CASE_10:To gather a `h_row` where `h_row.Area` is truthy
// (comments)WHERE_CASE_11:To gather a `h_row` which contains "Explain" 
// WHERE_CASE_12:To gather a `h_row` which matches the regular pattern 
//               like /Iteration|Mergesort|Recursion/
// Tips: See the output of the M39 and modify the M41 as needs.
// #####################################################################
aoh_rows = aoh_rows
    .where((h_row) => h_row.Area)
    // .where((h_row) => dv.func.contains(h_row.Title, "Explain"))
    .where((h_row) =>
        /Iteration|Mergesort|Recursion/.test(h_row.Title)
    )
    .sort((h_row) => h_row.Title, "asc");


```

A2_21:

Another Example: A2_21
```dataviewjs
// M41. filter and sort aoh_rows:
// WHERE_CASE_10:To gather a `h_row` where `h_row.Area` is truthy
// (comments)WHERE_CASE_11:To gather a `h_row` which contains "Explain" 
// WHERE_CASE_12:To gather a `h_row` which matches the regular pattern 
//               like /Iteration|Mergesort|Recursion/
// Tips: See the output of the M39 and modify the M41 as needs.
// #####################################################################
aoh_rows = aoh_rows
    // .where((h_row) => h_row.Area)
    // .where((h_row) => dv.func.contains(h_row.Title, "Explain"))
    // .where((h_row) =>
    //     /Iteration|Mergesort|Recursion/.test(h_row.Title)
    // )
    .sort((h_row) => h_row.Title, "asc");


```

Q3: What does the following code mean? (M41:WHERE_CASE_12)

Summary_Q3
Original Example: Q3 (To be explained)
```dataviewjs
// M41. filter and sort aoh_rows:
// WHERE_CASE_10:To gather a `h_row` where `h_row.Area` is truthy
// (comments)WHERE_CASE_11:To gather a `h_row` which contains "Explain" 
// WHERE_CASE_12:To gather a `h_row` which matches the regular pattern 
//               like /Iteration|Mergesort|Recursion/
// Tips: See the output of the M39 and modify the M41 as needs.
// #####################################################################
aoh_rows = aoh_rows
    .where((h_row) => h_row.Area)
    // .where((h_row) => dv.func.contains(h_row.Title, "Explain"))
    .where((h_row) =>
        /Iteration|Mergesort|Recursion/.test(h_row.Title)
    )
    .sort((h_row) => h_row.Title, "asc");


```

A3_31:

Another Example: A3_31
```dataviewjs
// M41. filter and sort aoh_rows:
// WHERE_CASE_10:To gather a `h_row` where `h_row.Area` is truthy
// (comments)WHERE_CASE_11:To gather a `h_row` which contains "Explain" 
// WHERE_CASE_12:To gather a `h_row` which matches the regular pattern 
//               like /Iteration|Mergesort|Recursion/
// Tips: See the output of the M39 and modify the M41 as needs.
// #####################################################################
aoh_rows = aoh_rows
    .where((h_row) => h_row.Area)
    // .where((h_row) => dv.func.contains(h_row.Title, "Explain"))
    .where(
        (h_row) =>
            dv.func.contains(h_row.Title, "Iteration") ||
            dv.func.contains(h_row.Title, "Mergesort") ||
            dv.func.contains(h_row.Title, "Recursion")
    )
    .sort((h_row) => h_row.Title, "asc");



```

Code DVJS10_TABLE_key_value_pairs_via_RegExp_and_Obsidian_API

Summary_code
title: DVJS10_TABLE_key_value_pairs_via_RegExp_and_Obsidian_API => 0.To require the plugin admonition 1.To get key-value pairs via RegExp and Obsidian API 2.To filter by h_row.Area 3.To filter by h_row.Title 4.To sort by h_row.Title in ascending order 5.To display the result as a table
collapse: open
icon: 
color: 
```dataviewjs
// M11. define pages: gather all relevant pages
// #####################################################################
const pages = dv.pages(
    '"100_Project/01_dataviewjs/01_by_example/Q27_Admonition/Q27_test_data"'
);


// M13. define regex: find the contents of a specifically formatted callout
// REGEX_CALLOUT_BOX_OF_ADMONITION
// input:
// 
// ```ad-abstract
// title: Explain Recursion (5)
// area: General
// cover: ![|150](file:///E:/Q27_test_data_images/Q27_General01.jpg)
// For an explaition of recursion, please look up Recursion
// ```
// 
// output:
// Title="Explain Recursion"
// Points="5"
// Area="General"
// Cover="![|150](file:///E:/Q27_test_data_images/Q27_General01.jpg)"
// Content="For an explaition of recursion, please look up Recursion\n"
//
// OS       Abbreviation  Escape sequence
// Windows : CR+LF         \r\n
// UNIX    : LF            \n
// MAC     : CR            \r
//
// 1.Following Unix, Obsidian under different operating systems uses 
//   LF alone as a line terminator in a markdown file.
// 2.In other words, a markdown file created in Obsidian under different 
//   operating systems uses LF alone as a line terminator.
// 3.Howerver, a read-only markdown file with CR+LF(or CR) as a line 
//   terminator still keeps its original newline. Obsidian cannot transform 
// a newline into LF because the file does not have write permission.
// 4.Therefore, if there is no read-only markdown file,
//  uses LF alone like `let regex_Newline = /(?=\n)/g;`. 
// Otherwise, uses `let regex_Newline = /(?=\r?\n|\r)/g;`
// 
// non-greedy y*?
// non-greedy y+?
// Named Capture Group(?<Name>y)
// #####################################################################
// const regex = /```ad-(\w+)\r?\ntitle:(.+?)\((\d*?)\)\r?\n(.+?)```/
const regex =
    /```ad-(\w+)\r?\ntitle:\s*(?<Title>.+?)\s*\((?<Points>\d*?)\)\r?\narea:\s*(?<Area>.+?)\s*\r?\ncover:\s*(?<Cover>.+?)\s*\r?\n(?<Content>.+?)```/;



// M21. define aoh_rows : a JavaScript array
// #####################################################################
let aoh_rows = [];


// M23. update aoh_rows : 
// transform a JavaScript array into a Dataview data array by using dv.array
// #####################################################################
aoh_rows = dv.array(aoh_rows);


// M31. gather the formatted data :
// #####################################################################
for (const page of pages) {
    
    // M31.FR10 define file:
    // #####################################################################
    const file = app.vault.getAbstractFileByPath(page.file.path);


    // M31.FR12 define contents: Read the file contents
    // #####################################################################
    const contents = await app.vault.read(file);


    // M31.FR14 Extract the summary via regex :
    // g modifier: global. All matches (don't return after first match)
    // s modifier: single line. Dot matches newline characters
    // #####################################################################
    for (const callout of contents.match(new RegExp(regex, "sg")) || []) {
        

        // M31.FR14.FR11 define match :
        // s modifier: single line. Dot matches newline characters
        // ##################################################################### 
        const match = callout.match(new RegExp(regex, "s"));


        // M31.FR14.FR13 push the key-value pairs into the array aoh_rows:
        // #####################################################################
        // aoh_rows.push([match[2], match[3],match[4],match[5], page.file.link]);   
        // aoh_rows["values"].push({
        //     Title: match[2],
        //     Points: match[3],
        //     Area: match[4],
        //     Content: match[5],
        //     Link: page.file.link,
        // }); 
        aoh_rows["values"].push({
            Title: match.groups?.Title,
            Points: match.groups?.Points,
            Area: match.groups?.Area,
            Cover: match.groups?.Cover,           
            Content: match.groups?.Content,
            Name_Of_File: page.file.name,
            Path_Of_File: page.file.path,          
            Link_Of_File: page.file.link,
        });
        
    }
}


// M39. dedbug output aoh_rows:
// #####################################################################
// dv.span("The following is the content of the `aoh_rows` from the DVJS10.<br>");
// aoh_rows.forEach((h_row) => {
//     if (h_row.Name_Of_File === "dic_19680301") {
//         dv.span(JSON.stringify(h_row, null, 2), "\n");
//     }
// });


// The following is the content of the aoh_rows from the DVJS10.
// [
//     {
//         Title: "Explain Recursion",
//         Points: "5",
//         Area: "General",
//         Cover: "![|150](file:///E:/Q27_test_data_images/Q27_General01.jpg)", //=>It is rendered as a image.
//         Content: "For an explaition of recursion, please look up Recursion\n",
//         Name_Of_File: "dic_19680301",
//         Path_Of_File:
//             "100_Project/01_dataviewjs/01_by_example/Q27_Admonition/Q27_test_data/03/dic_19680301.md",
//         Link_Of_File: {
//             path: "100_Project/01_dataviewjs/01_by_example/Q27_Admonition/Q27_test_data/03/dic_19680301.md",
//             embed: false,
//             type: "file",
//         },
//     },
//     {
//         Title: "Explain Mergesort",
//         Points: "3",
//         Area: "Sorting",
//         Cover: "![|150](file:///E:/Q27_test_data_images/Q27_Sorting01.jpg)", //=>It is rendered as a image.
//         Content: "It is 42\n",
//         Name_Of_File: "dic_19680301",
//         Path_Of_File:
//             "100_Project/01_dataviewjs/01_by_example/Q27_Admonition/Q27_test_data/03/dic_19680301.md",
//         Link_Of_File: {
//             path: "100_Project/01_dataviewjs/01_by_example/Q27_Admonition/Q27_test_data/03/dic_19680301.md",
//             embed: false,
//             type: "file",
//         },
//     },
//     {
//         Title: "Explain Iteration",
//         Points: "2",
//         Area: "Loop",
//         Cover: "![|150](file:///E:/Q27_test_data_images/Q27_Loop01.jpg)", //=>It is rendered as a image.
//         Content: "It is the opposite of recursion\n",
//         Name_Of_File: "dic_19680301",
//         Path_Of_File:
//             "100_Project/01_dataviewjs/01_by_example/Q27_Admonition/Q27_test_data/03/dic_19680301.md",
//         Link_Of_File: {
//             path: "100_Project/01_dataviewjs/01_by_example/Q27_Admonition/Q27_test_data/03/dic_19680301.md",
//             embed: false,
//             type: "file",
//         },
//     },
// ];


// M41. filter and sort aoh_rows:
// WHERE_CASE_10:To gather a `h_row` where `h_row.Area` is truthy
// (comments)WHERE_CASE_11:To gather a `h_row` which contains "Explain" 
// WHERE_CASE_12:To gather a `h_row` which matches the regular pattern 
//               like /Iteration|Mergesort|Recursion/
// Tips: See the output of the M39 and modify the M41 as needs.
// #####################################################################
aoh_rows = aoh_rows
    .where((h_row) => h_row.Area)
    // .where((h_row) => dv.func.contains(h_row.Title, "Explain"))
    .where((h_row) =>
        /Iteration|Mergesort|Recursion/.test(h_row.Title)
    )
    .sort((h_row) => h_row.Title, "asc");


// M51. Output aoh_rows as a table: 
// #####################################################################
//dv.span(aoh_rows);
dv.table(
    ["Area", "Cover", "Question", "Points", "Content", "Exame"],
    aoh_rows.map((h_row) => [
        h_row.Area,
        h_row.Cover,  
        h_row.Title,
        h_row.Points,
        h_row.Content,
        h_row.Link_Of_File,
    ])
);


```

Screenshots(DVJS10)


1 Like

@justdoitcc Wow, thanks a lot. I am still figuring out your code, your comments are extremely valuable for that. One last question, do you have a hint for me how to show local pictures in the dataframe? So that there can be an image displayed in the question column.

Topic : Part 2/2

Summary
  • How to group each h_row of aoh_rows by h_row.Title? (DVJS20)
  • How to embed the “area” and “cover” fields into the callout box of Admonitions? (DVJS10:M13 or DVJS20:M13)

DVJS20_TABLE_key_value_pairs_via_RegExp_and_Obsidian_API_groupBy_Title

Summary

Main DVJS

Code Name Data type Group By Purposes Remark
DVJS20
_TABLE
_key_value_pairs
_via_RegExp
_and_Obsidian_API
_groupBy_Title
callout:
a multi-line string

(A variable)
aoh_rows:
an array of JavaScript Objects
yes 1.To get key-value pairs via RegExp and Obsidian API
2.To group by h_row.Title
3.To display the result as a table
1.To require the plugin admonition

2.The DVJS20 is based on the DVJS10.
2.1 The M41 is modified.
2.2 The M51 is added.
2.3 The M61 is modified.

Notes

Summary

Q1: What the following code is the same as? (M31.FR10 + M31.FR12)

Summary_Q1
Original Example: Q1 (To be explained)
```dataviewjs
// M31. gather the formatted data :
// #####################################################################
for (const page of pages) {
    
    
    // M31.FR10 define file:
    // #####################################################################
    const file = app.vault.getAbstractFileByPath(page.file.path);


    // M31.FR12 define contents: Read the file contents
    // #####################################################################
    const contents = await app.vault.read(file);


 
}
```

A1_11: M31.FR10 + M31.FR12 = M31.FR13

Another Example: A1_11
```dataviewjs
// M31. gather the formatted data :
// #####################################################################
for (const page of pages) {
    

    // M31.FR10 define file:
    // #####################################################################
    // const file = app.vault.getAbstractFileByPath(page.file.path);


    // M31.FR12 define contents: Read the file contents
    // #####################################################################
    // const contents = await app.vault.read(file);


    // M31.FR13 define contents: Read the file contents
    // s_entire_content_of_md_file
    // #####################################################################
    let contents = await app.vault.readRaw(page.file.path);


   
}
```

Q2: How to get a string from an embedded image with the path “E:/Q27_test_data_images/computer science/Q27 General01.jpg”?

Summary_Q1
Original Example: Q2 (To be outputed)
```md
cover: ![|150](file:///E:/Q27_test_data_images/computer%20science/Q27%20General01.jpg)
```

A2_21:

Another Example: A2_21
```dataviewjs
// D11. define sStartPath:
// #####################################################################
const sStartPath = "E:/Q27_test_data_images/computer science/";


// D13. define sStartPath:
// #####################################################################
let sEndPath = "Q27 General01.jpg";


// D21. define sFilePath:
// #####################################################################
let sFilePath = sStartPath + sEndPath;


// D23. update sFilePath:
// Note: if you have spaces in the file path, they have to be url-encoded
//       or the link won't work in some systems (e.g. GitHub). –
// #####################################################################
sFilePath = sFilePath.replace(/\s/g, "%20");


// D31. define s_embedded_image:
// #####################################################################
let s_embedded_image = "![|150](file:///" + sFilePath + ")";


// D51. output s_embedded_image: Code Element
// #####################################################################
dv.span("`" + s_embedded_image + "`");
```

Screenshots(A2_21)

//=>![|150](file:///E:/Q27_test_data_images/computer%20science/Q27%20General01.jpg)


Code DVJS20_TABLE_key_value_pairs_via_RegExp_and_Obsidian_API_groupBy_Title

Summary_code
title: DVJS20_TABLE_key_value_pairs_via_RegExp_and_Obsidian_API_groupBy_Title => 0.To require the plugin admonition 1.To get key-value pairs via RegExp and Obsidian API 2.To group by h_row.Title 3.To display the result as a table
collapse: open
icon: 
color: 
```dataviewjs
// M11. define pages: gather all relevant pages
// #####################################################################
const pages = dv.pages(
    '"100_Project/01_dataviewjs/01_by_example/Q27_Admonition/Q27_test_data"'
);


// M13. define regex: find the contents of a specifically formatted callout
// REGEX_CALLOUT_BOX_OF_ADMONITION
// input:
// 
// ```ad-abstract
// title: Explain Recursion (5)
// area: General
// cover: ![|150](file:///E:/Q27_test_data_images/Q27_General01.jpg)
// For an explaition of recursion, please look up Recursion
// ```
// 
// output:
// Title="Explain Recursion"
// Points="5"
// Area="General"
// Cover="![|150](file:///E:/Q27_test_data_images/Q27_General01.jpg)"
// Content="For an explaition of recursion, please look up Recursion\n"
//
// OS       Abbreviation  Escape sequence
// Windows : CR+LF         \r\n
// UNIX    : LF            \n
// MAC     : CR            \r
//
// 1.Following Unix, Obsidian under different operating systems uses 
//   LF alone as a line terminator in a markdown file.
// 2.In other words, a markdown file created in Obsidian under different 
//   operating systems uses LF alone as a line terminator.
// 3.Howerver, a read-only markdown file with CR+LF(or CR) as a line 
//   terminator still keeps its original newline. Obsidian cannot transform 
// a newline into LF because the file does not have write permission.
// 4.Therefore, if there is no read-only markdown file,
//  uses LF alone like `let regex_Newline = /(?=\n)/g;`. 
// Otherwise, uses `let regex_Newline = /(?=\r?\n|\r)/g;`
// 
// non-greedy y*?
// non-greedy y+?
// Named Capture Group(?<Name>y)
// #####################################################################
// const regex = /```ad-(\w+)\r?\ntitle:(.+?)\((\d*?)\)\r?\n(.+?)```/
const regex =
    /```ad-(\w+)\r?\ntitle:\s*(?<Title>.+?)\s*\((?<Points>\d*?)\)\r?\narea:\s*(?<Area>.+?)\s*\r?\ncover:\s*(?<Cover>.+?)\s*\r?\n(?<Content>.+?)```/;



// M21. define aoh_rows : a JavaScript array
// #####################################################################
let aoh_rows = [];


// M23. update aoh_rows : 
// transform a JavaScript array into a Dataview data array by using dv.array
// #####################################################################
aoh_rows = dv.array(aoh_rows);


// M31. gather the formatted data :
// #####################################################################
for (const page of pages) {
    
    // M31.FR10 define file:
    // #####################################################################
    const file = app.vault.getAbstractFileByPath(page.file.path);


    // M31.FR12 define contents: Read the file contents
    // #####################################################################
    const contents = await app.vault.read(file);


    // M31.FR14 Extract the summary via regex :
    // g modifier: global. All matches (don't return after first match)
    // s modifier: single line. Dot matches newline characters
    // #####################################################################
    for (const callout of contents.match(new RegExp(regex, "sg")) || []) {
        

        // M31.FR14.FR11 define match :
        // s modifier: single line. Dot matches newline characters
        // ##################################################################### 
        const match = callout.match(new RegExp(regex, "s"));


        // M31.FR14.FR13 push the key/value pairs into the array aoh_rows:
        // #####################################################################
        // aoh_rows.push([match[2], match[3],match[4],match[5], page.file.link]);
        // aoh_rows["values"].push({
        //     Title: match[2],
        //     Points: match[3],
        //     Area: match[4],
        //     Content: match[5],
        //     Link: page.file.link,
        // }); 
        aoh_rows["values"].push({
            Title: match.groups?.Title,
            Points: match.groups?.Points,
            Area: match.groups?.Area,
            Cover: match.groups?.Cover,           
            Content: match.groups?.Content,
            Name_Of_File: page.file.name,
            Path_Of_File: page.file.path,          
            Link_Of_File: page.file.link,
        }); 

    }
}


// M39. dedbug output aoh_rows:
// #####################################################################
// dv.span("The following is the content of the `aoh_rows` from the DVJS20.<br>");
// aoh_rows.forEach((h_row) => {
//     if (h_row.Name_Of_File === "dic_19680301") {
//         dv.span(JSON.stringify(h_row, null, 2), "\n");
//     }
// });



// The following is the content of the aoh_rows from the DVJS20.
// [
//     {
//         Title: "Explain Recursion",
//         Points: "5",
//         Area: "General",
//         Cover: "![|150](file:///E:/Q27_test_data_images/Q27_General01.jpg)", //=>It is rendered as a image.
//         Content: "For an explaition of recursion, please look up Recursion\n",
//         Name_Of_File: "dic_19680301",
//         Path_Of_File:
//             "100_Project/01_dataviewjs/01_by_example/Q27_Admonition/Q27_test_data/03/dic_19680301.md",
//         Link_Of_File: {
//             path: "100_Project/01_dataviewjs/01_by_example/Q27_Admonition/Q27_test_data/03/dic_19680301.md",
//             embed: false,
//             type: "file",
//         },
//     },
//     {
//         Title: "Explain Mergesort",
//         Points: "3",
//         Area: "Sorting",
//         Cover: "![|150](file:///E:/Q27_test_data_images/Q27_Sorting01.jpg)", //=>It is rendered as a image.
//         Content: "It is 42\n",
//         Name_Of_File: "dic_19680301",
//         Path_Of_File:
//             "100_Project/01_dataviewjs/01_by_example/Q27_Admonition/Q27_test_data/03/dic_19680301.md",
//         Link_Of_File: {
//             path: "100_Project/01_dataviewjs/01_by_example/Q27_Admonition/Q27_test_data/03/dic_19680301.md",
//             embed: false,
//             type: "file",
//         },
//     },
//     {
//         Title: "Explain Iteration",
//         Points: "2",
//         Area: "Loop",
//         Cover: "![|150](file:///E:/Q27_test_data_images/Q27_Loop01.jpg)", //=>It is rendered as a image.
//         Content: "It is the opposite of recursion\n",
//         Name_Of_File: "dic_19680301",
//         Path_Of_File:
//             "100_Project/01_dataviewjs/01_by_example/Q27_Admonition/Q27_test_data/03/dic_19680301.md",
//         Link_Of_File: {
//             path: "100_Project/01_dataviewjs/01_by_example/Q27_Admonition/Q27_test_data/03/dic_19680301.md",
//             embed: false,
//             type: "file",
//         },
//     },
// ];



// M41. filter and sort aoh_rows:
// #####################################################################
// aoh_rows = aoh_rows
//     .where((h_row) => h_row.Area)
//     // .where((h_row) => dv.func.contains(h_row.Title, "Explain"))
//     .where((h_row) =>
//         /Iteration|Mergesort|Recursion/.test(h_row.Title)
//     )
//     .sort((h_row) => h_row.Title, "asc");


// M51. define groups: 
// groupBy_CASE:To group each `h_row` of the `aoh_rows` by `h_row.Title` AS `G1` 
//              (G1=group.rows)(key=group.key)
// (comments)sort_CASE:To sort each `G1` of the `group.rows` by `group.key` in descending order
// #####################################################################
let groups = aoh_rows
    .groupBy((h_row) => h_row.Title);  // groupBy: (default) in ascending order
    //.sort((group) => group.key, "desc");


// M61. Output aoh_rows as a table: 
// #####################################################################
//dv.span(aoh_rows);
dv.table(
    ["Area", "Cover", "Question", "Points", "Content", "Exame"],
    groups.map((group) => [
        group.rows.Area[0],
        group.rows.Cover[0],  
        group.rows.Title[0],
        group.rows.Points,
        group.rows.Content,
        group.rows.Link_Of_File.join(", "),
    ])
);



```

Screenshots(DVJS20)


Reference

Summary

The codes

To link/embed Local images

The local images

The online images

Obsidian_API


Summary

At the moment images, which are placed inside the callout are not shown in the Dataview, even if I place a <img width=“150px” src= …> . Do you have an idea why this is the case?

  • Your HTML syntax is perfect as described above.

Tips:
CASE20_HTML_file_outside: To use the HTML syntax with the file URL which is outside the Obsidian vault

```ad-abstract
title: Explain Recursion (50)
area: General
cover: <img width='150px' src='file:///E:/Q27_test_data_images/Q27_General01.jpg'>
It is Q1.
```
  • The reason why I use single quotation marks like <img width=‘150px’ src= …> is that I prefer to use the following expreesion for debugging:
```dataviewjs
dv.span(JSON.stringify(h_row, null, 2), "\n");
```

  • You can use the basic markdown syntax to add an image element instead of using the HTML syntax.

Tips:
CASE10_markdown_file_outside: To use the markdown syntax with the file URL which is outside the Obsidian vault

```ad-abstract
title: Explain Recursion (50)
area: General
cover: ![|150](file:///E:/Q27_test_data_images/Q27_General01.jpg)
It is Q1.
```

  • Both of the syntaxes are supported by the DVJS10 and DVJS20. In fact, they support six different syntaxes which are described in the sections like “Input > images files” or “DVJS10 > Q1”.
  • The DVJS10 and DVJS20 are updated on 2023-01-11 so that they can render an image in a Dataview table. Furthermore, several comments and Q&A are added. The number of characters in this topic has increased from the original 11,012 to 33,412. The maximum number of characters for a page is 32,000.
  • Many thanks for your question. It has benefited me a lot.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.