1
votes

I'm trying to create an applescript that prompts for a job name, then a desired directory in which to create a folder with the job name. It then should create a series of subfolders within the initial folder with names such as "job name-Links" & "job name-PDFs" etc.

I've cobbled this script together from different sources & would like feedback on how to improve it, because I'm sure it's ugly as sin.

tell application "Finder"
activate

set jobNum to text returned of (display dialog "Enter a job number:" default answer "[JobNumber]-[Description]")
set folderpath to POSIX path of (choose folder with prompt "Select client folder")
do shell script "/bin/mkdir -p " & quoted form of folderpath & "/" & quoted form of (jobNum) & "/" & quoted form of (jobNum & "-Links")
do shell script "/bin/mkdir -p " & quoted form of folderpath & "/" & quoted form of (jobNum) & "/" & quoted form of (jobNum & "-PDFs")
do shell script "/bin/mkdir -p " & quoted form of folderpath & "/" & quoted form of (jobNum) & "/" & quoted form of (jobNum & "-Supplied")
do shell script "/bin/mkdir -p " & quoted form of folderpath & "/" & quoted form of (jobNum) & "/" & quoted form of (jobNum & "-Versions")

end tell

Any thoughts & comments would be greatly appreciated. This is my first attempt at a script. I thought I'd post this because I've hunted for this specific use case for ages and only found bits and pieces. Hopefully someone can help me out so other will find it useful too.

7

7 Answers

0
votes

Another idea would be to create a template of the desired folder structure somewhere, then have the script copy that structure with the client prefix. A couple of benefits to this method are that you don't need to change the script if folders are added to the template (the handler will also add to existing structures), and you don't have to figure out how to script complex (or large) structures.

property template : "/path/to/template/folder" -- the folder structure to copy from


on run -- example
    set jobNum to text returned of (display dialog "Enter a job number:" default answer "[JobNumber]-[Description]")
    set folderPath to (choose folder with prompt "Select a location for the client folder")
    tell application "Finder" to try
        set clientFolder to (make new folder at folderPath with properties {name:jobNum})
    on error number -48 -- folder already exists
        set clientFolder to (folderPath as text) & jobNum
    end try
    copyFolderStructure_toFolder_withPrefix_(template, clientFolder, jobNum)
end run


to copyFolderStructure_toFolder_withPrefix_(source, destination, additions) -- copy folder structure using mkdir
    set {source, destination} to {source as text, destination as text}
    if source begins with "/" then set source to POSIX file source
    if destination begins with "/" then set destination to POSIX file destination
    set {source, destination} to {source as alias, destination as alias}

    set additions to additions as text
    tell application "Finder" to set theFolders to (folders of entire contents of source) as alias list
    set folderNames to ""

    repeat with someFolder in theFolders -- set up the folder names parameter for the shell script
        set {tempTID, AppleScript's text item delimiters} to {AppleScript's text item delimiters, ":"}
        set namePieces to text items of (text ((count (source as text)) + 1) thru -2 of (someFolder as text))
        set AppleScript's text item delimiters to ("/" & additions)
        set namePieces to space & quoted form of (additions & (namePieces as text))
        set AppleScript's text item delimiters to tempTID
        set folderNames to folderNames & namePieces
    end repeat
    do shell script "cd " & quoted form of POSIX path of destination & "; mkdir -p" & folderNames
end copyFolderStructure_toFolder_withPrefix_
0
votes

You can do something like this:

      set TID to AppleScript's text item delimiters

-- Ensure the answer is formatted properly
repeat
    try
        set theAnswer to text returned of (display dialog "Enter a job number:" default answer "[JobNumber]-[Description]")
        set AppleScript's text item delimiters to "-"
        set {jobNum, jobDes} to {text item 1 of theAnswer, text item 2 of theAnswer}
        exit repeat
    on error
        display dialog "Please enter your response in this format [JobNumber]-[Description]" buttons {"Cancel", "Try again"} default button "Try again"
    end try
end repeat

set folderpath to POSIX path of (choose folder with prompt "Select client folder")

set folderNames to {"-Links", "-PDFs", "-Supplied", "-Versions"}
repeat with aName in folderNames
    do shell script "mkdir -p " & quoted form of (folderpath & jobNum & "-" & jobDes & "/" & jobNum & "-" & jobDes & aName as text)
end repeat

set AppleScript's text item delimiters to TID
0
votes

mkdir can create a hierarchy, just put these names in the {} Example : '/destFolderPath/jobName/prefix'{"suffix1","suffix2","suffix3","suffix4"}

Here is the script.

set jName to my getJobNum()
set folderpath to POSIX path of (choose folder with prompt "Select client folder")
do shell script "mkdir -p " & (quoted form of (folderpath & jName & "/" & jName)) & "{\"-Links\",\"-PDFs\",\"-Supplied\",\"-Versions\"}"

on getJobNum()
    activate
    text returned of (display dialog "Enter a job number:" default answer "[JobNumber]-[Description]")
    tell the result to if it is not "" then return it
    getJobNum() -- else text returned is empty
end getJobNum

Important, if you plan to have slashes in the name, you must replace them by the colon character, it's easy to add a handler in the script to replace them, otherwise it will create other subfolders.

0
votes

I'm no scripter, but I was looking for the same kind of structured folder solution. After going over these other examples, I came up with a slightly modified version for setting up project folders for our group of designers.

set TID to AppleScript's text item delimiters

-- Ensure the answer is formatted properly
repeat
    try
        set theAnswer to text returned of (display dialog "Please create a job folder, with the name in this format:" default answer "JobNumber-JobCode-ClientDescription")
        set AppleScript's text item delimiters to "-"
        set {jobNum, JobCod, CliDes} to {text item 1 of theAnswer, text item 2 of theAnswer, text item 3 of theAnswer}
        exit repeat
    on error
        display dialog "Please enter your job details in this format [JobNumber]-[JobCode]-[ClientDescription]" buttons {"Cancel", "Try again"} default button "Try again"
    end try
end repeat

set folderpath to POSIX path of (choose folder with prompt "Where to create your project folder?")

set folderNames to {"Links", "Client_input", "SourceBuilds", "Review-Proofs"}
repeat with aName in folderNames
    do shell script "mkdir -p " & quoted form of (folderpath & jobNum & "-" & JobCod & "-" & CliDes & "/" & aName as text)
end repeat

set AppleScript's text item delimiters to TID
0
votes

I'm using a variant of jackjr300 script to generate a website folder template/

do shell script "mkdir -p " & (quoted form of (folderpath & jName & "/")) & "{\"codey\",\"js\",\"fonts\",\"images\"}"

Plus another run to generate the subfolders and populate those with an existing frameworks and scripts with a duplicate folder action.

Very handy Thanks ...!

0
votes

The first thing you could do to improve your script is don’t use shell scripting. Shell scripting is like a jackhammer, but what you are doing only requires the tiniest hand-held hammer.

Here is your original script:

tell application "Finder"
activate
set jobNum to text returned of (display dialog "Enter a job number:" default answer "[JobNumber]-[Description]")
set folderpath to POSIX path of (choose folder with prompt "Select client folder")
do shell script "/bin/mkdir -p " & quoted form of folderpath & "/" & quoted form of (jobNum) & "/" & quoted form of (jobNum & "-Links")
do shell script "/bin/mkdir -p " & quoted form of folderpath & "/" & quoted form of (jobNum) & "/" & quoted form of (jobNum & "-PDFs")
do shell script "/bin/mkdir -p " & quoted form of folderpath & "/" & quoted form of (jobNum) & "/" & quoted form of (jobNum & "-Supplied")
do shell script "/bin/mkdir -p " & quoted form of folderpath & "/" & quoted form of (jobNum) & "/" & quoted form of (jobNum & "-Versions")
end tell

Compare to a “de-shelled” version, which is easier to read, write, and maintain:

tell application "Finder"
    activate
    set jobNum to text returned of (display dialog "Enter a job number:" default answer "[JobNumber]-[Description]")
    set theClientFolder to (choose folder with prompt "Select client folder")
    set theSubFolderNames to {"Links", "PDFs", "Supplied", "Versions"}
    repeat with theSubFolderName in theSubFolderNames
        make new folder at theClientFolder with properties {name:jobNum & "-" & theSubFolderName}
    end repeat
end tell

Notice that the variable “folderpath” has changed to “theClientFolder” because choose folder does not return a path, it returns an alias object for the chosen folder. The command for telling Finder to make a new folder is “make new folder at [alias where you want the folder to be made]” so you don’t need to convert that alias into a path to make new folders inside of it.

Notice also that the names of the folders have been declared as variables in a list, which makes them easier to read or change later. Even somebody who does not know AppleScript could open this script and change the name of the “PDFs” folder to “Documents” or something like that.

Another improvement I could suggest would be to make the way that you’re gathering the input from the user a little more sophisticated. You seem to be asking for 2 items of information other than the client folder — job number and job description — so use 2 dialog boxes, because that will be easier for the user and less error-prone because you can give 2 actual examples of what you want them to input. You can also improve the appearance of those dialogs in a few ways. And you can add a little error handling — if the user cancels the dialogs you’re presenting, that generates errors. You can handle them gracefully by checking if the user clicked “OK” or not.

Here is a version of your script with the user input cleaned up a bit:

tell application "Finder"
    activate
    display dialog "Enter a job number:" default answer "100" buttons {"Cancel", "OK"} default button "OK" with title (the name as text) with icon note giving up after 180
    if the button returned of the result is equal to "OK" then
        set theJobNumber to the text returned of the result
        display dialog "Enter a job description:" default answer "Photos" buttons {"Cancel", "OK"} default button "OK" with title (the name as text) with icon note giving up after 180
        if the button returned of the result is equal to "OK" then
            set theJobDescription to the text returned of the result
            try
                choose folder with prompt "Select client folder:" default location (the path to the desktop folder as alias) without multiple selections allowed, invisibles and showing package contents
                set theClientFolder to the result
            on error
                set theClientFolder to ""
            end try
            if theClientFolder is not equal to "" then
                set theSubFolderNames to {"Links", "PDFs", "Supplied", "Versions"}
                repeat with theSubFolderName in theSubFolderNames
                    make new folder at theClientFolder with properties {name:theJobNumber & "-" & theJobDescription & "-" & theSubFolderName}
                end repeat
            end if
        end if
    end if
end tell

This will give you dialog boxes that say “Finder” at the top and include the Finder icon, which will give up after 180 seconds (to prevent at timeout error) and which, if the user presses “Cancel” at any point, will just die gracefully without generating any errors. You can tweak the default answers for the input dialogs so that they help the user put in the right input, for example, if job numbers are 4 digits, put a 4 digit number here. And the choose folder prompt will start by showing the Desktop — you can change that to another location if there is a disk or folder where you store all your client folders:

choose folder with prompt "Select client folder:" default location (disk "Clients" as alias) …

To be clear, shell scripting is awesome, but I recommend you only use “do shell script” to get at unique features of the UNIX layer, like PHP functions, Perl regex, and so on. Anything that is already available to you in the Mac layer is going to be easier and safer to use, and easier to read, write, and maintain in AppleScript. Especially really basic things like creating folders.

0
votes

I have a solution that copies the folder from a template location. It also creates a shortcut that allows to email the design manager with the subject as the job number/project number

global jobNum
global newJobFolder
set jobNum to text returned of (display dialog "Enter a job number:" default answer "")
set jobName to text returned of (display dialog "Enter a job name:" default answer "")
set jobMgr to text returned of (display dialog "Enter account manager email:" default answer "")
set folderpath to (choose folder with prompt "Select client folder")
set newJobFolder to my newFold(jobNum, jobName, folderpath, jobMgr)


on newFold(theNumber, theName, thefolder, jobMgr)
    set emailAddress to jobMgr
    set emailSubject to theNumber & " -" & theName
    set bodyText to ""
    set emailLinkFileName to theNumber & " -" & theName

    set subNameList to {"Designs", "Documents", "Received"}
    set itemCount to count of subNameList
    set linkBody to "mailto:" & emailAddress & "?subject=" & my 
replace_chars(emailSubject, " ", "%20") & "&body=" & my 
replace_chars(bodyText, " ", "%20")
    tell application "Finder"
        set newJobFolder to (make new folder at thefolder with properties ¬
            {name:theNumber & " " & theName})
        repeat with i from 1 to itemCount
            set aSubFolder to make new folder at newJobFolder with properties {name:jobNum & " " & item i of subNameList}
            set theMailto to make new internet location file to linkBody at aSubFolder with properties {name:emailLinkFileName, name extension:"mailloc"}
            set the name extension of theMailto to "mailloc"
        end repeat
        duplicate file "Users:ace:Dropbox:Company:0000-1_E.xlsx" of startup disk to folder (jobNum & " Documents") of newJobFolder
        set name of the result to jobNum & " E.xlsx"
        set theMailto to make new internet location file to linkBody at newJobFolder with properties {name:emailLinkFileName, name extension:"mailloc"}
        set the name extension of theMailto to "mailloc"
    end tell
end newFold

-- Generic find and replace functionality
on replace_chars(this_text, search_string, replacement_string)
    set AppleScript's text item delimiters to the search_string
    set the item_list to every text item of this_text
    set AppleScript's text item delimiters to the replacement_string
    set this_text to the item_list as string
    set AppleScript's text item delimiters to ""
    return this_text
end replace_chars