[eggPlant Functional] Create a web based catalog of scripts

One more script which you may/may not find useful.

This script creates a web-based catalog of all the scripts available in a suite. It also looks for embedded descriptions, and pulls them out if any.

The descriptions of the scripts are required to be in a certain format…

They need to be specified within @@ signs. The description can span multiple lines. I’ve embedded one in the script below.

You can call this script from the command line by passing in the suite path as a param or directly run this from eggplant.

I’m including the stylesheet that this file uses to create the page. Copy paste the stylesheet code into a file called style.css and place this file in the folder ~/Sites/SciptCatalogs … which is where all the webpages will be written to.

Feel free to change anything you like. Code reviews are welcome!

style.css


BODY    {
         background: peru;
         font: 11pt "Arial";
         color: black;
         margin-left: 10%;
         margin-right: 15%;
         margin-top: 2%;
         }
BODY.sp  {
         background: brown;
         font: 11pt "Comic Sans MS";
         color: crimson;
         margin-left: 10%;
         margin-right: 15%;
         margin-top: 2%;
         font-weight: bold
         }
TD       {font: 11pt "Arial"; color: black; text-align: left; font-weight: bold}
TD.sp    {font: 11pt "Arial"; color: gray; text-align: justify}
TH       {font: 14pt "Arial"; color: navy; font-weight: bold}
H1       {font: 20pt "Arial"; color: maroon; font-weight: bold}
H2       {font: 16pt "Arial"; color: gray; font-weight: bold}
A    	 {font: 11pt "Arial"; color: navy; font-style: italic}
A.sp     {font: 11pt "Arial"; color: white; font-style: italic; font-weight:bold}
FONT.sp  {font: 10pt "Arial"; color: firebrick}
P	 {text-align: justify}
P.pic	 {text-align: center}
P.indent {margin-left: 2em}
P.outdent {margin-left: -2em}

Script


(*@@This script creates an HTML page that lists all the scripts
located in a suite. This script can be called from the 
command line or run directly in eggplant.@@*)

(* Allows Script to be run from command line *)
params suitePath

setlogging off

set suiteName to ""

if suitePath is empty then
	(* Getting Suite Information *)
	put getSuitePath() into suitePath
	if suitePath is empty then exit me
end if

put word 1 to -2 delimited by "." of the last word delimited by "/" of suitePath into suiteName

(* Getting output file *)
put getOutputFile(suiteName) into outputFile

(* Go through file list to catalog them *)
put suitePath & "/Scripts/" into scriptsFolder
put files(scriptsFolder) into lScripts

(* Creating ouptut file as HTML *)
createHTMLPage outputFile, lScripts, suiteName, scriptsFolder

(* Open file with Browser *)
open outputFile with "Firefox"

(* Sub for logging *)
on logFile strToLog, outputFile
	--put "+++++ Writing " & strToLog
	put strToLog & return after file outputFile
end logFile

function getSuitePath
	(* Select suite to catalog *)
	answer folder "Select Suite to Catalog" \ 
			in folder (items 1 to -4 delimited by "/" of the long name of me)
	
	(* Check for empty string in case of cancel *)
	if it is empty then return it
	
	(* Check if valid eggplant suite has been chosen *)
	if it ends with ".suite" then return it
	else return empty
end getSuitePath

function getOutputFile suiteName
	set tempStr to suiteName
	replace every occurrence of " " with "_" in tempStr
	
	put  "~/Sites/ScriptCatalogs/" & tempStr & ".html" into outputFile
	create file outputFile
	return outputFile
end getOutputFile

on createHTMLPage outputFile, lScripts, suiteName, scriptFolder
	(* Creating Header *)
	logFile "<HTML>", outputFile
	logFile tab & "<HEAD>", outputFile
	logFile tab & tab & "<TITLE>", outputFile
	logFile tab & tab & tab & sTitle, outputFile
	logFile tab & tab & "</TITLE>" , outputFile
	logFile tab & tab & "<LINK>> \
			& "style.css" & <<TYPE>> & "text/css" & <<">> & ">", outputFile
	logFile tab & " </HEAD>", outputFile
	logFile tab & "<BODY>", outputFile
	
	(* Creating body *)
	logFile tab & tab & "<H1>Script Catalog for Suite -" &&  suiteName & "</H1>", outputFile
	
	if the number of items of lScripts is greater than 0 then
		logFile tab & tab & "<TABLE>> & "0" & <<CELLSPACING>> & "2" & <<CELLPADDING>> & "8" & <<">>  & \
				">", outputFile
		logFile tab & tab & tab & "<TR>", outputFile
		logFile tab & tab & tab & "<TH> Script Name </TH>", outputFile
		logFile tab & tab & tab & "<TH>> & "10" & <<">> &"> Description </TH>", outputFile
		logFile tab & tab & tab & "</TR>" & return, outputFile
		repeat with each item of lScripts
			if it ends with ".script" then
				put chars 1 to -8 of it's name into scriptName
				
				logFile tab & tab & tab & "<TR>> & \ 
						"2" & <<">> &  ">", outputFile
				logFile tab & tab & tab & "<TD>" && scriptName && "</TD>", outputFile
				put getScriptDescription(scriptFolder & it) into sDescription
				logFile tab & tab & tab & "<TD>> & "sp" & <<COLSPAN>> & "10" & <<">> &">" && \
						sDescription && " </TD>", outputFile
				logFile tab & tab & tab & "</TR>" & return, outputFile
			end if
		end repeat
		
		
		(* Ending HTML File *)
		logFile tab & tab & "</TABLE>", outputFile
	else
		logFile tab & tab & "<h2> No scripts available for cataloging</h2>" , outputFile
	end if
	logFile tab & "</BODY>", outputFile
	logFile "</HTML>", outputFile
	
end createHTMLPage

function getScriptDescription scriptName
	put "" into sDesc
	
	put file scriptName into sFile
	set num to 0
	set prevnum to 0
	
	repeat forever
		set num to offset("@@",sFile, num)
		if num is equal to 0 then exit repeat
		if prevnum is not equal to 0 then
			if num - prevnum is greater than 1000 then exit repeat
			put chars 2+prevnum to num-2 of sFile into sDesc
		end if
		set prevnum to num
		put num
	end repeat
	if sDesc is empty then put "No description available/found" into sDesc
	
	
	return sDesc
end getScriptDescription

[/b]

Thanks for that contribution! It’s wonderful to see people coming up with innovative script solutions that many people can use, and this is a good one.

Since you invited changes and code reviews… I couldn’t resist! So here are a few comments and alternate ideas that crossed my mind:

The first thing I noticed was this line, which took me by surprise:

if suitePath is empty then exit me

I immediately had to try this myself, because the command “exit me” shouldn’t be valid! Apparently there’s a small bug in the SenseTalk compiler that lets this work – but I rather like it, so it may continue to work in the future. For now, though, you may want to change it to “exit handler” which is the officially sanctioned way to do this (or to “exit all” if you want to terminate execution immediately).

You also had this line in your script:

 put word 1 to -2 delimited by "." of the last word delimited by "/" of suitePath into suiteName

While this works fine, I just wanted to mention a subtle point that might not be obvious, regarding the difference between words and items. Items in SenseTalk can be delimited by any character (or sequence of characters) that you choose. Each occurrence of this delimiter indicates a separation between one item and the next, even if the delimiter occurs several times in a row.

Word delimiters, on the other hand, are somewhat more fluid. One word may be separated from the next by any number and combination of the characters specified for the delimiter. The default word delimiter allows for one or more spaces, tabs, or return characters in any order between words. The code above will treat multiple "."s or "/"s in a row the same as a single one. So it would treat “MyOddSuite…suite” as being named “MyOddSuite”. If you change “word” to “item” in the command, it will recognize the name as “MyOddSuite…” instead. Not a likely occurrence, I admit, but one you may want to be aware of.

The biggest change I made was to your createHTMLPage handler. The way it was written was absolutely fine, but I couldn’t resist converting it to use the merge() function to see how it would look. SenseTalk’s merge() function is very powerful but often overlooked, and this is a perfect place to use it. It allows you to create a template in which expressions enclosed in double square brackets will be replaced by their values, but it also allows the brackets to include lines of SenseTalk code.

Here is my version of the createHTMLPage handler:

on createHTMLPage outputFile, lScripts, suiteName, scriptFolder 
	(* Creating Header *) 
	set headerTemplate to {{
	<HTML> 
	[[tab]]<HEAD> 
	[[tab & tab]]<TITLE>
	[[tab & tab & tab & suiteName]] Script Catalog 
	[[tab & tab]]</TITLE>" 
	[[tab & tab]]<LINK>
	[[tab]]</HEAD> 
	}}
	logFile merge(headerTemplate), outputFile
	
	(* Creating body *) 
	set bodyTemplate to {{
	[[tab]]<BODY> 
	[[tab & tab]]<H1>Script Catalog for Suite - [[suiteName]]</H1> 
	
	[[if the number of items of lScripts is greater than 0 then]] 
	[[tab & tab]]<TABLE> 
	[[tab & tab & tab]]<TR>
	[[tab & tab & tab]]<TH> Script Name </TH> 
	[[tab & tab & tab]]<TH> Description </TH> 
	[[tab & tab & tab]]</TR> 
	[[repeat with each item of lScripts 
	if it ends with ".script" then 
	put chars 1 to -8 of it's name into scriptName ]]
	
	[[tab & tab & tab]]<TR> 
	[[tab & tab & tab]]<TD>[[scriptName]]</TD> 
	[[put getScriptDescription(scriptFolder & it) into sDescription]] 
	[[tab & tab & tab]]<TD>[[sDescription]]</TD> 
	[[tab & tab & tab]]</TR> 
	[[end if 
	end repeat]] 
	[[tab & tab]]</TABLE> 
	[[else]] 
	[[tab & tab]]<h2> No scripts available for cataloging</h2> 
	[[end if]] 
	[[tab]]</BODY> 
	</HTML>
	}}
	logFile merge(bodyTemplate), outputFile
	
end createHTMLPage 

I think you’ll agree that it is a lot less cluttered and easier to read this way, although having some of the SenseTalk code mixed in with the template may look a bit odd. It also has the disadvantage of being difficult to debug if there is a problem, so you need to write the embedded code carefully when using this approach.

I hope some of this feedback is helpful. I enjoyed the chance to pontificate, and I thought your script was excellent, and a terrific idea.

My complete script (including a couple of other minor enhancements) is attached.

Thank you very much for the review.

Never used the merge function.

The line
if it ends with “.suite” then return it

Should be:
if it ends with “.suite/” then return it

For some strange reason, the suite selection returns “/” at the end.

~Qie He

Thanks for catching that. The script posted here should be considered obsolete (see below), but I’ve updated it now anyway, so it should work properly. The answer folder command was changed slightly a while ago, along with other commands and functions that return a folder path, to return a path ending in “/” for convenience.

For creating a catalog of scripts in a suite, a newer example has been posted: EggDoc. The EggDoc script will produce not only a listing of all scripts in a suite, but also document all of the handlers in each script. Please use EggDoc for your script documentation needs.

Hello Bharathh

Are you still around and using Eggplant or have moved on ?

I wanted to know how to use the Merge function / template. I have a html template file but not sure how to use the merge function to read the template and use it.

Any help would be great!

Thank you