Applescript to find Offline Files

Discussion regarding all scripting related questions
Please DO NOT post to this thread anything that is not directly related to scripting of Capture One.

Applescript to find Offline Files

Postby Eric Nepean » Sat Dec 28, 2019 7:51 pm

Here is an Applescript to search a Catalog for Images with offline files.
Sessions are not supported. It is not yet tested for Capture One 20, but I expect it will run just fine. The minimum Capture One version is version 12.1

If you have 1000's of images or 10's of 1000';s of images in a catalog, then finding items with missing files by inspection is tedious and inaccurate, and this script becomes useful.

Once files have been identified as missing, then other actions are needed. These actions are not included in the AppleScript.
  • Check if the missing files are on a drive which is not connected. Easy Fix.
  • The file may have been accidently deleted. Check OS X Trash. Check Capture One Trash.
  • The file may have been moved. Use the search bar in OSXs Finder to locate the file
  • Failing that, open Terminal and execute: sudo find -x / -iname "*thefilename*"
  • Failing that, use Time Machine to retrieve the file
This AppleScript can report results by Dialog, by Notifications, through Text Edit, onthe ClipBoard, and by adding the images to a Capture One collection.These options and settings can be configureed at the top of the Applescript. (
I have removed the GUI that was present in the previous version)

This Applescript can be compiled as an Application and added to the Capture One Scripts menu.

Previous version
https://forum.phaseone.com/En/viewtopic.php?f=70&t=28891&p=139609#p139609

Code: Select all
## Applescript to search a Capture One Catalog for Images with offline files
## Version 12.00 !! NO GUARANTEE OF SUPPORT !!  Best effort
## Version with no GUI
## Copyright 2019 Eric Valk, Ottawa, Canada   Creative Commons License CC BY-SA    No Warranty.

-- ***To Setup
-- Start Script Editor, open a new (blank) file, copy and paste both parts into one Script Editor Document, compile (hammer symbol) and save.
-- Best if you make "Scripts" folder somewhere in your Documents or Desktop
-- This file is suitable to use as an application in Capture One Pro's Script Menu

-- *** Operation in Script Editor
-- Open  the compiled and saved document
-- Open the Script Editor log window, and select the messages tab
-- The user may elect to set defaults for enabling or disabling results in Notifications, TextEdit and Script Editor by setting the "enable" variables at beginning of the script
-- The user may change the default amount of reporting by setting the "debugLogLevel" and "ResultsFileMaxDebug" variables at beginning of the script
-- There is a GUI which allows the user to modify settings
-- If you are having some issues, then set debugLogLevel to 3 and send me the results from Script Editors log window, or Text Edit.

## Values in this section are safe to change, within limits indicated. Support is likely but no commitment

use AppleScript version "2.5"
use scripting additions

set debugLogLevel to 0 --            0...6 Values >1 result in increasing amounts of debug data that takes longer to report
set enableResultsFile to true --         (true/false)
set enableResultsByDialog to false --      (true/false)
set enableResultsByClipboard to false --   (true/false)
set enableNotifications to true --      (true/false)
set enableResultsInCollection to false --     (true/false)
set ResultsFileMaxDebug to 2 --         1...6  suggest not more than 2
set SearchAllImages to false --                If set to true, then only "all Images" is searched, If false, the current user collection is searched
set ExcludedSubCollectionNames to {} -- Collections in this list will not be searched
set maxSearchlevel to 100 --         Reduce if you only want to search top level collections Range 1.... Verified to 100
set enableFastGUI to true --             (true/false)


## ***** Not safe to change stuff below this line, unless you have some background in SW development.
## I generally won't help much if you change stuff below this line. I may explain the design intent.

tell application "System Events" to set parent_name to name of current application

set Script_Title to (get name of me)

set SE_Parent to (parent_name = "Script Editor")
set SE_Logging to SE_Parent as boolean
set COPisParent to (get parent_name begins with "Capture One") -- version independent
set Result_DocName to "COP_Image_Search.txt"
set Result_AlbumRoot to "NotInUserCollection"
set Result_ProjectName to "ScriptSearchResults"

set ResultMethod to my InitializeLoqqing3(Result_DocName, Script_Title)
loq_Results2(0, false, ("Started from: " & parent_name & "  Action: Find images which refer to a missing file"))

set minCOPversion to "12.1"
set maxCOPversion to "12"
validateCOP2(minCOPversion, maxCOPversion)
validateCOPdoc3({"catalog"})
validateCOPcollections3()
tell application "Capture One 12"
   set countCollectionImages to count of images -- images in the current collection
   tell document COPDocName to tell collection "All Images" to set count_All_Images to get count of every image --variants in the All Images Collection
end tell

if not selectedCollectionIsUser then set SearchAllImages to true

if enableResultsInCollection then
   set Result_AlbumRoot to "OfflineFile"
   set Result_ProjectName to "ScriptSearchResults"
   set Coll_Init_Text to "Images with offline files"
   InitializeResultsCollection(Result_ProjectName, Result_AlbumRoot, Coll_Init_Text)
end if

set Mark1 to GetTick_Now()


tell application "System Events" to set frontmost of process theAppName to true

## Start the search

if SearchAllImages then
   tell application "Capture One 12" to tell COPDocRef
      set thisCollectionRef to get the collection "All Images"
      set thisColl_name to (get name of thisCollectionRef) as text
      set thisCollKind to my convertKindList((get kind of thisCollectionRef))
   end tell
   
   set countProcessedImages to count_All_Images
else
   tell application "Capture One 12" to tell COPDocRef
      set thisCollectionRef to current collection
      set thisColl_name to (get name of thisCollectionRef) as text
      set thisCollKind to my convertKindList((get kind of thisCollectionRef))
   end tell
   set countProcessedImages to countCollectionImages
end if

loq_Results2(3, false, ("In " & thisCollKind & " " & thisColl_name & " " & countProcessedImages & " Images"))

set nextSearchLevel to 1
set All_Found to true
set countImageNotFound to 0
set Coll_path to ">" & thisColl_name

set Mark2 to GetTick_Now()

if 0 < countProcessedImages then
   set progress description to "Searching " & thisCollKind & " " & thisColl_name
   if enableNotifications then display notification "Searching " & thisCollKind & " " & thisColl_name
   set All_Found to my search_collection(thisCollectionRef, nextSearchLevel, Coll_path)
   if All_Found then
      my loq_Results2(0, false, "No Images with offline files found")
   else
      my loq_Results2(0, false, "Found " & countImageNotFound & " Images with offline or missing files")
   end if
else
   loq_Results2(0, true, ("There are no Images in " & thisCollKind & " " & thisColl_name & " - unable to proceed "))
end if

set Mark3 to GetTick_Now()


tell application "System Events" to set frontmost of process theAppName to true
tell current application to set date_string to (current date) as text

set elapsedTime1s to roundDecimals(Mark2 - Mark1, 3)
set elapsedTime2s to roundDecimals(Mark3 - Mark2, 3)

if countProcessedImages > 0 then
   set searchTimePerVariant to get ((Mark3 - Mark2) / countProcessedImages)
   set searchTimePerVariantDisplay to roundDecimals(searchTimePerVariant, 3)
else
   set searchTimePerVariantDisplay to "--"
end if

loq_Results2(1, false, (return & "SetupTime: " & elapsedTime1s & "sec   Process time: " & elapsedTime2s & "sec   per Image: " & searchTimePerVariantDisplay & "sec"))

loq_Results2(0, true, (return & "*** Done on " & date_string & " ***  " & countImageNotFound & " of " & countProcessedImages & " images have offline files" & return & return))

finalCleanup() -- cleanup large arrays to avoid the situation where the script cannot be saved

## Arrange the windows to show results on top
tell application "System Events" to set frontmost of process theAppName to true

if enableResultsFile then
   tell application "System Events" to set frontmost of process "TextEdit" to true
else
   tell application "System Events" to set frontmost of process parent_name to true
end if


## Script Specific Handlers #######

on search_collection(thisCollection, searchLevel, thisCollPath)
   -- recursive handler to search a collection and it's subcollections
   
   global debugLogLevel, enableResultsByDialog, maxSearchlevel, COPDocName, COPDocRef, countImageNotFound, enableResultsInCollection, ref2ResultAlbum
   set nextSearchLevel to searchLevel + 1
   
   tell application "Capture One 12" to tell thisCollection
      set thisCollKind to my convertKindList(kind)
      set everyImagePathList to get path of every image
   end tell
   
   
   loq_Results2(2, false, ("Searching " & thisCollKind & "  " & thisCollPath & ":"))
   set loc_Text to "In " & thisCollKind & " " & thisCollPath & ":"
   set first_Hit to true
   set All_Found to true
   set everyImageCount to count of everyImagePathList
   
   repeat with ImageCounter from 1 to everyImageCount
      set imagepath to item ImageCounter of everyImagePathList
      tell application "System Events" to if not (get exists file imagepath) then
         if first_Hit then my loq_Results2(0, false, (return & loc_Text))
         set first_Hit to false
         tell application "Capture One 12" to tell COPDocRef to tell thisCollection to tell image ImageCounter
            set imageName to name
            if enableResultsInCollection then add inside ref2ResultAlbum variants (get variants)
         end tell
         my loq_Results2(0, false, ("File for " & imageName & " not found at " & imagepath))
         set countImageNotFound to countImageNotFound + 1
         set All_Found to false
      end if
   end repeat
   loq_Results2(2, false, ("Done " & thisCollKind & "  " & thisCollPath & " "))
   
   if thisCollKind ≠ "project" then -- do not search collections contained inside a project to avoid repeated "hits" of the same image
      tell application "Capture One 12" to tell COPDocRef to set subCollections to (get every collection of thisCollection)
      set nextSearchLevel to searchLevel + 1
      if nextSearchLevel ≤ maxSearchlevel then
         repeat with searchCollection in subCollections
            tell application "Capture One 12" to tell COPDocRef to set nextCollName to (get name of searchCollection) as text
            set progress description to "Searching " & thisCollKind & " " & nextCollName
            if enableNotifications then display notification "Searching " & thisCollKind & " " & thisColl_name
            set nextCollPath to thisCollPath & ">" & nextCollName
            set All_Found_here to my search_collection(searchCollection, nextSearchLevel, nextCollPath)
            set All_Found to All_Found and All_Found_here
         end repeat
      end if
   end if
   
   return All_Found
end search_collection

on finalCleanup()
   ## clean up the large arrays to avoid a large stack that may prevent AppleScript from saving the script
   global everyImagePathList, everyImageNameList, everyTopCollection, namesTopCollections, namesTopCollections, kindsTopCollections_p, search_name_list
   ## Cleanup Memory to avoid Script Editor having a stack overflow error on saving; this data will be dirty on the next run anyway
   set everyImageNameList to {}
   set everyImagePathList to {}
   set everyTopCollection to {}
   set namesTopCollections to {}
   set kindsTopCollections_p to {}
end finalCleanup


###########################################################################################################################
## Capture One General Handlers  Version 2019/12/28

on validateCOP2(minCOPversionstr, maxCOPversionstr)
   ## Copyright 2019 Eric Valk, Ottawa, Canada   Creative Commons License CC BY-SA    No Warranty.
   ## General purpose initialisation handler for scripts using Capture One Pro
   ## Extract and check basic information about the Capture One application
   
   global debugLogLevel, theAppName, copVersion, copDetailedVersion, enableNotifications
   tell application "System Events"
      set COPProcList to every process whose name begins with "Capture One" and background only is false
      if debugLogLevel ≥ 2 then
         set COPProcNameList to name of every process whose name begins with "Capture One" and background only is false
         my loq_Results2(2, false, ("Capture One Processes:" & COPProcNameList))
      end if
   end tell
   if (count of COPProcList) = 0 then my loqqed_Error_Halt3(true, "Capture One is not running")
   if (count of COPProcList) ≠ 1 then my loqqed_Error_Halt3(true, "Unexpected: >1 Capture One instances")
   set theAppRef to item 1 of COPProcList
   tell application "System Events" to set theAppName to ((get name of theAppRef) as text)
   tell application "System Events" to set copDetailedVersion to get version of my application theAppName
   
   tell application "Capture One 12" to set copVersion to (get app version)
   
   if debugLogLevel ≥ 2 then
      tell application "System Events"
         my loq_Results2(2, false, ("All Processes: " & (get my joinListToString((get name of every process whose background only is false), ", "))))
      end tell
      loq_Results2(2, false, ("theAppName: " & theAppName))
      loq_Results2(2, false, ("Capture One Version: " & copVersion))
      loq_Results2(2, false, ("Capture One Detailed Version: " & copDetailedVersion))
   end if
   
   set numCOPversion to (splitStringToList((word -1 of copVersion), "."))
   set minCOPversion to (splitStringToList(minCOPversionstr, "."))
   set maxCOPversion to (splitStringToList(maxCOPversionstr, "."))
   
   set digit_mult to 1000000
   set Version_digit to 0
   repeat with dig_ctr from 1 to count of numCOPversion
      set digit_mult to digit_mult / 100
      set Version_digit to Version_digit + (get item dig_ctr of numCOPversion as integer) * digit_mult
   end repeat
   
   set digit_mult to 1000000
   set min_digit to 0
   repeat with dig_ctr from 1 to count of minCOPversion
      set digit_mult to digit_mult / 100
      set min_digit to min_digit + (get item dig_ctr of minCOPversion as integer) * digit_mult
   end repeat
   
   set digit_mult to 1000000
   set max_digit to 0
   set digit_count to count of maxCOPversion
   repeat with dig_ctr from 1 to digit_count
      set digit_mult to digit_mult / 100
      set max_digit to max_digit + (get item dig_ctr of maxCOPversion as integer) * digit_mult
      if (dig_ctr = digit_count) then set max_digit to max_digit + digit_mult
   end repeat
   
   if (Version_digit < min_digit) then
      if enableNotifications then display notification "This Capture One Version is unsupported"
      my loqqed_Error_Halt3(true, "This Capture One Version is " & copDetailedVersion & " - the minimum supported Capture One version is " & minCOPversionstr)
   end if
   
   if (Version_digit ≥ max_digit) then
      if enableNotifications then display notification "This Capture One Version is untested"
      loq_Results2(0, true, "This Capture One Version is " & copDetailedVersion & " - versions above " & maxCOPversionstr & " are not verified yet, but are likely OK")
   end if
   
   tell application "System Events" to set frontmost of process theAppName to true
   loq_Results2(1, false, ("Capture One version: " & copDetailedVersion))
end validateCOP2

on validateCOPdoc3(COP_kind_list)
   ## Copyright 2019 Eric Valk, Ottawa, Canada   Creative Commons License CC BY-SA    No Warranty.
   ## General purpose initialisation handler for scripts using Capture One Pro
   ## Extract and check basic information about the current document
   
   global debugLogLevel, COPDocName, COPDocKind_s, COPDocRef, theAppName
   
   try
      tell application "Capture One 12" to set COPDocName to get name of current document
   on error
      loqqed_Error_Halt3(true, "The Script could not retrieve the Capture One document - Perhaps a Capture One Dialog window is open?")
   end try
   
   tell application "Capture One 12"
      set current_doc_kind_p to (get kind of current document)
      set current_doc_ref_list to (get every document whose name is COPDocName and kind is current_doc_kind_p)
      set number_of_hits to count of current_doc_ref_list
   end tell
   set COPDocKind_s to convertKindList(current_doc_kind_p)
   
   loq_Results2(2, false, ("Found Documents: " & number_of_hits))
   
   if COP_kind_list does not contain COPDocKind_s then loqqed_Error_Halt3(true, (COPDocName & " is a " & COPDocKind_s & " -- this script does not support " & COPDocKind_s & "s"))
   
   if number_of_hits = 0 then
      loqqed_Error_Halt3(false, "Could not find find " & COPDocKind_s & COPDocName)
      error "Could not find find " & COPDocKind_s & COPDocName
   else if number_of_hits > 1 then
      loqqed_Error_Halt3(false, "Found more than one " & COPDocKind_s & " with the name " & COPDocName)
      error "Found more than one " & COPDocKind_s & " with the name " & COPDocName
   else
      tell application "Capture One 12" to set COPDocRef to item 1 of current_doc_ref_list
   end if
   
   loq_Results2(1, false, ("CO Document: " & COPDocKind_s & " " & COPDocName))
end validateCOPdoc3

on validateCOPcollections3()
   ## Copyright 2019 Eric Valk, Ottawa, Canada   Creative Commons License CC BY-SA    No Warranty.
   ## General purpose initialisation handler for scripts using Capture One Pro
   ## Extract basic information regarding the current collection, and thhe top level collections
   global debugLogLevel, COPDocName, COPDocKind_s, COPDocRef, enableNotifications
   global everyTopCollection, namesTopCollections, kindsTopCollections_s, countTopCollections, selectedCollectionRef, selectedCollectionIndex, kindSelectedCollection_s, nameSelectedCollection
   global selectedCollectionMirroredAtTopLast, selectedCollectionIsUser, bottomUserCollectionIndex, topUserCollectionIndex
   -- selectedCollectionMirroredAtTopLast replaces selectedCollectionAtTopEnd
   -- bottomUserCollectionIndex, topUserCollectionIndex replaces indexInCatalog
   
   tell application "Capture One 12" to tell COPDocRef
      set selectedCollectionRef to get current collection
      if (missing value = selectedCollectionRef) then
         set current collection to collection 1
         set selectedCollectionRef to get current collection
      end if
      set {nameSelectedCollection, selectedCollectionIsUser} to {name, user} of selectedCollectionRef
      set kindSelectedCollection_s to my convertKindList(kind of selectedCollectionRef)
      set {everyTopCollection, namesTopCollections, userTopCollections} to {it, name, user} of every collection
      set kindsTopCollections_s to my convertKindList(kind of every collection)
   end tell
   set countTopCollections to count of namesTopCollections
   
   repeat with collectionCounter from 1 to countTopCollections
      if (nameSelectedCollection = item collectionCounter of namesTopCollections) and ¬
         (kindSelectedCollection_s = item collectionCounter of kindsTopCollections_s) then
         set selectedCollectionIndex to collectionCounter
         exit repeat
      end if
   end repeat
   
   if COPDocKind_s = "catalog" then
      repeat with collectionCounter from (countTopCollections - 1) to 1 by -1
         if (true = item collectionCounter of userTopCollections) then
            set topUserCollectionIndex to collectionCounter + 0
            exit repeat
         end if
      end repeat
      repeat with collectionCounter from 1 to countTopCollections
         if (true = item collectionCounter of userTopCollections) then
            set bottomUserCollectionIndex to collectionCounter + 0
            exit repeat
         end if
      end repeat
      set selectedCollectionMirroredAtTopLast to ¬
         (selectedCollectionIndex = countTopCollections) and selectedCollectionIsUser and ¬
         ({"catalog folder", "favorite"} does not contain last item of kindsTopCollections_s)
      
   else if COPDocKind_s = "session" then
      tell application "Capture One 12" to tell COPDocRef
         set foldersTopCollection to folder of every collection
         set folderSelectedCollection to folder of selectedCollectionRef
      end tell
      
      repeat with collectionCounter from countTopCollections to 1 by -1
         if (true = item collectionCounter of userTopCollections) and (missing value = item collectionCounter of foldersTopCollection) then
            set topUserCollectionIndex to collectionCounter + 0
            exit repeat
         end if
      end repeat
      repeat with collectionCounter from 1 to countTopCollections
         if (true = item collectionCounter of userTopCollections) and (missing value = item collectionCounter of foldersTopCollection) then
            set bottomUserCollectionIndex to collectionCounter + 0
            exit repeat
         end if
      end repeat
      set selectedCollectionMirroredAtTopLast to false
      set selectedCollectionIsUser to selectedCollectionIsUser and (missing value = folderSelectedCollection)
   end if
end validateCOPcollections3

on convertKindList(kind_list)
   ## Copyright 2019 Eric Valk, Ottawa, Canada   Creative Commons License CC BY-SA    No Warranty.
   ## General Purpose Handler for scripts using Capture One Pro
   ## Many releases of Capture One return the chevron form of the property "kind" when AppleScript is run as an Application
   ## This script converts a list of kind enums to text, handling the chevron form correctly
   ## Assume that the list either contains the plain text enums or the chevron form enums, but not both
   ## Assume that the list contains the kind enums from the same class
   
   if "list" = (get (class of kind_list) as text) then
      set input_is_list to true
   else
      set kind_list to {kind_list}
      set input_is_list to false
   end if
   set kind_s1 to (item 1 of kind_list) as text
   
   set kind_s_list to {}
   set fail_flag to false
   
   if "«" ≠ (get text 1 of kind_s1) then
      repeat with Kind_item in kind_list
         set the end of kind_s_list to (get Kind_item as text)
      end repeat
   else
      ## minimum 6 characters for valid logic. Normally 19
      if 6 > length of kind_s1 then loqqed_Error_Halt3(true, "convertKind received an unexpected Kind string: " & kind_s1)
      set code_start to (get length of kind_s1) - 4
      set kind_type to get (text code_start thru (code_start + 1) of kind_s1)
      
      repeat with Kind_item in kind_list
         set kind_s to Kind_item as text
         set kind_code to get (text code_start thru (code_start + 3) of kind_s)
         
         if kind_type = "CC" then ## Collection Kinds
            if kind_code = "CCpj" then
               set the end of kind_s_list to "project"
            else if kind_code = "CCgp" then
               set the end of kind_s_list to "group"
            else if kind_code = "CCal" then
               set the end of kind_s_list to "album"
            else if kind_code = "CCsm" then
               set the end of kind_s_list to "smart album"
            else if kind_code = "CCfv" then
               set the end of kind_s_list to "favorite"
            else if kind_code = "CCff" then
               set the end of kind_s_list to "catalog folder"
            else
               set fail_flag to true
            end if
            
         else if kind_type = "CL" then ## Layer Kinds
            if kind_code = "CLbg" then
               set the end of kind_s_list to "background"
            else if kind_code = "CLnm" then
               set the end of kind_s_list to "adjustment"
            else if kind_code = "CLcl" then
               set the end of kind_s_list to "clone"
            else if kind_code = "CLhl" then
               set the end of kind_s_list to "heal"
            else
               set fail_flag to true
            end if
            
         else if kind_type = "CR" then ## Watermark Kinds
            if kind_code = "CRWn" then
               set the end of kind_s_list to "none"
            else if kind_code = "CRWt" then
               set the end of kind_s_list to "textual"
            else if kind_code = "CRWi" then
               set the end of kind_s_list to "imagery"
            else
               set fail_flag to true
            end if
            
         else if kind_type = "CO" then ## Document Kinds
            if kind_code = "COct" then
               set the end of kind_s_list to "catalog"
            else if kind_code = "COsd" then
               set the end of kind_s_list to "session"
            else
               set fail_flag to true
            end if
         else
            set fail_flag to true
         end if
         
         if fail_flag then loqqed_Error_Halt3(true, "convertKindList received an unexpected Kind string: " & kind_s)
      end repeat
      
   end if
   
   if input_is_list then
      return kind_s_list
   else
      return item 1 of kind_s_list
   end if
   
end convertKindList

on InitializeResultsCollection(nameResultProject, nameResultAlbumRoot, Coll_Init_Text)
   ## Copyright 2019 Eric Valk, Ottawa, Canada   Creative Commons License CC BY-SA    No Warranty.
   ## General Purpose Handler for scripts using Capture One Pro
   ## Sets up a project and albums for collecting images
   
   global debugLogLevel, COPDocRef, ref2ResultAlbum, enableNotifications
   
   tell application "Capture One 12" to tell COPDocRef
      if not (exists collection named (get nameResultProject)) then
         set ref2ResultProject to make new collection with properties {kind:project, name:nameResultProject}
      else
         if ("project" = my convertKindList(kind of (get collection named nameResultProject))) then
            set ref2ResultProject to collection named nameResultProject
         else
            my loqqed_Error_Halt3(true, ("A collection named \"" & nameResultProject & "\" already exists, and it is not a project."))
         end if
      end if
   end tell
   
   set coll_ctr to 1
   set nameResultAlbum to nameResultAlbumRoot & "_" & (get short date string of (get current date)) & "_"
   repeat
      tell application "Capture One 12" to tell ref2ResultProject
         if not (exists collection named (get nameResultAlbum & coll_ctr)) then
            set nameResultAlbum to (get nameResultAlbum & coll_ctr)
            set ref2ResultAlbum to make new collection with properties {kind:album, name:nameResultAlbum}
            exit repeat
         else
            set coll_ctr to coll_ctr + 1
         end if
      end tell
   end repeat
   
   if enableNotifications then display notification (Coll_Init_Text & " " & nameResultProject & ">" & nameResultAlbum)
   loq_Results2(1, false, (Coll_Init_Text & "  " & nameResultProject & ">" & nameResultAlbum))
   
end InitializeResultsCollection

###########################################################################################################################
## General Handlers  Version 2019/12/28

on InitializeLoqqing3(DocName_Ext, sourceTitle)
   ## Copyright 2019 Eric Valk, Ottawa, Canada   Creative Commons License CC BY-SA    No Warranty.
   ## General purpose handler to set up Text Editor document for logging results
   
   global debugLogLevel, Script_Title, Result_Doc_ref, SE_Logging, enableResultsFile, enableResultsByDialog, enableResultsByClipboard, DialogTextList, enableNotifications
   global initEnableResultsByDialog, initEnableResultsByClipboard
   
   tell current application to set date_string to (current date) as text
   set LogMethods to {}
   set LogHeader to (sourceTitle & " results on " & date_string)
   
   if enableResultsFile then
      set end of LogMethods to DocName_Ext
      set targetFileWasCreated to false
      set ResultDocIsOpen to false
      
      ## Check if TextEdit is already open and has the document open
      tell application "System Events" to set TextEditlist to get background only of every application process whose name is "TextEdit"
      if (0 < (count of TextEditlist)) and not item 1 of TextEditlist then
         if (DocName_Ext is in (get name of documents of application "TextEdit")) then
            tell application "TextEdit" to set Result_Doc_ref to document DocName_Ext
            set ResultDocIsOpen to true
         end if
      end if
      
      if not ResultDocIsOpen then
         -- create the document and the folder if necessary
         -- Do not use finder to test for the file existence because it has a bug that ignores leading 0's
         -- https://www.macscripter.net/viewtopic.php?id=45178
         
         set target_folder_parent_a to alias (get path to desktop folder as text)
         set target_folder_parent_p to get POSIX path of target_folder_parent_a
         set target_folder_name to "ScriptReports"
         set target_folder_p to (target_folder_parent_p & target_folder_name)
         set Result_Doc_Path_p to target_folder_p & "/" & DocName_Ext
         
         try
            set Result_Doc_Path_a to (get alias POSIX file Result_Doc_Path_p)
         on error
            try
               set target_folder_a to (get alias POSIX file target_folder_p) --x1
            on error
               tell application "Finder" to set newFolder to make new folder at target_folder_parent_a with properties {name:target_folder_name}
               set target_folder_a to newFolder as alias
            end try
            tell application "Finder" to set newFile to make new file at target_folder_a with properties {name:DocName_Ext}
            set Result_Doc_Path_a to newFile as alias
            set targetFileWasCreated to true
         end try
         
         set First_line to ("Created by " & Script_Title & " on " & date_string)
         tell application "TextEdit" -- open the document and add the first line if empty
            activate
            set Result_Doc_ref to open Result_Doc_Path_a
            set ResultDocIsOpen to true -- For consistency
            tell text of Result_Doc_ref
               if targetFileWasCreated then
                  set paragraph 1 to First_line & return & return
                  tell me to if 2 ≤ debugLogLevel then log Result_Doc_Path_p & ": " & First_line
               else
                  try
                     if (0 = (count of paragraphs)) then set paragraph 1 to First_line & return & return
                  on error
                     set paragraph 1 to First_line & return & return
                  end try
               end if
            end tell
         end tell
      end if
      
      tell application "TextEdit" to tell text of Result_Doc_ref to ¬
         set paragraph (1 + (count paragraphs)) to return & LogHeader & return
      
   end if
   
   if enableResultsByDialog then
      set end of LogMethods to "Display Dialog"
      try
         initEnableResultsByDialog
         set DialogTextList to DialogTextList & ""
         set initEnableResultsByDialog to false
      on error
         set DialogTextList to {LogHeader}
         set initEnableResultsByDialog to true
      end try
   end if
   
   if enableResultsByClipboard then
      set end of LogMethods to "Clipboard"
      try
         initEnableResultsByClipboard
         set initEnableResultsByClipboard to false
      on error
         set the clipboard to LogHeader
         set initEnableResultsByClipboard to true
      end try
   end if
   
   if SE_Logging then -- if Script Editor logging, then open the Log History window
      try
         tell application "System Events" to tell application process "Script Editor"
            if (get name of windows) does not contain "log History" then
               click menu item "Log History" of menu "Window" of menu bar 1
            end if
         end tell
      end try
      set end of LogMethods to " Script Editor Log"
   end if
   
   set LogMethods_S to joinListToString(LogMethods, ", ")
   loq_Results2(2, false, ("Results by " & LogMethods_S))
   
   return LogMethods_S
end InitializeLoqqing3

on loq_Results2(thisLogDebugLevel, MakeFront, log_Text)
   ## Copyright 2019 Eric Valk, Ottawa, Canada   Creative Commons License CC BY-SA    No Warranty.
   ## General purpose handler for logging results
   ## log results if the debug level of the message is below the the threshold set by debugLogLevel
   ## log the results by whatever mechanism is ebabled - {Script Editor Log, Text Editor Log, Display Dialog}
   
   global Result_Doc_ref, debugLogLevel, SE_Logging, parent_name, ResultsFileMaxDebug, enableResultsFile, enableResultsByDialog, enableResultsByClipboard, DialogTextList
   if thisLogDebugLevel > debugLogLevel then return
   
   set log_Text_S to joinListToString(log_Text, ", ")
   
   if enableResultsFile and ((thisLogDebugLevel ≤ ResultsFileMaxDebug) or not SE_Logging) then
      tell application "TextEdit" to tell text of Result_Doc_ref to ¬
         set paragraph (1 + (count paragraphs)) to ((log_Text_S as text) & return)
      tell application "System Events" to if MakeFront then set frontmost of process "TextEdit" to true
   end if
   
   if enableResultsByDialog and (1 ≥ thisLogDebugLevel) then
      set DialogTextList to DialogTextList & log_Text_S
      tell application "System Events" to set frontmost of process parent_name to true
      display dialog joinListToString(DialogTextList, return)
   end if
   
   if enableResultsByClipboard and (1 ≥ thisLogDebugLevel) then set the clipboard to ((get the clipboard) & return & (log_Text_S as text))
   if SE_Logging then log (log_Text_S as text)
   
end loq_Results2

on loqqed_Error_Halt3(createErrorHere, error_text)
   ## General purpose handler for logging during script termination
   ##
   ## found an error somewhere, so now we exit in a controlled fashion
   ## set createError to "false" to create a local error  instead of here
   global debugLogLevel, Script_Title, enableNotifications
   tell current application to set date_string to (current date) as text
   finalCleanup()
   if enableNotifications then display notification error_text
   if createErrorHere then
      loq_Results2(0, true, ("Script \"" & Script_Title & "\" has halted at " & date_string & return & "Reason: " & error_text & return & return))
      error error_text
   else
      loq_Results2(0, true, ("Script \"" & Script_Title & "\" is exitting at " & date_string & "Reason: " & error_text & return))
   end if
end loqqed_Error_Halt3

on splitStringToList(theString, theDelim)
   ## Public Domain
   set astid to AppleScript's text item delimiters
   try
      set AppleScript's text item delimiters to theDelim
      set theList to text items of theString
   on error
      set AppleScript's text item delimiters to astid
   end try
   set AppleScript's text item delimiters to astid
   return theList
end splitStringToList

to joinListToString(theList, theDelim)
   ## Public Domain
   set theString to ""
   set astid to AppleScript's text item delimiters
   try
      set AppleScript's text item delimiters to theDelim
      set theString to theList as string
   on error
      set AppleScript's text item delimiters to astid
   end try
   set AppleScript's text item delimiters to astid
   return theString
end joinListToString

on roundDecimals(n, numDecimals)
   ## Nigel Garvey, Macscripter
   set x to 10 ^ numDecimals
   tell n * x to return (it div 0.5 - it div 1) / x
end roundDecimals

on MSduration(firstTicks, lastTicks)
   ## Public domain
   ## returns duration in ms
   ## inputs are durations, in seconds, from GetTick's Now()
   return (round (10000 * (lastTicks - firstTicks)) rounding to nearest) / 10
end MSduration

on GetTick_Now()
   ## From MacScripter Author "Jean.O.matiC"
   ## returns duration in seconds since since 00:00 January 2nd, 2000 GMT, calculated using computer ticks
   script GetTick
      property parent : a reference to current application
      use framework "Foundation" --> for more precise timing calculations
      on Now()
         return (current application's NSDate's timeIntervalSinceReferenceDate) as real
      end Now
   end script
   
   return GetTick's Now()
end GetTick_Now
Eric Nepean
 
Posts: 652
Joined: Sat Oct 25, 2014 8:02 am
Location: Ottawa

Re: Applescript to find Offline Files

Postby Paul_Steunebrink » Sun Dec 29, 2019 7:32 pm

This is really awesome! Thanks for sharing, this will help many users.
User avatar
Paul_Steunebrink
Certified Professional
Certified Professional
 
Posts: 10072
Joined: Tue Feb 06, 2007 8:49 pm
Location: The Netherlands


Return to Scripting



Who is online

Users browsing this forum: No registered users and 2 guests