Wednesday, January 23, 2019

#1 2016-01-19 04:28:18 pm

Registered: 2015-04-21
Posts: 7

AutoCopy application code

I wrote an app in AppleScript called 'AutoCopy' and here is the code.
AutoCopy is a simple app that saves a copy of any item in the Finder, as often as you choose. 
Its purpose is to auto-save versions while you're making changes to a file or several files inside a folder.
How to use:  the fastest way is to pick the item you want to autocopy (it can be a single item or folder containing many items) and drag it onto the AutoCopy icon.  You're asked how often (in minutes) you want to save a copy.
Enter a number, and that's it.
Open the below code in AppleScript Editor and save it as application:


Script written in AppleScript Editor vers. 2.6.1, using AppleScript version 2.3.2 in OS X version 10.9.5.
Author: Steve Thompson

--This routine runs when the app is launched by dragging an item onto its icon.
on open draggedItem
   tell application "Finder"
       set chosenItem to draggedItem as alias
   end tell
end open

-- This routine runs if you launch by double-clicking the app.
on run
   tell application "Finder"
       set theResult to display dialog "Want to auto-copy a File or a Folder ?" buttons ¬
           {"Cancel", "Folder", "File"} default button 3
   end tell
   set chosenItem to chooseItem(theResult)
   if chosenItem is false then return --program quits.
end run

on mainRoutine(chosenItem)
   tell application "Finder"
       repeat while kind of chosenItem is "Volume"
           -- will keep repeating until they choose "Cancel" or anything that's not a disk.
           set theResult to display dialog ¬
               "Don't choose a disk! You can choose a File or a Folder." buttons ¬
               {"Cancel", "Folder", "File"} default button 3
           set chosenItem to my chooseItem(theResult)
           if chosenItem is false then return --program quits.
       end repeat
       set {chosenItemName, chosenItemLocation} to {(name of chosenItem), (container of chosenItem)}
       --Ask user how often to make a copy:
       set {copyFrequency, waitTime} to my requestedcopyFrequency()
   end tell -- Because the next line of code must run outside of Finder.
   -- Begin repeat loop of this task: Make new duplicate of chosenItem, name it after current date and time
   --and put it in folder "versions".
   frequencyLoop(chosenItem, chosenItemName, chosenItemLocation, copyFrequency, waitTime)
end mainRoutine

on chooseItem(theResult)
   if button returned of theResult is "Cancel" then return false
   tell application "Finder"
       if button returned of theResult is "Folder" then
           set chosenItem to choose folder with prompt ¬
               "Choose Folder to AutoCopy" default location desktop as alias
       else -- if button returned is "File"
           set chosenItem to choose file with prompt "Choose File to AutoCopy" default location ¬
               desktop as alias
       end if
   end tell
   return chosenItem
end chooseItem

on requestedcopyFrequency()
   set copyFrequency to display dialog "Enter every number of minutes you want to " & return & ¬
       "save a copy:" default answer "1" with title "AutoCopy"
   set waitTime to display dialog ¬
       "AutoCopy will quit automatically if the item hasn't been " & return & ¬
       "modified after a certain number of minutes. Enter the " & return & ¬
       "number of minutes AutoCopy should wait before quitting:" default answer "15" with title "AutoCopy"
   return {(text returned of copyFrequency as integer), (text returned of waitTime as integer)}
end requestedcopyFrequency

on frequencyLoop(chosenItem, chosenItemName, chosenItemLocation, copyFrequency, waitTime)
   set x to 0 --x keeps track of number of minutes it's been since a copy of item was made.
       tell application "Finder"
           --Make sure folder "versions" exists in same folder as specified item.
           if not (exists folder (chosenItemName & "__versions") of chosenItemLocation) then
               set AutoCopyFolder to (make new folder at chosenItemLocation with properties ¬
                   {name:chosenItemName & "__versions"})
               set AutoCopyFolder to folder (chosenItemName & "__versions") of chosenItemLocation
           end if
           set AutoCopyFolder to (AutoCopyFolder as alias)
           my renameLoop(AutoCopyFolder as string, chosenItem)
               set lastModified to modification date of chosenItem
               set theDateTime to my makeDateTime(lastModified)
               set newName to (theDateTime & "__" & chosenItemName as string)
               set AutoCopyFolder to (AutoCopyFolder as string)
               --Only make a new copy if there's not already one with an identical name in AutoCopyFolder.
               if not (exists item (AutoCopyFolder & newName)) then
                   --First check if item with same name as chosenItem already exists in AutoCopyFolder:
                   set duplicateItem to (AutoCopyFolder & chosenItemName) as string
                   if (exists item duplicateItem) then
                       set duplicateItem to (duplicateItem as alias)
                       set lastModified to modification date of duplicateItem
                       --Prepare to change its name to its modification date:
                       set theDateTime to my makeDateTime(lastModified)
                       --But if an item with exact same modification date exists, get rid of duplicate:
                       if exists item (AutoCopyFolder & theDateTime & "__" & chosenItemName) then
                           delete item duplicateItem
                       else --change duplicate's name to modification date:
                           set name of duplicateItem to (theDateTime & "__" & chosenItemName)
                       end if
                   end if
                   --Make a new copy, but only if item with same modification date doesn't exist:
                   if not (exists item (AutoCopyFolder & newName)) then
                       copy item chosenItem to folder (AutoCopyFolder as string)
                       set duplicateItem to result as alias
                       set name of duplicateItem to newName
                       set x to 0
                   end if
               end if
           on error
               my itemDoesntExistMessage()
               return --program quits.
           end try
       end tell
       -- pause script for ((number of minutes user specified) * 60) seconds:
       repeat copyFrequency times
           delay 60
           set x to (x + 1) --x gets incremented plus-one for every minute.
           if x ≥ waitTime then return --program quits.
       end repeat
   end repeat
end frequencyLoop

--Make function that will run at beginning of every loop iteration in frequencyLoop.
--It checks modification dates of every item in a folder, and renames each
--item after the modification date. If multiple items share a mod date (and are same filetype), only one can remain.
on renameLoop(folderStringPath, chosenItem)
   tell application "System Events"
       --get every visible item in the folder:
       set theItems to every item of item folderStringPath whose visible is true
       set modDatesAndExtensions to {}
       repeat with i in theItems
           set end of modDatesAndExtensions to ¬
               {modified:modification date of i, name extension:¬
                   name extension of i}
       end repeat
       --Find out if any items match, both in mod date and extension, and if so, keep only one:
       set removedItems to item 2 of my removeDuplicates(modDatesAndExtensions)
       if (count of removedItems) > 0 then
           repeat with i in removedItems
               delete (item i of theItems)
           end repeat
       end if
       set theItems to every item of item folderStringPath whose visible is true
       --Make sure the names of the items all begin with mod dates:
       repeat with i in theItems
           set theDateTime to my makeDateTime(modification date of i)
           set formattedNm to (theDateTime & "__" & chosenItem's name as string)
           if ((i's name does not start with theDateTime) or ("copy" is in i's name)) and ¬
               (i's name extension is chosenItem's name extension) then
               set name of i to formattedNm
           else if (i's name does not start with theDateTime) then
               set name of i to (theDateTime & "__" & (i's name) as string)
           end if
       end repeat
   end tell
   --return modDatesAndExtensions
end renameLoop

--Returns list of two items: first is modified version of lst with duplicates removed.
--Second item is list of indexes of removed items.
on removeDuplicates(lst)
   local lst, itemRef, res, itm
       if lst's class is not list then error "not a list." number -1704
       script k
           property l : lst
           property res : {}
           property indx : {}
       end script
       repeat with i from 1 to count of k's l
           set itm to (item i of k's l)
           if k's res does not contain {itm} then
               set k's res's end to itm
               set k's indx's end to i
           end if
       end repeat
       return {k's res, k's indx}
   on error eMsg number eNum
       return false
   end try
end removeDuplicates

--This function returns a specially formatted date-time string, i.e, "140211-201412"
on makeDateTime(dateObject)
   set shortString to short date string of dateObject
   set theList to explode(shortString, "/")
   if (count of (LI(1, theList))) = 1 then
       setLI(1, theList, ("0" & (item 1 of theList)))
   end if
   if (count of (item 2 of theList)) = 1 then
       setLI(2, theList, ("0" & (item 2 of theList)))
   end if
   set theDate to (item 3 of theList) & (item 1 of theList) & (item 2 of theList)
   set AppleScript's text item delimiters to ""
   set theTime to time of dateObject
   set theHour to (round (theTime / 3600) rounding down) as string
   set hourInt to theHour as integer
   set hourSecs to (hourInt * 3600)
   set minSecs to ((theTime - hourSecs) / 60)
   set theMin to (round minSecs rounding down) as string
   set minInt to theMin as integer
   set minSecs to (minInt * 60)
   set theSec to (theTime - hourSecs - minSecs) as string
   if ((count of theHour) = 1) then set theHour to ("0" & theHour)
   if ((count of theMin) = 1) then set theMin to ("0" & theMin)
   if ((count of theSec) = 1) then set theSec to ("0" & theSec)
   return (theDate & "-" & theHour & theMin & theSec) as string
end makeDateTime

on itemDoesntExistMessage()
   display dialog ¬
       "The item no longer exists. Either its name changed or it was deleted. I'm quitting. Bye." with title ¬
       "AutoCopy" buttons {"OK"} default button 1 giving up after 10
end itemDoesntExistMessage

--This function is just for creating a short-hand way of accessing a list item.
--ItemNum can be a single integer, or a list of two integers for accessing a range of items:
on LI(itemNum, theList)
   if class of itemNum is integer then
       return (item itemNum of theList)
   else if class of itemNum is list then
       return (items (item 1 of itemNum as integer) thru ¬
           (item 2 of itemNum as integer) of theList)
   end if
end LI

--This function is for assigning a value to a list item:
on setLI(itemNum, theList, theValue)
   set item itemNum of theList to theValue
end setLI

-- This function separates pieces of a string into list items, using theDelimit
-- as the separator:
on explode(theString, theDelimit)
   set origDelimit to AppleScript's text item delimiters
   set AppleScript's text item delimiters to theDelimit
   set theResult to every text item of theString
   set AppleScript's text item delimiters to origDelimit
   return theResult
end explode

--This function re-assembles a list of strings into a single string,
--using theDelimit as glue to reconnect each string.
on implode(textList, theDelimit)
   set origDelimit to AppleScript's text item delimiters
   set AppleScript's text item delimiters to theDelimit
   set theString to (textList as string)
   set AppleScript's text item delimiters to origDelimit
   return theString
end implode

Last edited by tootall8888 (2016-01-19 04:30:37 pm)



#2 2017-03-19 08:08:57 am

From:: Utrecht, NL
Registered: 2008-09-12
Posts: 551

Re: AutoCopy application code

The script fails in any locale that does not use the / as a delimiter - which is most of them, I think.

Here's a bit of locale-agnostic code to get the datestamp:


set dateObject to (current date)
set sep to "-"

tell dateObject to set {year:y, month:m, day:d, hours:hh, minutes:mm, seconds:ss} to it

set dateStamp to (y * 10000 + m * 100 + d) as text
set timeStamp to (hh * 10000 + mm * 100 + ss) as text
if (count timeStamp) = 5 then set timeStamp to "0" & timeStamp
return dateStamp & sep & timeStamp



Board footer

Powered by FluxBB

RSS (new topics) RSS (active topics)