(* :Title: StartUp *) (* :Authors: Phillip Kent, Phil Ramsden, John Wood The METRIC Project, Imperial College, London. *) (* :Summary: This package contains global configurations and logging functions for the learning modules written by the METRIC Project. It is called when a notebook '*pack.m' file is loaded. The learning modules are published in the book "Experiments in Undergraduate Mathematics: A Mathematica-based approach" (Imperial College Press, 1996). *) (* :Package Version: 2.3 *) (* :Mathematica Version: 2.2 and 3.0 *) (* :Copyright: Copyright 1993-97 Imperial College *) (* :History: Version 1.0 prepared February-September 1993; Version 1.1 with additions for Macintosh, Feb 1994; Version 1.2, minor changes for Windows v2.2.2, June 1994; Version 1.3, Oct 94, changes for 94/95 evaluation period - revised FinalComments[]; Version 1.3.1, Nov 94, in Unix, takes Username from the shell and logs it; InstallFromPath[] moved here from Question.m; Version 1.3.2, Nov 94, in Unix, program checks for existence of log directory (/tmp/MathLog) and creates it if required. Version 2.0, Sept 95, this file renamed to StartUp.m, new logging mechanism, unused material deleted; 2.0.1 (29/9/95): Logging writes both the input text and its number; 2.0.2 (24/2/96): Changes to usage messages; Version 2.3 - February 97 - changes for Mathematica 3.0: InstallFromPath removed - not used any more; LogCommandCall modified to handle 3.0's "boxes"; Configuration options for Windows95, WindowsNT. *) (* :Description: At the beginning of a METRIC package, the commands in this file establish the global configuration, and in particular if logging is to be performed a log file is created with commands for writing data to it. Before this file is loaded, the following variables should be set: NotebookCode [compulsory]: this is a (usually) two-letter string used to identify the topic in the logfile name (e.g. "DF" for Differentiation is set in the package diffpack.m). LocalLoggingQ [optional]: (True or False) will override the value of the global setting LoggingQ. LocalUserNameQ [optional]: (True or False) will override the value of the global setting UserNameQ. defaultLogCommandsList [compulsory if logging]: the list of commands which will be logged by default, unless modified in a session by a call to Transcript. The global fallback default is {"GiveQuestion", "LastAnswer"} . The logging functions are OS specific. OS's are "Unix", "Windows95" or "WindowsNT", "MSDOS" (for Windows versions before v.2.2.2), "Windows" (for Windows v2.2.2 and later), and "MacOS". There are also some differences between version 3.0 and version 2.x. In MSDOS/Windows and MacOS, items written to files are buffered until a Close is issued. We're working round this by doing Close after each Write, and using OpenAppend. *) (*********** Global configurations - these should be set according to your requirements and computer system(s). *) (* LoggingQ: to log or not to log, set to True or False*) If[ValueQ[LocalLoggingQ], LoggingQ = LocalLoggingQ, (* else apply the global value - set this to True or False as you wish *) LoggingQ = False ] (* UserNameQ: if True, prompt the user for a user identity to be kept in the log*) If[ValueQ[LocalUserNameQ], (* a value was specified for a particular module *) UserNameQ = LocalUserNameQ, (* else apply the global value - set this to True or False as you wish *) UserNameQ = False ] (* NotebookCode: ought to be set in the module package, do this if it wasn't... *) If[Not[ValueQ[NotebookCode]], NotebookCode = "ND" (* "Not Defined" *) ] (* If LoggingQ = True, $PreRead is set up to record specified command calls *) If[LoggingQ, LogCommandCall[s_]:= Apply[Or, Map[StringMatchQ[ToString[s],"*"<>#<>"*"]&, logTheseCommandsList]]; If[ValueQ[defaultLogCommandsList], (* value is set in the module package *) logTheseCommandsList = defaultLogCommandsList, (*else the minimal/default set*) logTheseCommandsList = defaultLogCommandsList = {"GiveQuestion", "LastAnswer"} ]; LogSelectiveFunction = (If[LogCommandCall[#], WriteToLog[{$Line,#}]]; #) &; (* everything except null inputs to be recorded *) LogAllFunction = (If[Not[# === ""], WriteToLog[{$Line,#}]]; #) &; $PreRead = LogSelectiveFunction, (* else - this is necessary to reset $PreRead when two or more modules used in the same session *) $PreRead = . ] (* If LoggingQ=True you must choose a location for the log file directory, LogFileDir, which is set differently for each type of computer system - we specify here some defaults for Unix, DOS/Windows and Macintosh. Locations differ also between Mathematica 2.x and 3.0.. If you use a different system from the ones given you should add an entry both here and in the section below where WriteToLog is defined - the variable $OperatingSystem is the name by which Mathematica identifies the underlying system. You should ensure that the chosen LogFileDir exists - this file will attempt to create it if it doesn't already exist but this may fail where file systems are write-protected. *) If[LoggingQ, If[TrueQ[$OperatingSystem=="Unix"], LogFileDir="/tmp/MathLog"]; If[TrueQ[$VersionNumber < 3.0], If[TrueQ[$OperatingSystem=="MSDOS"||$OperatingSystem=="Windows"], LogFileDir=HomeDirectory[]<>"\\MATHLOG"]; If[TrueQ[$OperatingSystem=="Windows95"], LogFileDir=HomeDirectory[]<>"\\MathLog"]; If[TrueQ[$OperatingSystem=="WindowsNT"], LogFileDir=HomeDirectory[]<>"\\MathLog"]; If[TrueQ[$OperatingSystem=="MacOS"], LogFileDir=HomeDirectory[]<>":MathLog"], (* else >= 3.0 *) If[TrueQ[$OperatingSystem=="Windows"], LogFileDir=$LaunchDirectory<>"\\MATHLOG"]; If[TrueQ[$OperatingSystem=="Windows95"], LogFileDir=$LaunchDirectory<>"\\MathLog"]; If[TrueQ[$OperatingSystem=="WindowsNT"], LogFileDir=$LaunchDirectory<>"\\MathLog"]; If[TrueQ[$OperatingSystem=="MacOS"], LogFileDir=$LaunchDirectory<>":MathLog"] ] ] (*********** End of global configuration *) (**************************************************************************) (********* ALL THIS SECTION ONLY EVALUATED WHEN LoggingQ = True **********) (**************************************************************************) If[LoggingQ, (* Usage Messages *) Transcript::usage= "Transcript allows changes to the logging process during a session by modifying the value of logTheseCommandsList. Possible arguments: All, None, Default (=defaultLogCommandsList), {}, [Add, {}] to append new items to existing.\n\nTranscript[Status] shows the current logging status."; DirectoryExistsQ::usage= "DirectoryExistsQ[dir] returns True if dir exists, False otherwise."; NumToString::usage= "NumToString[NUM] takes a number as input, and returns a string, adding a leading zero is NUM is a single digit."; TimeNow::usage= "TimeNow[] returns the current time as a string in the form \"HH:MM\"."; RelativeTime::usage= "RelativeTime[dzero] returns the time in the form (M)MM.SS since dzero (which is in Date[] format). Used to get time since logging began."; WriteToLog::usage= "WriteToLog[RECORD] writes the arbitrary expression RECORD to the logging file specified by LogFile in the directory LogFileDir."; BegunNotebook::usage= "BegunNotebook[label_String] inserts a marker in the log file when a Notebook session is begun."; (*********** Functions used in logging *) (* Transcript: to modify the logging action interactively during a session *) Transcript[None] := ( (*log no commands*) $PreRead = .; Return[logTheseCommandsList={}] ); Transcript[All] := ( (*log all commands*) $PreRead = LogAllFunction; Return[logTheseCommandsList = {"*"}] ); Transcript[l_List] := ( (*log given list of commands*) $PreRead = LogSelectiveFunction; Return[logTheseCommandsList = l] ); Transcript[Default] := ( (*log default list of commands*) $PreRead = LogSelectiveFunction; Return[logTheseCommandsList = defaultLogCommandsList] ); Transcript[Add, new_List] := ( (*add new commands to the list*) $PreRead = LogSelectiveFunction; Return[logTheseCommandsList = Join[logTheseCommandsList, new]] ); Transcript[Status] := ( (*display the current list and LogFile*) Print["logfile = ",LogFile]; Return[logTheseCommandsList] ); (* DirectoryExistsQ *) DirectoryExistsQ[dir_String]:=Block[{current,retval},( Off[SetDirectory::cdir]; (*switch off standard failed error message*) current=Directory[]; If[current==dir, (*already there*) Return[True]]; If[SetDirectory[dir]==current, (*call failed to find dir*) retval=False, ResetDirectory[]; retval=True]; On[SetDirectory::cdir]; Return[retval] )]; (* Convert number to string, and add leading zero if necessary *) NumToString[num_]:=Block[{numstring},( numstring=ToString[num]; If[StringLength[numstring]==1,numstring="0"<>numstring]; Return[numstring] )]; (* Define a simple time function, returning string "HH:MM". This function not used currently. *) TimeNow[] := Block[{d},( d=Date[];Return[ NumToString[d[[4]]]<>":"<>NumToString[d[[5]]] ] )]; (* A relative time function: returns time elapsed "(M)MM.SS" since time dzero *) RelativeTime[dzero_]:= Block[{ddiff,mdiff,sdiff},( ddiff=Date[]-dzero; sdiff=Mod[ddiff[[6]],60]; mdiff=60*(Mod[ddiff[[4]],24]+Quotient[ddiff[[5]],60]) +Mod[ddiff[[5]],60]+Quotient[ddiff[[6]],60]; Return[ NumToString[mdiff]<>"."<>NumToString[sdiff] ] )]; (* Logging marker function for beginnings of Notebooks *) BegunNotebook[label_String]:=(WriteToLog[{"begunNB",label}];); (*********** End of function definitions *) (*********** Prompt the user for UserName: A null response is not allowed. We could also check that the response is appropriate - but seems not worthwhile. *) If[UserNameQ, UserName=Block[{tmpstr=""},( While[TrueQ[tmpstr==""], tmpstr=InputString["Please type in your user identity. Ask for\n"<> "help if you don't know what this should be.\n\n"<> "Click the mouse on the OK button to input."] ]; tmpstr)], (*else anonymous logging*) UserName="Anon"]; (*********** End of prompting user *) (* Construct log file name from LogFileDir, NotebookCode and the hour/minute/seconds of timeZeroForLog, and create it. Define the function WriteToLog depending on the operating system - procedures for Unix, Windows95, MSDOS/Windows and Mac are supplied. If you use a different system you will need to make a new entry for it. *) timeZeroForLog=Date[]; If[TrueQ[$OperatingSystem=="Unix"], If[Not[DirectoryExistsQ[LogFileDir]],(*LogFileDir doesn't exist*) If[CreateDirectory[LogFileDir]=!=$Failed, (*create it*) Run["chmod 777",LogFileDir] (*set global rwx permissions*) ]; (* if create has failed then OpenWrite will fail *) ]; LogFile=LogFileDir<>"/"<>NotebookCode<> NumToString[timeZeroForLog[[4]]]<>NumToString[timeZeroForLog[[5]]]<> NumToString[timeZeroForLog[[6]]]<>".log"; If[OpenWrite[LogFile]==$Failed, LogFile={}, (*if cannot write, set output channel to the dustbin*) Run["chmod 666",LogFile] (*set global rw file permissions on log file*) ]; WriteToLog[record_]:= Write[LogFile,{RelativeTime[timeZeroForLog],record}] ]; If[TrueQ[$OperatingSystem=="MSDOS"||$OperatingSystem=="Windows" ||$OperatingSystem=="Windows95"||$OperatingSystem=="WindowsNT"], If[Not[DirectoryExistsQ[LogFileDir]],(*LogFileDir doesn't exist*) directoryStatus=CreateDirectory[LogFileDir]; (*create it*) ]; LogFile=NotebookCode<>NumToString[timeZeroForLog[[4]]]<> NumToString[timeZeroForLog[[5]]]<> NumToString[timeZeroForLog[[6]]]<>".LOG"; If[TrueQ[directoryStatus==$Failed], (*log directory was not created*) WriteToLog[record_]:= Null, (*write no output*) (*else*) WriteToLog[record_]:=( SetDirectory[LogFileDir]; If[TrueQ[OpenAppend[LogFile]==$Failed], Null, (*logfile cannot be written to, send no output*) (*else*) Write[LogFile,{RelativeTime[timeZeroForLog],record}]; Close[LogFile] ]; ResetDirectory[]; ) ] ]; If[TrueQ[$OperatingSystem=="MacOS"], If[Not[DirectoryExistsQ[LogFileDir]],(*LogFileDir doesn't exist*) directoryStatus=CreateDirectory[LogFileDir]; (*create it*) ]; LogFile=LogFileDir<>":"<> NotebookCode<>NumToString[timeZeroForLog[[4]]]<> NumToString[timeZeroForLog[[5]]]<> NumToString[timeZeroForLog[[6]]]<>".log"; If[TrueQ[directoryStatus==$Failed], (*log directory was not created*) WriteToLog[record_]:= Null, (*write no output at all*) (*else*) WriteToLog[record_]:=( If[TrueQ[OpenAppend[LogFile]==$Failed], Null, (*logfile cannot be written to, send no output*) (*else*) Write[LogFile,{RelativeTime[timeZeroForLog],record}]; Close[LogFile] ]; ) ] ]; (* Finally, write the first line of LogFile... *) WriteToLog[{NotebookCode,timeZeroForLog,UserName}]; (* ... and if this is Unix, log the system Username. *) If[TrueQ[$OperatingSystem=="Unix"], WriteToLog[{"SystemUserName",Environment["USER"]}] ]; ] (**************************************************************************) (********************* END OF LoggingQ = True SECTION *********************) (**************************************************************************)