Notebook Assets

Ian Hojnicki, Wolfram Research

(RowDefault[, (Spacer1), , (Spacer1), ])  #WolframTechConf

Dealing With External Data

Dependencies on external data sources adds a level of complexity to distributing your documents/applications.

Including binary data directly in a Notebook is typically less efficient.

Space

Binary vs Text (* base64, which is 3:4 *)

Format differences (* compressed vs raw *)

Computation Time

Format conversions can have a real impact serializing Notebooks

Goals for Notebook Assets

Simplify the distribution of Notebooks with external data dependencies

Allow binary data to remain as binary

Provide functionality which promotes data reuse

v14.0: A First Step

Programmatic  Access

Modify a Notebook’s Asset collection

Enumerate Assets in a collection

Request properties from a specific Asset

AssetAdd

Create a new Asset and add it to a collection

({{, Experimental`AssetAdd[name, data]}, {, Experimental`AssetAdd[notebook,name,data]}})

Note:  AssetAdd is HoldRest.

Example

ipsum=Experimental`AssetAdd["loremipsum", "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."]

Note: Asset names are required to be unique to the collection.  Name conflicts result in an error.

Experimental`AssetAdd["loremipsum", "Interdum velit euismod in pellentesque massa placerat. Id cursus metus aliquam eleifend mi in nulla posuere."]

AssetValue

Query an Asset for the specified value

({{, Experimental`AssetValue[asset, property]}})

Note:  Use the Properties key to discover what other keys are supported on a particular AssetObject.

Example

Experimental`AssetValue[ipsum, "Properties"]

Note: You can use the short form as well.

ipsum["Formats"]

Note: DefaultFormat returns the content in the default (aka “preferred”) format.  Which in this simple case, is String.

ipsum["DefaultFormat"]

More Than Just “String”

Expression

expr = Experimental`AssetAdd["expr", 34^78];
Information[expr]

expr["Expression"]

Note: Expression supports an additional Hold, HoldComplete, or HoldForm argument.

expr["Expression",HoldForm]

Bytes

audio = With[{fileName= FindFile["ExampleData/rule30.wav"]},Experimental`AssetAdd["rule30", File[fileName]]];
Information[audio]

AudioPlot@ImportByteArray[audio["Bytes"]]

File

db=With[{fileName = FindFile["ExampleData/ecommerce-database.sqlite"]}, Experimental`AssetAdd["sqlite", File[fileName]]];Information[db]

DatabaseReference[db["File"]]

Note: Any temporary file created will be cleaned up when the FrontEnd exits.

Note: Any modifications made to the temporary file will be discarded.

Image

image = With[{fileName= FindFile["ExampleData/spikey3.bmp"]},Experimental`AssetAdd["spikey", File[fileName]]];
Information[image]

RawBoxes[image["Image"]]

AssetObject Lifetime

An AssetObject is valid until it’s underlying Asset has been removed from the collection.

Example

DynamicModule[{obj=Experimental`AssetObject[\[LeftAssociation]"Location"->,"Name"->"dynamic","MIMEType"->"text/plain","UUID"->"ab9bc575-25d6-9348-bd81-bd2aaa1149f6"\[RightAssociation]]}, Dynamic[Column[{Row[{"Contents: ", obj["DefaultFormat"]}],Row[{"Now: ",DateString@Now}],Row[{"Created: ",DateString@FromUnixTime[obj["Created"]]}]}]]]

AssetFind

Lookup an Asset in a collection

({{, Experimental`AssetFind[name]}, {, Experimental`AssetFind[notebook,name]}, {, Experimental`AssetFind[notebook, All]}})

Example

Experimental`AssetFind["loremipsum"]

Information@Experimental`AssetFind[EvaluationNotebook[], All]

AssetRemove

Removes an Asset from a collection

({{, Experimental`AssetRemove[asset]}, {, Experimental`AssetRemove[notebook,name]}})

Example

Experimental`AssetRemove[ipsum]

Note: AssetRemove will return Missing[“NotAvailable”] if the Asset does not exist.

Experimental`AssetRemove[EvaluationNotebook[], "loremipsum"]

Note: Any existing reference will then be invalidated.

ipsum["Created"]

Demo

A “simple” image previewer

Note: You can find the code at the end of this presentation.

imagePreviewerDemo[]

Future Development

AssetAdd: Support content type sniffing for ByteArray

AssetUpdate: In-place modifications

New Notebook file format

Zip based with a few custom extensions

More efficient storage of binary data

Interface for managing a Notebook’s collection of Assets

Potential  Future Extensions

Additional Asset collections

PacletObject

$FrontEndSession

Native FrontEnd support for more formats

Fonts

Shaders

Stylesheets

Getting Started

You can start experimenting with this in the conference pre-release build.

Questions and/or Comments?

FrontEnd Office Hour 
Friday Nov 3, 2023
11:00 am - 12:00 pm (CDT)

Initialization and Helper stuff...

Off[General::sysffmpeg];

Map[If[#["Name"]=!="dynamic",Experimental`AssetRemove[#]]&, Experimental`AssetFind[EvaluationNotebook[], All]];

Dynamic Module Helper functions

generateListItems[items_List]:=Map[#->Tooltip[#["FileName"],#["FileName"]]&, items]

generatePreview[selectedItem_List]:= If[Length[selectedItem]===0, Pane["No image selected",ImageSize->{200,200}, Alignment->Center],Pane[RawBoxes@(First[selectedItem]["Image"]),ImageSize->{200,200},ImageSizeAction->"ResizeToFit", Alignment->Center]]

insertPicture[]:=Module[{},With[{fileName=SystemDialogInput["FileOpen", {"test",{"Image files"->{"*.png", "*.jpeg","*.jpg", "*.tiff", "*.heic", "*.heif","*.webp"}}}]}, If[fileName =!= $Canceled, Experimental`AssetAdd[CreateUUID[], File[fileName]]]]; Select[Experimental`AssetFind[EvaluationNotebook[], All], StringStartsQ[#["MIMEType"], "image/"]&]]

imagePreviewerDemo[]:=DynamicModule[{selected={}, items=Select[Experimental`AssetFind[EvaluationNotebook[], All], StringStartsQ[#["MIMEType"], "image/"]&]},Panel[Dynamic[PaneSelector[{False-> Button["Add an Image", items=insertPicture[],Method->"Queued"],True->Grid[{{Item[ListPicker[Dynamic[selected],generateListItems[items], Multiselection->False, ImageSize->{100, Automatic}], ItemSize->{Automatic, Full}], Dynamic[generatePreview[selected]]},{Button["Remove Selected", (Experimental`AssetRemove[First@selected];items=Select[Experimental`AssetFind[EvaluationNotebook[], All], StringStartsQ[#["MIMEType"], "image/"]&];selected={}), Enabled->Dynamic[Length[selected] > 0]],Button["Add an Image", items = insertPicture[],Method->"Queued"]}}]}, Dynamic[Length@items > 0], ImageSize->Automatic, ImageMargins->10]]]]

Information support

getAssetObjectInformation[obj_Experimental`AssetObject]:=<|"ObjectType"->"Experimental`AssetObject","Name"->obj["Name"],"MIMEType"->obj["MIMEType"],"Created"->DateString@FromUnixTime[obj["Created"]],"Modified"->DateString@FromUnixTime[obj["Modified"]],"Formats"->obj["Formats"]|>;
Information`AddRegistry[Experimental`AssetObject,getAssetObjectInformation];
Experimental`AssetObject/:Information`FormatLabel[Experimental`AssetObject,"Experimental`AssetObject"]:="Asset";
Experimental`AssetObject/:Information`FormatLabel[Experimental`AssetObject,"Created"]:="Created On";
Experimental`AssetObject/:Information`FormatLabel[Experimental`AssetObject,"Modified"]:="Last Modified";
Experimental`AssetObject/:Information`FormatLabel[Experimental`AssetObject,"Formats"]:="Supported Formats";