Molding CSV data — actions, queries and visualizations

This is Part 4 of Tutorial: molding CSV data into an explainable system. This part shows that, in addition to contextual views, there are also contextual actions and queries that can also be implemented easily as tiny tools. We also see that simple visualizations can easily be created to provide richer views of our domain.

If you are jumping into the tutorial at this point, and have some previously saved work not already in this image, you should file it in now. Otherwise, you can file in a sample snapshot reflecting the work completed at the end of the previous part:

VRTutorialExamples new fileIn10MoldingAuthors
  

We can easily navigate from the dataset to individual papers or authors, but how can we navigate back? We can evaluate self dataset in an inspector playground, but is there an easier way? Instead of introducing another contextual view, what we need here is a Contextual Action, a button that performs a useful task, possibly updating a view, spawning a new view, or launching an external tool.

Evaluate self dataset in an Inspector playground of paper 128. Extract an action to navigate to the dataset. Hint: Right-click in the playground as you would to extract a method or a view, but this time select Create <gtAction> for dataset. Before accepting the generated method, change the icon from empty to up, and change the tooltip text to Spawn dataset. ⇒ Refresh the view to see the new action associated with an “up” button. Hint: You can refresh the Inspector views by clicking the ▾ menu icon at the top right, and selecting “↻ Update pane tool.”

VRDatasetExamples new paper128
  

Take a closer look at the newly defined action. It looks like this:

It's quite similar to a view method, except that its argument is anAction instead of aView, and the pragma is <gtAction> instead of <gtView>.

This action is currently defined only for papers. Let's make it work for authors as well. ⇒ Push the gtDatasetFor: method up to its superclass, VRDomainEntity. Now the action is also available for authors!

It would be nice to access the actual papers referred to in the dataset. We don't have the URLs or DOIs of these publications, but we can ask Google Scholar to find them for us, like this:

paper := VRDatasetExamples new paper128.
query := paper title copyReplaceAll: String space with: '+'.
gsUrl := 'https://scholar.google.com/scholar?hl=en&q=' , query.
WebBrowser openOn: gsUrl
  

Create an action for papers that will open a web browser on the Google Scholar query for that paper. Hint: You can use the BrGlamorousVectorIcons>>#web web <script: 'self actor inspect'> ^ [ | element svg | svg := '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="#000" stroke-width="1" stroke-linecap="round" stroke-linejoin="round"> <path d="M8 1.5 a 6.5 6.5 0 1 1 0 13 a 6.5 6.5 0 1 1 0 -13 M8 1.5 a 3.4 6.5 0 0 1 0 13 M8 1.5 a 3.4 6.5 0 0 0 0 13 M1.5 8 H14.5 M1.5 8 a 6.5 3.4 0 0 1 13 0 M1.5 8 a 6.5 3.4 0 0 0 13 0"/> </svg>'. element := BlSkiaSvgElement new size: 16 @ 16; svg: svg; yourself. element ] asStencil icon for the button.

Here are the changes:

"protocol: #actions"

VRDomainEntity >> gtDatasetFor: anAction
	<gtAction>
	^ anAction button
		icon: BrGlamorousVectorIcons up;
		tooltip: 'Spawn dataset';
		action: [ :button | button phlow spawnObject: self dataset ]

"protocol: #views"

VRPaper >> gtGoogleScholarFor: anAction
	<gtAction>
	^ anAction button
		icon: BrGlamorousVectorIcons web;
		priority: 10;
		tooltip: 'Look up in Google Scholar';
		action: [ :button | 
			WebBrowser
				openOn: 'https://scholar.google.com/scholar?hl=en&q='
						, (self title copyReplaceAll: String space with: '+') ]
  

To sync to this point in the tutorial (throwing away any other changes) evaluate:

VRTutorialExamples new fileIn11Actions
  

To find papers related to a particular topic, we need a way to query the list of papers of a dataset. A contextual view cannot do this on its own, nor can an action. We need a Contextual Search. Like views and actions, they are defined as methods of the objects to search, with a dedicated pragma, namely <gtSearch>. Have a look at the following search method for datasets. It searches through the list of papers of the dataset, using the filterBySubstring criterion. We search papers by title (itemName: #title), and the result (sendCategory:) is a new paper group built from the query result (aCategory items contents):

Add this method to the VRDataset class. You should now see a 🔍 search icon at the top right. Which papers have “Mondrian” in the title? Add further searches by paper type and author name. Set the priorities so that paper titles are searched before paper types, and then author names.

VRDatasetExamples new vrDataset 
  

As usual, here are the changes:

"protocol: #search"

VRDataset >> gtSpotterForPaperTitles: aSearch
	<gtSearch>
	^ aSearch list
		title: 'Paper titles';
		priority: 10;
		items: [ self allPapers ];
		itemsLimit: Float infinity;
		itemName: #title;
		sendCategory: [ :aCategory :aStep :thisSearch |
			VRPaperGroup withAll: aCategory items contents ];
		filterBySubstring

"protocol: #search"

VRDataset >> gtSpotterForPaperTypes: aSearch
	<gtSearch>
	^ aSearch list
		title: 'Paper titles';
		priority: 20;
		items: [ self allPapers ];
		itemsLimit: Float infinity;
		itemName: #type;
		sendCategory: [ :aCategory :aStep :thisSearch |
			VRPaperGroup withAll: aCategory items contents ];
		filterBySubstring

"protocol: #search"

VRDataset >> gtSpotterForAuthorNames: aSearch
	<gtSearch>
	^ aSearch list
		title: 'Author name';
		priority: 30;
		items: [ self authors ];
		itemsLimit: Float infinity;
		itemName: #name;
		sendCategory: [ :aCategory :aStep :thisSearch |
			VRAuthorGroup withAll: aCategory items contents ];
		filterBySubstring
  

To sync to this point in the tutorial (throwing away any other changes) evaluate:

VRTutorialExamples new fileIn12Searches
  

Here is an example of a somewhat more complex analysis. We would like to understand whether coauthors independently submit papers or not. Here we display a graph starting from a given author, and linking to that authors papers, the other co-authors, and their further publications, if any:

aMondrian := GtMondrian new.
anAuthor := VRDatasetExamples new authorLanza.

theAuthors := {anAuthor} asOrderedCollection , anAuthor coauthors.
thePapers := (theAuthors flatCollect: #papers) copyWithoutDuplicates.
nodes := theAuthors , thePapers.
aMondrian nodes
	shape: [ :node | node mondrianNodeElementHighlighting: self ];
	with: nodes.
aMondrian edges connectToAll: #mondrianChildren.
aMondrian layout
	custom: (GtGraphForceBasedLayout new
			charge: -2000;
			length: 200).
aMondrian
  

We have to implement a few helper methods first, such as mondrianChildren and so on. ⇒ Accept the following changes to enable the mondrian graph.

"protocol: #mondrian"

VRPaper >> mondrianNodeElementHighlighting: aNode
	^ BlElement new
		size: 10 @ 10;
		border: (self mondrianBorderHighlighting:aNode);
		background: self mondrianNodeColor;
		aptitude: (BrGlamorousWithExplicitTooltipAptitude text: self printString)

"protocol: #mondrian"

VRAuthor >> mondrianNodeElementHighlighting: aNode
	^ BlTextElement new
		text: self initials asRopedText;
		padding: (BlInsets all: 12);
		border: (self mondrianBorderHighlighting:aNode);
		background: self mondrianNodeColor;
		aptitude: (BrGlamorousWithExplicitTooltipAptitude text: self name);
		geometry: BlCircleGeometry new;
		yourself

"protocol: #mondrian"

VRDomainEntity >> mondrianNodeElementHighlighting: aNode
	self subclassResponsibility

"protocol: #mondrian"

VRAuthor >> mondrianNodeColor
	^ Color paleRed

"protocol: #mondrian"

VRPaper >> mondrianNodeColor
	^ Color paleBlue

"protocol: #mondrian"

VRDomainEntity >> mondrianNodeColor
	self subclassResponsibility

"protocol: #mondrian"

VRDomainEntity >> mondrianChildren
	self subclassResponsibility

"protocol: #mondrian"

VRPaper >> mondrianChildren
	^ self authors

"protocol: #mondrian"

VRAuthor >> mondrianChildren
	^ self papers

"protocol: #mondrian"

VRDomainEntity >> mondrianBorderHighlighting: aNode
	^ BlBorder paint: Color black width: 1

"protocol: #mondrian"

VRAuthor >> initials
	^ '' join: (self name substrings collect: #first)
  

Here we can see clearly which co-authors are also active in other papers, and which are not. The diagram is also clickable, so we can dive into specific authors and papers. Now we would like to turn this into a dedicated view for authors. ⇒ Extract a method coauthorMondrian: from this snippet, parameterized by the mondrian instance. Hint: copy this code to an inspector playground of an author, replacing anAuthor by self. ⇒ Introduce a mondrian view that uses this helper method. Hint: To do this, you need to send the mondrian factory message to the view, like this:

VRDatasetExamples new authorLanza
  

Note how you can now navigate between Co-author graph views by clicking on authors in the graph.

Here are the remaining changes:

"protocol: #views"

VRAuthor >> gtCoauthorGraphFor: aView
	<gtView>
	^ aView mondrian
		title: 'Co-author graph';
		priority: 40;
		painting: [ :aMondrian | self coauthorMondrian: aMondrian ];
		actionUpdateButton

"protocol: #mondrian"

VRAuthor >> coauthorMondrian: aMondrian
	| thePapers nodes theAuthors |
	theAuthors := {self} asOrderedCollection , self coauthors.
	thePapers := (theAuthors flatCollect: #papers) copyWithoutDuplicates.
	nodes := theAuthors , thePapers.
	aMondrian nodes
		shape: [ :node | node mondrianNodeElementHighlighting: self ];
		with: nodes.
	aMondrian edges connectToAll: #mondrianChildren.
	aMondrian layout
		custom: (GtGraphForceBasedLayout new
				charge: -2000;
				length: 200).
	^ aMondrian
  

To sync to this point in the tutorial (throwing away any other changes) evaluate:

VRTutorialExamples new fileIn13Mondrian
  

NB: This page links to Pages containing missing references - allowed failures to allow references to missing classes and methods in this page. Next: Part 5. Molding CSV data — continuing the exploration