<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:taxo="http://purl.org/rss/1.0/modules/taxonomy/" version="2.0">
  <channel>
    <title>topic Session 8: Working with external files &amp;amp; Databases in JMP Scripters Club Discussions</title>
    <link>https://community.jmp.com/t5/JMP-Scripters-Club-Discussions/Session-8-Working-with-external-files-amp-Databases/m-p/655930#M99</link>
    <description>&lt;P&gt;&lt;STRONG&gt;Working with external files &amp;amp; Databases&lt;/STRONG&gt;&lt;BR /&gt;&lt;SPAN&gt;Text files, excel files, Zip files, SQL embedded in your JSL&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;With:&amp;nbsp;&lt;a href="https://community.jmp.com/t5/user/viewprofilepage/user-id/14366"&gt;@jthi&lt;/a&gt;&amp;nbsp;and&amp;nbsp;&lt;a href="https://community.jmp.com/t5/user/viewprofilepage/user-id/4182"&gt;@maurogerber&lt;/a&gt;.&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;Video 1:&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;&lt;div class="lia-vid-container video-embed-center"&gt;&lt;div id="lia-vid-6330667953112w960h540r987" class="lia-video-brightcove-player-container"&gt;&lt;video-js data-video-id="6330667953112" data-account="6058004218001" data-player="default" data-embed="default" class="vjs-fluid" controls="" data-application-id="" style="width: 100%; height: 100%;"&gt;&lt;/video-js&gt;&lt;/div&gt;&lt;script src="https://players.brightcove.net/6058004218001/default_default/index.min.js"&gt;&lt;/script&gt;&lt;script&gt;(function() {  var wrapper = document.getElementById('lia-vid-6330667953112w960h540r987');  var videoEl = wrapper ? wrapper.querySelector('video-js') : null;  if (videoEl) {     if (window.videojs) {       window.videojs(videoEl).ready(function() {         this.on('loadedmetadata', function() {           this.el().querySelectorAll('.vjs-load-progress div[data-start]').forEach(function(bar) {             bar.setAttribute('role', 'presentation');             bar.setAttribute('aria-hidden', 'true');           });         });       });     }  }})();&lt;/script&gt;&lt;a class="video-embed-link" href="https://community.jmp.com/t5/video/gallerypage/video-id/6330667953112"&gt;(view in My Videos)&lt;/a&gt;&lt;/div&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;Video 2: Examples&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;&lt;div class="lia-vid-container video-embed-center"&gt;&lt;div id="lia-vid-6330667374112w960h540r811" class="lia-video-brightcove-player-container"&gt;&lt;video-js data-video-id="6330667374112" data-account="6058004218001" data-player="default" data-embed="default" class="vjs-fluid" controls="" data-application-id="" style="width: 100%; height: 100%;"&gt;&lt;/video-js&gt;&lt;/div&gt;&lt;script src="https://players.brightcove.net/6058004218001/default_default/index.min.js"&gt;&lt;/script&gt;&lt;script&gt;(function() {  var wrapper = document.getElementById('lia-vid-6330667374112w960h540r811');  var videoEl = wrapper ? wrapper.querySelector('video-js') : null;  if (videoEl) {     if (window.videojs) {       window.videojs(videoEl).ready(function() {         this.on('loadedmetadata', function() {           this.el().querySelectorAll('.vjs-load-progress div[data-start]').forEach(function(bar) {             bar.setAttribute('role', 'presentation');             bar.setAttribute('aria-hidden', 'true');           });         });       });     }  }})();&lt;/script&gt;&lt;a class="video-embed-link" href="https://community.jmp.com/t5/video/gallerypage/video-id/6330667374112"&gt;(view in My Videos)&lt;/a&gt;&lt;/div&gt;&lt;/SPAN&gt;&lt;/P&gt;</description>
    <pubDate>Mon, 10 Jul 2023 07:02:51 GMT</pubDate>
    <dc:creator>maria_astals</dc:creator>
    <dc:date>2023-07-10T07:02:51Z</dc:date>
    <item>
      <title>Session 8: Working with external files &amp; Databases</title>
      <link>https://community.jmp.com/t5/JMP-Scripters-Club-Discussions/Session-8-Working-with-external-files-amp-Databases/m-p/655930#M99</link>
      <description>&lt;P&gt;&lt;STRONG&gt;Working with external files &amp;amp; Databases&lt;/STRONG&gt;&lt;BR /&gt;&lt;SPAN&gt;Text files, excel files, Zip files, SQL embedded in your JSL&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;With:&amp;nbsp;&lt;a href="https://community.jmp.com/t5/user/viewprofilepage/user-id/14366"&gt;@jthi&lt;/a&gt;&amp;nbsp;and&amp;nbsp;&lt;a href="https://community.jmp.com/t5/user/viewprofilepage/user-id/4182"&gt;@maurogerber&lt;/a&gt;.&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;Video 1:&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;&lt;div class="lia-vid-container video-embed-center"&gt;&lt;div id="lia-vid-6330667953112w960h540r426" class="lia-video-brightcove-player-container"&gt;&lt;video-js data-video-id="6330667953112" data-account="6058004218001" data-player="default" data-embed="default" class="vjs-fluid" controls="" data-application-id="" style="width: 100%; height: 100%;"&gt;&lt;/video-js&gt;&lt;/div&gt;&lt;script src="https://players.brightcove.net/6058004218001/default_default/index.min.js"&gt;&lt;/script&gt;&lt;script&gt;(function() {  var wrapper = document.getElementById('lia-vid-6330667953112w960h540r426');  var videoEl = wrapper ? wrapper.querySelector('video-js') : null;  if (videoEl) {     if (window.videojs) {       window.videojs(videoEl).ready(function() {         this.on('loadedmetadata', function() {           this.el().querySelectorAll('.vjs-load-progress div[data-start]').forEach(function(bar) {             bar.setAttribute('role', 'presentation');             bar.setAttribute('aria-hidden', 'true');           });         });       });     }  }})();&lt;/script&gt;&lt;a class="video-embed-link" href="https://community.jmp.com/t5/video/gallerypage/video-id/6330667953112"&gt;(view in My Videos)&lt;/a&gt;&lt;/div&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;Video 2: Examples&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;&lt;div class="lia-vid-container video-embed-center"&gt;&lt;div id="lia-vid-6330667374112w960h540r77" class="lia-video-brightcove-player-container"&gt;&lt;video-js data-video-id="6330667374112" data-account="6058004218001" data-player="default" data-embed="default" class="vjs-fluid" controls="" data-application-id="" style="width: 100%; height: 100%;"&gt;&lt;/video-js&gt;&lt;/div&gt;&lt;script src="https://players.brightcove.net/6058004218001/default_default/index.min.js"&gt;&lt;/script&gt;&lt;script&gt;(function() {  var wrapper = document.getElementById('lia-vid-6330667374112w960h540r77');  var videoEl = wrapper ? wrapper.querySelector('video-js') : null;  if (videoEl) {     if (window.videojs) {       window.videojs(videoEl).ready(function() {         this.on('loadedmetadata', function() {           this.el().querySelectorAll('.vjs-load-progress div[data-start]').forEach(function(bar) {             bar.setAttribute('role', 'presentation');             bar.setAttribute('aria-hidden', 'true');           });         });       });     }  }})();&lt;/script&gt;&lt;a class="video-embed-link" href="https://community.jmp.com/t5/video/gallerypage/video-id/6330667374112"&gt;(view in My Videos)&lt;/a&gt;&lt;/div&gt;&lt;/SPAN&gt;&lt;/P&gt;</description>
      <pubDate>Mon, 10 Jul 2023 07:02:51 GMT</pubDate>
      <guid>https://community.jmp.com/t5/JMP-Scripters-Club-Discussions/Session-8-Working-with-external-files-amp-Databases/m-p/655930#M99</guid>
      <dc:creator>maria_astals</dc:creator>
      <dc:date>2023-07-10T07:02:51Z</dc:date>
    </item>
    <item>
      <title>Re: Session 8: Working with external files &amp; Databases</title>
      <link>https://community.jmp.com/t5/JMP-Scripters-Club-Discussions/Session-8-Working-with-external-files-amp-Databases/m-p/656432#M100</link>
      <description>&lt;P&gt;Regarding Parse XML. You can find working example from JMP's Sample Scripts. You can open the folder by running following command in the JMP Script Window&lt;/P&gt;
&lt;PRE&gt;&lt;CODE class=" language-jsl"&gt;Open("$SAMPLE_SCRIPTS")&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;It might be possible to simplify the example, but at least it seemed to get the sheetname correctly&lt;/P&gt;
&lt;PRE&gt;&lt;CODE class=" language-jsl"&gt;sheetname = XML Attr(
	"urn:schemas-microsoft-com:office:spreadsheet^Name",
	"Untitled"
);
dt = New Table( sheetname );
&lt;/CODE&gt;&lt;/PRE&gt;</description>
      <pubDate>Fri, 07 Jul 2023 08:40:56 GMT</pubDate>
      <guid>https://community.jmp.com/t5/JMP-Scripters-Club-Discussions/Session-8-Working-with-external-files-amp-Databases/m-p/656432#M100</guid>
      <dc:creator>jthi</dc:creator>
      <dc:date>2023-07-07T08:40:56Z</dc:date>
    </item>
    <item>
      <title>Re: Session 8: Working with external files &amp; Databases</title>
      <link>https://community.jmp.com/t5/JMP-Scripters-Club-Discussions/Session-8-Working-with-external-files-amp-Databases/m-p/656815#M101</link>
      <description>&lt;P&gt;It seems like&amp;nbsp;&lt;a href="https://community.jmp.com/t5/user/viewprofilepage/user-id/4125"&gt;@maria_astals&lt;/a&gt;&amp;nbsp;post contains same .zip twice. Could you Maria update the starting post with the attachment I have added here?&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;Also, would it be helpful if the scripts were to be posted also as comments in a spoiler? Of course the scripts won't work if there are external files being used, but depending for example how the search on community works, it could maybe be seen there?&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;Script from the session below:&lt;/P&gt;
&lt;LI-SPOILER&gt;
&lt;PRE&gt;&lt;CODE class=" language-jsl"&gt;//##################################################################################
//###
//### 8. Working with external files and databases
//###
//##################################################################################
// In this file we will cover:
// - Short recap of session 7 (scope/namespaces and report layer)
// - .xlsx
// - text files
// - xml files
// - json files
// - GET Request

// - DLL (See Session 5)
// - BATCH FILES (See Session 5)

// - Databases (using .sqlite database)
// - pictures
// - pdf


//##################################################################################
//### Recap of session 7 (Scope properties and report layer)
// https://www.jmp.com/support/help/en/17.0/#page/jmp/advanced-scoping-and-namespaces.shtml#ww339298
// https://www.jmp.com/support/help/en/17.0/#page/jmp/best-practices-for-advanced-scripting.shtml#
// https://www.jmp.com/support/help/en/17.0/#page/jmp/show-symbols-clear-symbols-and-delete-symbols.shtml
//##################################################################################

// Scope and namespaces
// Start your scripts with 
Names Default To Here(1);
// Enabling the Names Default To Here mode associates a scope called Here with an executing script

// Do not leave following to production code as they will break other scripts
Clear Symbols();
Clear Globals();
Delete Symbols();
Delete Globals();

// To see which messages object has you can use Show Properties(obj);

// Report layer
// To explore report layer use obj &amp;lt;&amp;lt; Show Properties; and obj &amp;lt;&amp;lt; Show Tree Structure; (obj &amp;lt;&amp;lt; Get XML;)
// Get access to report layer using rep = obj &amp;lt;&amp;lt; Report; or Report(obj);

// Report subscripting (returns first match)
param_tratio2 = biv_report[Outline Box("Parameter Estimates"), Number Col Box("t Ratio")];

// Use obj &amp;lt;&amp;lt; Get XML; to see how XML looks before using XPath

// XPath (returns a list of matches AND you can send messages to a list)
biv_report &amp;lt;&amp;lt; XPath("//OutlineBox[contains(text(), 'Bivariate Normal Ellipse')]");


//##################################################################################
//###
//### Working with external files
//###
//##################################################################################

//##################################################################################
//### JMP Data Table reminders (see session 6)
//##################################################################################
dt = Open("$SAMPLE_DATA/Big Class.jmp"); // Invisible, Private
m_num = dt &amp;lt;&amp;lt; get as matrix;
names = Column(dt, "name") &amp;lt;&amp;lt; Get Values;
all_vals = dt[0, 0]; // list of lists
dt[0, {"height", "weight"}]; // matrix because columns are numeric
Close(dt, no save);

//##################################################################################
//### Excel file
//##################################################################################
// Open with interactive Open first to get basic settings and modify as needed
// or see the syntax from Scripting Index and hope for best
dt = Open(
	"$SAMPLE_IMPORT_DATA/Team Results.xlsx",
	Worksheets("Ungrouped Team Results"),
	Worksheet Settings(Headers Start on Row(3), Data Starts on Row(4))
);

// Save jmp data table as excel workbook
dt = Open("$SAMPLE_DATA/Big Class.jmp", invisible);
dt &amp;lt;&amp;lt; Save(Get Default Directory() || "BigClass.xlsx"); // save to same directory as this script is saved into

// Create as excel workbook
dt2 = Open("$SAMPLE_DATA/Abrasion.jmp", invisible);
Create Excel Workbook(Get Default Directory() || "MyWorkbook.xlsx", {dt, dt2}, {"Big", "Abrasive"});
Close(dt, no save); Close(dt2, no save);

Open(Get Default Directory());

Try(Delete File(Get Default Directory() || "BigClass.xlsx"));
Try(Delete File(Get Default Directory() || "MyWorkbook.xlsx"));

//##################################################################################
//### Text files
//##################################################################################
txt1 = Load Text File("$SAMPLE_IMPORT_DATA/Animals.txt");
txt2 = Load Text File("$SAMPLE_IMPORT_DATA/Animals_L.txt");
dt_txt = Open("$SAMPLE_IMPORT_DATA/Animals_L.txt"); // if data is clean formatted Open() might work

Save Text File(Get Default Directory() || "example.txt", "line1\!Nline2\!N", mode("replace"));
Save Text File(Get Default Directory() || "example.txt", "line3\!Nline4\!N", mode("append"));
txt4 = Load Text File(Get Default Directory() || "example.txt");

Try(Delete File(Get Default Directory() || "example.txt"));

// To read text line by line, use Words() with correct separator
// Some possible separators, \!N, \!r, \!n
all_lines = Words(txt2, "\!N");
For Each({line}, all_lines,
	show(line);
	//Show(Words(line, "\!t"));
);

// Some trickery opening as script and using ScriptBox
my_scriptwindow = Open("$SAMPLE_IMPORT_DATA/Animals_L.txt", "script");
my_scriptwindow &amp;lt;&amp;lt; Show Window(0);
myfile_lines = my_scriptwindow[scriptBox(1)] &amp;lt;&amp;lt; getLines();
my_scriptwindow &amp;lt;&amp;lt; close window;
wait(0);
show(all_lines, myscript_lines);


txt3_line3 = Load Text File("$SAMPLE_IMPORT_DATA/Animals_line3.txt"); // data starts on line 3
// Depending on what you want to do, you could remove unnecessary lines (1 and 2)
lines = Words(txt3_line3, "\!N"); // be careful with Words() as it will combine separators
Remove From(lines, 1, 1);
// and then save it, reopen or build table by looping or just Open with Blob
// Advanced... you can use Blob with Open. There is explanation somewhere in community by @Craige_Hales
dt = Open(Char To Blob(Concat Items(lines, "\!N")), "text"); //"text" must be here


//##################################################################################
//### XML files
//##################################################################################
// I have very rarely used this, but this script can give an idea
// Parse XML, XML Attr and XML Text
file_contents = Load Text File("$SAMPLE_IMPORT_DATA/BigclassExcel.xml");
Parse XML(file_contents,
	OnElement(
		"Worksheet",
		StartTag(
			sheetname = XML Attr("Name");
			show(sheetname);
			dt = New Table(sheetname);
			row = 1;
			col = 1;
		)
	),
	OnElement(
		"Row",
		StartTag(
			If(row &amp;gt; 1, // assume first row is column names 
				dt &amp;lt;&amp;lt; addRows(1)
			)
		),
		EndTag(
			row++;
			col = 1;
		)
	),
	OnElement("Cell", EndTag(col++)),
	OnElement(
		"Data",
		EndTag(
			data = XML Text(collapse);
			If(row == 1,
				New Column(data, character(10)), 	// assume first row has column names 
				Column(col)[row - 1] = data// and other rows have data 
			);
		)
	)
);

// Open with XML export made with interactive. Might be ways to get this working
dt = Open(
	"$SAMPLE_IMPORT_DATA/BigclassExcel.xml",
	XML Settings(
		Stack(0),
		Row("/Workbook/Worksheet/Table/Row/Cell/Data"),
		Col(
			"/Workbook/Worksheet/Table/Row/Cell/Data",
			Column Name("Data"),
			Fill("Use Once"),
			Type("Character"),
			Format({"Best"}),
			Modeling Type("Continuous")
		)
	),
	XML Wizard(1) // XML Wizard(0) to disable ui
);

//##################################################################################
//### DLL and Batch files (See Session 5 (Load DLL and Run Program())
//##################################################################################
// DLL
// See https://docs.microsoft.com/en-us/windows/win32/api/
F_getScreenSize = Function({}, {Default Local},
	securDll = Load DLL("user32.dll", autoDeclare(false));
	securDll &amp;lt;&amp;lt; DeclareFunction("GetSystemMetrics", Arg(Int32), returns(Int32));
	size_x = 0;
	size_y = 0;
	mode_x = 0;
	mode_y = 1;
	size_x = securDll &amp;lt;&amp;lt; GetSystemMetrics(mode_x);
	size_y = securDll &amp;lt;&amp;lt; GetSystemMetrics(mode_y);  
	
	Return(Eval List({size_x, size_y}));
);
myScreenSize = F_getScreenSize();


// Batch file (.bat)
// A quick note on Batch Files
//   - No trailing spaces
//   - Must add a 'NewLine'
mybat_string = "
echo off \!n
cls \!n
echo Hello World \!n
echo The time is " || Char(MDYHMS(Today())) || " \!n
";
mybat_path = Get Default Directory() || "testbatch.bat";
Save Text File(mybat_path, mybat_string, Mode("replace"));

// If we need return result, you could use Run Program
bat_path = Convert File Path(mybat_path, windows);
rp1 = Run Program(
	Executable(bat_path),
	ReadFunction("text")
);


Show(Directory Exists("C:\SCRIPTERSCLUBTEST"));
mybat_string2 = "\[mkdir "C:\SCRIPTERSCLUBTEST\" \!n]\";
mybat_path = Get Default Directory() || "testbatch.bat";
mybat_path = Convert File Path(mybat_path, windows);
Save Text File(mybat_path, mybat_string2, Mode("replace"));
Open(mybat_path); // this will run the bat
Show(Directory Exists("C:\SCRIPTERSCLUBTEST"));
wait(3); // we will need to wait
Show(Directory Exists("C:\SCRIPTERSCLUBTEST"));

// Or use DLL
Try(Delete Directory("C:\SCRIPTERSCLUBTEST"));
wait(0);
Show(Directory Exists("C:\SCRIPTERSCLUBTEST"));

kernel32 = LoadDLL("Kernel32");
kernel32 &amp;lt;&amp;lt; DeclareFunction(
	 "WinExec",
	 Convention(STDCALL),
	 Alias("WinExec"),
	 Arg(AnsiString, "lpCmdLine"),
	 Arg(Int32, "uCmdShow"),
	 Returns(Int32) 
);

kernel32 &amp;lt;&amp;lt; WinExec(mybat_path, 5);
Show(Directory Exists("C:\SCRIPTERSCLUBTEST"));
wait(3); // we will need to wait. The time depends on .bat ran
Show(Directory Exists("C:\SCRIPTERSCLUBTEST"));

Try(Delete File(mybat_path));
Try(Delete Directory("C:\Users\SCRIPTERSCLUBTEST"));


//##################################################################################
//### JSON Files
//### there are many different functions, search scripting index for "JSON"
//##################################################################################
json1 = Load Text File(Get Default Directory() ||"Big Class_column.json", "JSON"); // associative array
json2 = Load Text File(Get Default Directory() ||"Big Class_row.json"); // string
// Parse JSON(json2);

// Using interactive Open with preview
dt = Open(
	"/H:/JSL Course/session 8/Big Class_row.json",
	JSON Settings(
		Stack(0),
		Row("/root"),
		Col("/root/name", Column Name("name"), Fill("Use Once"), Type("Character"), Format({"Best"}), Modeling Type("Continuous")),
		Col("/root/age", Column Name("age"), Fill("Use Once"), Type("Numeric"), Format({"Best"}), Modeling Type("Continuous")),
		Col("/root/sex", Column Name("sex"), Fill("Use Once"), Type("Character"), Format({"Best"}), Modeling Type("Continuous")),
		Col("/root/height", Column Name("height"), Fill("Use Once"), Type("Numeric"), Format({"Best"}), Modeling Type("Continuous")),
		Col("/root/weight", Column Name("weight"), Fill("Use Once"), Type("Numeric"), Format({"Best"}), Modeling Type("Continuous"))
	),
	JSON Wizard(0)
);

// If the JSON is easy to use format for JMP just Open might work
dt2 = Open(Get Default Directory() ||"Big Class_row.json");

// Sometimes it might be useful to use Open(Blob()) trick
dt3 = Open(Char To Blob(json2), "json"); // open text to table

Try(Close(dt, no save));
Try(Close(dt2, no save));
Try(Close(dt3, no save));

// JSON to AA and AA to JSON
my_json_str = "{ \!"myChar\!": \!"Character Value\!", \!"myNum\!": 12345}";
aa_json = Parse JSON(my_json_str);
json_str = As JSON Expr(aa_json); // back to JSON
// aa_json2 = Parse JSON(json_str);

//##################################################################################
//### HTTP Requests (GET)
//##################################################################################
// can be used for testing simple requests https://httpbin.org/

request = New HTTP Request(
	URL("https://httpbin.org//get"), 
	Method("GET"), 
	Timeout(5) // default is 60. I suggest modifying it as needed and depending on the server
);
data = request &amp;lt;&amp;lt; Send; // See scripting index for more messages related to New HTTP Request
// request &amp;lt;&amp;lt; Get Last URL;
// request &amp;lt;&amp;lt; Get Status;
aa_json = Parse JSON(data);

//##################################################################################
//### Working with databases (sqlite select)
	// https://community.jmp.com/t5/Discussions/Which-way-to-go-query-database/m-p/655026
	// https://www.jmp.com/support/help/en/17.0/#page/jmp/sql-functions.shtml#ww5089351
	// https://www.jmp.com/support/help/en/17.0/#page/jmp/sql-messages.shtml#
//##################################################################################
// General format (to my knowledge) for connection string is
	// connection_str ="DSN=MYDSN_NAME;UID=USERNAME;PWD=PASSWORD";
	// dsn_string = "Driver={ODBC DRIVER};Dbq=DBDSNNAME;UID=DBUSERNAME;PWD=DBPASSWORD;"; (driver part isn't always needed)
// When New SQL Query is used you need to prefix with ODBC:
	//dsn_string = "ODBC:Driver={ODBC DRIVER};Dbq=DBDSNNAME;UID=DBUSERNAME;PWD=DBPASSWORD;";
// If you are able to build a query using JMP's Query Builder to your database, you can get connection string from there
	// it might contain much more information but you can most likely simplify it
// THESE ARE DEPENDENT ON YOUR DATABASE
	// IT department might be able to help with these
	// https://learn.microsoft.com/en-us/sql/integration-services/import-export-data/connect-to-an-odbc-data-source-sql-server-import-and-export-wizard?view=sql-server-ver16
	// https://docs.oracle.com/en/cloud/saas/netsuite/ns-online-help/section_4406003916.html#Connecting-Using-a-Connection-String

	// Also be careful when sharing tables which have table scripts created by database query as those might contain passwords
		// Hide ODBC connection String preference might help with this (https://community.jmp.com/t5/JMP-Knowledge-Base/Database-table-scripts-Update-from-Database-Modify-etc-or/ta-p/621050)

sqlite_path = Get Default Directory() || "QCdata.sqlite";
connection_str = Eval Insert("SQLite:Database=^sqlite_path^");
sql_str = "select * from lot";


// Open database 
dt = Open Database(connection_Str, sql_str, "NewTable");


// Create Database Connection, Execute SQL, Close Database Connection
dbc = Create Database Connection(connection_str);
dt = Execute SQL(dbc, sql_str, "NewTable");
Close Database Connection(dbc);


// New SQL Query (to my knowledge this is the preferred method)

sql_obj = New SQL Query(Connection(connection_str), Custom SQL(sql_str));
dt = sql_obj &amp;lt;&amp;lt; Run; // uses users jmp preferences
dt = sql_obj &amp;lt;&amp;lt; Run Background; // Will let JMP work
dt = sql_obj &amp;lt;&amp;lt; Run Foreground; // Will block JMP execution until query finishes

// You can set OnRunComplete scripts to New SQL Query (see scripting index)
sql_obj &amp;lt;&amp;lt; Run Foreground(OnRunComplete(Write("Number of rows in query result: ", N Rows(queryResult)))); // will not print to embdedded log
// There is also OnRunCanceled and OnError


// Few SQL "tips"
// Use connection_str and sql_template pattern (you could even save these to simple .jsl files)
dsn_string = Eval Insert("SQLite:Database=^sqlite_path^");
sql_template = "\[select * 
from lot 
where lot in (¤lotnr¤)]\";

lotnr = "'API00017'";
sql_str = Eval Insert(sql_template, "¤"); // insert values to your template

sql_obj = New SQL Query(Connection(dsn_string), Custom SQL(sql_str));
dt = sql_obj &amp;lt;&amp;lt; Run Foreground;

lotnr = "'API00017', 'API00058'";
sql_str = Eval Insert(sql_template, "¤");
sql_obj = New SQL Query(Connection(dsn_string), Custom SQL(sql_str));
dt = sql_obj &amp;lt;&amp;lt; Run Foreground;


// If database queries won't throw errors and you can try to use Log Capture() to capture issues
sql_error = Log Capture(dt = Open Database(connection_Str, sql_str, "NewTable"));
show(sql_error);
If(Contains(sql_error, "[ODBC Driver Manager]"),
    Throw(sql_error);
);

// Might be a good idea to check if dt is empty or has no rows
Is Empty(dt10); 
N Rows(dt); // this will error if dt doesn't exist


// Use Query to perform SQL queries with OPEN JMP data tables
dt = Open("$SAMPLE_DATA/Big Class.jmp", Invisible);
dt_result = Query(dt, 
	"SELECT name, age, height 
	FROM 'Big Class'
	WHERE age &amp;gt; 14;"
);
 
 // Using aliases, performing a join
dtSAT = Open("$SAMPLE_DATA/SATByYear.jmp", Invisible);
dtUS = Open("$SAMPLE_DATA/US Demographics.jmp", Invisible);
dt_result2 = Query(
	Table(dtSAT, "t1"),
	Table(dtUS, "t2"), 
	"\[SELECT t1.State, t1."SAT Math", t2."College Degrees",
            t2."Eighth Grade Math"
       FROM t1
       LEFT OUTER JOIN t2
           ON t1.State = t2.State
       WHERE t1.'SAT Math' &amp;gt; 550;
      ]\"
);

// Query that returns a scalar value
retval1 = Query(Scalar, dt, "SELECT AVG(height) from 'Big Class';");
// Query with no tables
retval2 = Query(Scalar, "SELECT SQRT(152399025);");

// could be done without sql, but it is good to have options
avg_height = Col Mean(Column(dt, "height")); 
sqrt_val = Sqrt(152399025);



// available functions https://www.jmp.com/support/help/en/16.2/#page/jmp/sql-functions-available-for-jmp-queries.shtml#ww998370
// There are tasks which are sometimes easier to perform with SQL (if you can write the query) than with JMP
dt = Open("$SAMPLE_DATA/Big Class.jmp", Invisible);
// one example could be concatenation of values to single column by group
// This can be helpful before summary or split for example to avoid losing data
dt_result = Query(dt, 
	"\[SELECT age, group_concat(name, ', ') names
	FROM 'Big Class'
	GROUP BY age
	]\"
);



//##################################################################################
//### zip files (scripting index for more)
// https://www.jmp.com/support/help/en/16.2/#page/jmp/zip-archives.shtml?os=win&amp;amp;source=application#1931138
//##################################################################################
za = Open(Get Default Directory() || "folder1.zip", zip); // open as zip archive object
dirlist = za &amp;lt;&amp;lt; dir; // returns list of members in the list 
za &amp;lt;&amp;lt; Write("newfile.txt", "this is newfiledata"); // write new file
dirlist = za &amp;lt;&amp;lt; dir; // 
text = za &amp;lt;&amp;lt; Read(dirlist[2]); // read from zip archive

// Compress folder into a Zip file (@Justin_Chilton)
	// https://community.jmp.com/t5/JSL-Cookbook-Archived/Compress-a-Folder-into-a-Zip-File/ta-p/48878
	
//##################################################################################
//### Multiple File Import (MFI)
// https://www.jmp.com/support/help/en/16.2/#page/jmp/utility-functions.shtml?os=win&amp;amp;source=application#ww7168938
//### Can be helpful when you have multiple similar looking .csv files for example
//### Try it first out interactivelty and then copy the script
//### You could also get list of files using Files In Directory() and looping over them but 
//### MFI has additional features
//##################################################################################
mfi = Multiple File Import(
	&amp;lt;&amp;lt;Set Folder("$SAMPLE_IMPORT_DATA"),
	&amp;lt;&amp;lt;Set Name Filter("UN*.csv"), // import files with this name
	&amp;lt;&amp;lt;Set Name Enable(1), // display the file name in a column
	&amp;lt;&amp;lt;Set Add File Name Column(1),
) &amp;lt;&amp;lt; Import Data(); // returns a list

// MFI + Zip (@Craige_Hales)
	// https://community.jmp.com/t5/Uncharted/MFI-from-Selected-CSVs-in-a-ZIP/ba-p/439274#U439274

// Directory Tree: Explore Space Used by Folders (@Craige_Hales)
	// https://community.jmp.com/t5/Uncharted/Directory-Tree-Explore-Space-Used-by-Folders/ba-p/456571


//##################################################################################
//### Pictures
// https://community.jmp.com/t5/Discussions/Add-images-from-URLs-in-a-column/td-p/556300
// https://community.jmp.com/t5/Discussions/Display-PNG-image-from-URL-into-a-Wafer-Map/m-p/578427
// See Set Graphlet from scripting index and External image example
//##################################################################################
/* Picture file imported as a picture object */
pic = Open("$SAMPLE_IMAGES/tile.jpg", jpg);
show(pic);
// Adding picture to new window
nw = New Window("Picture", 
	Outline Box("Picture", 
		Picture Box(pic)
	)
);

// adding picture to data table's expression column
dt = New Table("Untitled",
	Add Rows(1),
	Compress File When Saved(1),
	New Column("Column 1", Expression, "None", Set Selected, Set Values({}))
);
dt[1, 1] = pic;



//##################################################################################
//### PDF
// Example from scripting index
//##################################################################################
/* PDF file imported as one or multiple data tables
open(pdfFilePath,
	PDF Tables(Table(&amp;lt;Name(name)&amp;gt;, Add Rows(Page(n), &amp;lt;Header Rows(n)&amp;gt;, Rect(top, left, right, bottom), &amp;lt;RowBorders(n, ...)&amp;gt;, &amp;lt;Column Borders(n, ....)&amp;gt;), ...)) |
	PDF All Tables(&amp;lt; Combine(All | Matching Headers | None)&amp;gt;, &amp;lt;Minimum Rows(n)&amp;gt;, &amp;lt;Minimum Columns(n)&amp;gt;) |
	PDF Text(&amp;lt;Pages(n, ...)&amp;gt;, &amp;lt;sort&amp;gt;) |
	PDF Wizard
);*/
dt = Open("$SAMPLE_DATA\Big Class.jmp");
w = New Window("test", Data Table Box(dt));
w &amp;lt;&amp;lt; save picture(Get Default Directory() || "test.pdf", pdf);
w &amp;lt;&amp;lt; close window;
close(dt, no save);
pdftable = Open(Get Default Directory() || "test.pdf", PDF All Tables(Combine(all))); // just some of the rows
pdftable2 = Open(Get Default Directory() || "test.pdf", PDF Tables(Table(Table Name("test"), Add Rows(Page(1), Rect(0, 0, 5, 3)))));

Close(pdftable, no save);
Close(pdftable2, no save);

Try(Delete File(Get Default Directory() || "test.pdf"));
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;/LI-SPOILER&gt;</description>
      <pubDate>Mon, 10 Jul 2023 06:39:48 GMT</pubDate>
      <guid>https://community.jmp.com/t5/JMP-Scripters-Club-Discussions/Session-8-Working-with-external-files-amp-Databases/m-p/656815#M101</guid>
      <dc:creator>jthi</dc:creator>
      <dc:date>2023-07-10T06:39:48Z</dc:date>
    </item>
    <item>
      <title>Re: Session 8: Working with external files &amp; Databases</title>
      <link>https://community.jmp.com/t5/JMP-Scripters-Club-Discussions/Session-8-Working-with-external-files-amp-Databases/m-p/656818#M102</link>
      <description>&lt;P&gt;&lt;a href="https://community.jmp.com/t5/user/viewprofilepage/user-id/14366"&gt;@jthi&lt;/a&gt;&amp;nbsp;, done :)&lt;/img&gt;&lt;/P&gt;</description>
      <pubDate>Mon, 10 Jul 2023 07:03:31 GMT</pubDate>
      <guid>https://community.jmp.com/t5/JMP-Scripters-Club-Discussions/Session-8-Working-with-external-files-amp-Databases/m-p/656818#M102</guid>
      <dc:creator>maria_astals</dc:creator>
      <dc:date>2023-07-10T07:03:31Z</dc:date>
    </item>
    <item>
      <title>Re: Session 8: Working with external files &amp; Databases</title>
      <link>https://community.jmp.com/t5/JMP-Scripters-Club-Discussions/Session-8-Working-with-external-files-amp-Databases/m-p/656824#M103</link>
      <description>&lt;P&gt;Sorry I was not able to make it but I thought I re share this post from the community, which I found extremely helpful:&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&lt;A title="Save to Database is too slow (10k records per minute)" href="https://community.jmp.com/t5/Discussions/Save-to-Database-is-too-slow-10k-records-per-minute/td-p/10264" target="_blank" rel="noopener"&gt;Save to Database is too slow (10k records per minute)&lt;/A&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;Henning&lt;/P&gt;</description>
      <pubDate>Mon, 10 Jul 2023 08:16:12 GMT</pubDate>
      <guid>https://community.jmp.com/t5/JMP-Scripters-Club-Discussions/Session-8-Working-with-external-files-amp-Databases/m-p/656824#M103</guid>
      <dc:creator>vohwinkelh0</dc:creator>
      <dc:date>2023-07-10T08:16:12Z</dc:date>
    </item>
    <item>
      <title>Re: Session 8: Working with external files &amp; Databases</title>
      <link>https://community.jmp.com/t5/JMP-Scripters-Club-Discussions/Session-8-Working-with-external-files-amp-Databases/m-p/664614#M104</link>
      <description>&lt;P&gt;That was another highly useful lesson for the novice scripter. I specifically liked&amp;nbsp;&lt;a href="https://community.jmp.com/t5/user/viewprofilepage/user-id/14366"&gt;@jthi&lt;/a&gt;'s comment at 29:32 ("&lt;EM&gt;If you can get [the JMP Query Builder] to work.&lt;/EM&gt;"). My experience was that the JMP Query Builder constantly froze or crashed in JMP14, but I have since given it another try with JMP17 where it is stable, although it can take a lot of time to build a query, depending on the size of the tables involved. Being a novice and not knowing SQL, I worked for a long time by loading a millions of rows (i.e., the entirety of a given table via Open Database(), ignoring all filter functionality) and discarding everything not needed locally. This was easy enough to learn over time, but has its limits. With JMP17 I was finally able to understood -or at least, I like to believe so- the meaning of "pushing queries back to the server". The first query copy-pasted from the JMP Query Builder into a jsl has already saved me a few hours of waiting time.&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;Also interesting comments by &lt;A href="https://community.jmp.com/t5/user/viewprofilepage/user-id/4182" target="_blank" rel="noopener"&gt;@maurogerber&lt;/A&gt;&amp;nbsp;in the second video around 13:20. Can I interpret&amp;nbsp;them as shown below?&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;PRE&gt;&lt;CODE class=" language-jsl"&gt;New SQL Query(); // which is what I extract from the JMP Query builder and copy to a script

// Question 1: Can the below use the same or similar SQL commands as the above?&lt;BR /&gt;&lt;BR /&gt;// Question 2: If yes, is there a preferred method?

Open Database();
Execute SQL();&lt;BR /&gt;&lt;/CODE&gt;&lt;/PRE&gt;</description>
      <pubDate>Tue, 01 Aug 2023 19:11:34 GMT</pubDate>
      <guid>https://community.jmp.com/t5/JMP-Scripters-Club-Discussions/Session-8-Working-with-external-files-amp-Databases/m-p/664614#M104</guid>
      <dc:creator>Ressel</dc:creator>
      <dc:date>2023-08-01T19:11:34Z</dc:date>
    </item>
    <item>
      <title>Re: Session 8: Working with external files &amp; Databases</title>
      <link>https://community.jmp.com/t5/JMP-Scripters-Club-Discussions/Session-8-Working-with-external-files-amp-Databases/m-p/664629#M105</link>
      <description>&lt;P&gt;With Open Database() and Execute SQL() you will have to use similar commands as you would use with New SQL Query() when using &lt;STRONG&gt;Custom&lt;/STRONG&gt; &lt;STRONG&gt;SQL &lt;/STRONG&gt;(so sql queries)&lt;STRONG&gt;.&lt;/STRONG&gt; To my understanding New SQL Query() is the preferred method &lt;SPAN class="lia-message-subject"&gt;(see Brian's answer&lt;LI-MESSAGE title="Which way to go query database?" uid="652420" url="https://community.jmp.com/t5/Discussions/Which-way-to-go-query-database/m-p/652420#U652420" discussion_style_icon_css="lia-mention-container-editor-message lia-img-icon-forum-thread lia-fa-icon lia-fa-forum lia-fa-thread lia-fa"&gt;&lt;/LI-MESSAGE&gt; ) as it will have support in the future. &lt;BR /&gt;&lt;/SPAN&gt;&lt;/P&gt;</description>
      <pubDate>Tue, 01 Aug 2023 20:01:18 GMT</pubDate>
      <guid>https://community.jmp.com/t5/JMP-Scripters-Club-Discussions/Session-8-Working-with-external-files-amp-Databases/m-p/664629#M105</guid>
      <dc:creator>jthi</dc:creator>
      <dc:date>2023-08-01T20:01:18Z</dc:date>
    </item>
  </channel>
</rss>

