How to loop through all rows in Excel using Applescript? - ruby

I'm trying to loop through all rows in Excel and run some command on each row, but I can't figure out how!
I tried initially doing that in rb-appscript (the Ruby wrapper for AppleScript) but decided that I'd be better off trying to get it working first in Applescript before adding another DSL. Any tips? (the rb-appscript version would be nice, too, though not required).
Thanks!

The coordinates of a range is a string that you can create dynamically. The simplest way to loop through your rows would be something like this:
repeat with thisRow from 1 to 10
tell application "Microsoft Excel"
set theRange to "A:" & thisRow & "Z:" & thisRow
set theValue to (get value of range theRange) as list
-- do something with the row's data
end tell
end repeat
Update per comments: To get the number of rows needing calculation, I wrote a subroutine ages ago to help with this. Be sure you have some kind of "key" column that is consistently populated (in other words, there are no skipped cells). This currently takes into account a header row since all of my spreadsheets have them:
on GetItemCount(KeyColumn)
set RowNumber to 1
set theText to "cSyoyodylg" -- dummy value
tell application "Microsoft Excel"
repeat until theText is ""
set RowNumber to RowNumber + 1
set theRange to KeyColumn & RowNumber & ":" & KeyColumn & RowNumber
set dataRange to range theRange of sheet 1
set theText to (get value of range theRange)
end repeat
end tell
set rowCount to RowNumber - 1
return rowCount
end GetItemCount
To use simply do this:
set lastRow to GetItemCount("A") of me
repeat with thisRow from 2 to lastRow + 1
-- go attack Excel
end repeat

Related

Get text of selected field code in Microsoft Word using AppleScript

I'm making a Automator to jump from citation in Word to the reference software(Zotero). But I can't find a AppleScript to extract text of selected field code (the first step).
The field code in Word is
ADDIN ZOTERO_ITEM CSL_CITATION {"citationID":"AFUiwuqi","properties":{"formattedCitation":"[1]","plainCitation":"[1]","noteIndex":0},"citationItems":[{"id":9752,"uris":["http://zotero.org/users/6410528/items/YYTRWPHH"],"itemData":{"id":9752,"type":"article-journal","container-title":"Nature","DOI":"10.1038/s41586-019-1737-7","ISSN":"0028-0836, 1476-4687","issue":"7782","page":"324-329","title":"Controlled flight of a microrobot powered by soft artificial muscles","volume":"575","author":[{"family":"Chen","given":"Yufeng"},{"family":"Zhao","given":"Huichan"},{"family":"Mao","given":"Jie"},{"family":"Chirarattananon","given":"Pakpong"},{"family":"Helbling","given":"E. Farrell"},{"family":"Hyun","given":"Nak-seung Patrick"},{"family":"Clarke","given":"David R."},{"family":"Wood","given":"Robert J."}],"issued":{"date-parts":[["2019",11,14]]}}}],"schema":"https://github.com/citation-style-language/schema/raw/master/csl-citation.json"}
Here is the script process:
Extract text from selected field code in Word (This is the question)
Get the uris text(http://zotero.org/users/6410528/items/YYTRWPHH)
Get the item-codes (YYTRWPHH).
Open url (zotero://select/library/items?itemKey=YYTRWPHH)
Now I use VBA to extract field code text, see below. But in this way, the file will be changed. So I want to do this via AppleScript.
Sub GetFiledsCodes()
Dim myRange As Range, myCodes As String
Set myRange = Selection.Range
With myRange
If .Fields.Count = 0 Then
MsgBox "No Code!", vbInformation
Exit Sub
Else
.Fields.Update
.TextRetrievalMode.IncludeFieldCodes = True
.TextRetrievalMode.IncludeHiddenText = True
myCodes = .Text
myCodes = VBA.Replace(myCodes, Chr(19), "{")
myCodes = VBA.Replace(myCodes, Chr(21), "}")
.SetRange .End, .End
.InsertAfter myCodes
.Font.Name = "Times New Roman"
.Font.Size = 12
.Cut
End If
End With
End Sub
PS:
Here is my process in Automator(it can work but using VBA):
Run AppleScript
on run {input, parameters}
tell application "Microsoft Word" to activate
tell application "Microsoft Word"
run VB macro macro name "GetFiledsCodes"
delay 0.5
end tell
return input
end run
Get contents from clipboard
Extract URLs from Text
Filter Paragraphs begin with http://zotero.org/users/
Copy to Clipboard
Run AppleScript
set myStr to do shell script "pbpaste"
tell application "Zotero" to activate
set AppleScript's text item delimiters to "
"
set myList to every text item of myStr
set zoterocode to ""
set codes to ""
repeat with j from 1 to the length of myList
set itemValue to item j of myList
set zoterocode to (do shell script "sed -E 's#http://zotero.org/users/[0-9]+/items/##g' <<< " & itemValue)
if j = 1 then
set codes to zoterocode
else
set codes to codes & "," & zoterocode
end if
end repeat
tell application "System Events"
key code 18 using {command down, control down, option down}
delay 0.5
set collectionKey to do shell script "pbpaste"
if collectionKey = myStr then
set theurl to "zotero://select/library/items?itemKey=" & codes
else
set theurl to collectionKey & "/items?itemKey=" & codes
end if
open location theurl
end tell
That helps a lot. Okay, so this isn't a turnkey solution for your question but I don't think you really need that as you'd probably end up having to tell me more about how this app works than is really necessary. So this script focuses on your initial question about getting the field codes/result ranges from a merge document.
I put together a simple mail merge consisting of labels and a data file with 8 records, each of which have 5 fields: {"«LastName»", "«JobTitle»", "«Company»", "«City»", "«Web»"}. The latter is the key field.
Basically, the script runs through the data merge document and cycles first through its fields, then the web field, and finally the web addresses.
Based on your script, I can't really determine what you are doing with each address so it finishes by collecting just the final part of each address in a list. The obscure parts for me are the pbpastes, the codes and the whole System Events block. This area would need tweaking.
Incidentally, it's quite likely that you can avoid some of the shell scripts but I can't say how yet. Obviously the script has some redundancies and could be further refined but I think it demonstrates how to extract the information you need. Take a look at it and let me know what issues there are that need addressing.
tell application "Microsoft Word"
set d1 to document "cardo_labels.docx"
set fContents to {} -- list of mergefield
set fResRange to {} -- list of result range, i.e. field merge data
repeat with x from 1 to (count of fields of d1)
set fcs to content of field code of field x of d1 --> " MERGEFIELD LastName "
set frr to content of result range of field x of d1 --> "Smith"
if fcs is not " NEXT " then -- ignore «Next Record»
set end of fContents to fcs
set end of fResRange to frr
end if
end repeat
--> single record example
fContents --> {" MERGEFIELD LastName ", " MERGEFIELD JobTitle ", " MERGEFIELD Company ", " MERGEFIELD City ", " MERGEFIELD Web "}
fResRange --> {"Smith", "President", "Acme Screw & Gear", "Metz", "http://zotero.org/users/1234/items/smith-metz"}
-- NB when not displaying 'merged data', fResRange will appear thusly: {"«LastName»", "«JobTitle»", "«Company»", "«City»", "«Web»"}
set webList to {}
repeat with y from 1 to (count of fResRange)
if item y of fResRange begins with "http://zotero.org/users/" then
set end of webList to (item y of fResRange)
end if
end repeat
--> {"http://zotero.org/users/1234/items/smith-metz"}
--> {"http://zotero.org/users/1234/items/smith-metz", "http://zotero.org/users/4222/items/branson-metz", "http://zotero.org/users/3236/items/house-metz", "http://zotero.org/users/3342/items/kurtz-london", "http://zotero.org/users/12345/items/jones-london"}
set urlPiece to {}
set AppleScript's text item delimiters to "/"
repeat with z in webList
set end of urlPiece to last text item of z
end repeat
-- contents of z
--> "http://zotero.org/users/1234/items/smith-metz"
set AppleScript's text item delimiters to ""
urlPiece
--> {"smith-metz"}
--> {"smith-metz", "jones-saopaolo", "branson-metz", "house-metz", "kurtz-london", "jones-london"}
end tell
Thanks to ideas from #Mockman.
Combining with the selection, here is the way to extract text from selected field code via AppleScript:
tell application "Microsoft Word"
tell selection
set fcs to content of field code of field of text object
end tell
end tell
fcs

Syntax error - Expected variable name or property but found class name

I get the syntax error below with this script. It fails in the line beginning repeat with cell in rows. How can I get the script to compile?
Expected variable name or property but found class name.
-- Declare variables
property albumDescription : ""
-- Display input box to get album description
display dialog "Enter the album description:" default answer ""
set albumDescription to text returned of result
-- Open Excel
tell application "Microsoft Excel"
activate
end tell
-- Set variables for the active sheet and range of cells
tell application "Microsoft Excel"
set sh to active sheet
set rng to range of sh
end tell
-- Loop through each cell in the range, starting from row 2
repeat with cell in rows 2 thru (count of rows of rng) of rng
-- Check if the cell value is not empty
if value of cell is not "" then
-- Update the album description for the row
set value of cell to albumDescription
end if
end repeat
Actually there are three issues:
All terminology which belongs to a specific application (in this case rows, the content of rng and value) must be inside a tell application block.
You cannot use the reserved word cell as a variable name. Well, you can, but you have to wrap the word in pipes (|cell|). However I recommend to use a different name like aCell
range in set rng to... must be used range
-- Declare variables
property albumDescription : ""
-- Display input box to get album description
display dialog "Enter the album description:" default answer ""
set albumDescription to text returned of result
-- Open Excel
activate application "Microsoft Excel"
-- Set variables for the active sheet and range of cells
tell application "Microsoft Excel"
set sh to active sheet
set rng to used range of sh
-- Loop through each cell in the range, starting from row 2
repeat with aCell in rows 2 thru (count of rows of rng) of rng
-- Check if the cell value is not empty
if value of aCell is not "" then
-- Update the album description for the row
set value of aCell to albumDescription
end if
end repeat
end tell

Find text from a file and set it as a variable in applescript?

I am trying to build a script that sends me updates and notifications from cex.io. Please keep on reading below, so I may guide you until the point I have trouble with.
The first simple script in this operation goes to cex.io's trading page for BTC/GHS. It records ands saves the text to a file every 4 seconds. It works great. It doesn't need to have safari refresh because the site pushes info to the browser live.
repeat
set the webpage_content to ""
tell application "Safari" to set the webpage_content to the text of document 1
set theText to webpage_content
set a to "Macintosh HD:Users:PRIVATE:Desktop:CEX:"
set theFile to (open for access file ((a) & "CEXRaw") with write permission)
write theText to theFile
close access theFile
delay 4
end repeat
-
And it returns this in a main file every 4 seconds: (note I cut off a chunk from the bottom and the top of the file, because they are unimportant)
GHS:
0.05233439
BTC:
0.00000223
NMC:
0.00002939
LTC:
0.00000000
GHS/BTC
0.02362958 LTC/BTC
0.02438131 NMC/BTC
0.00597565 GHS/NMC
3.96951800 BF1/BTC
1.67000000 Fund Account
GHS/BTC
Last price:
0.02362958
Daily change:
-0.00018042
Today's open:
0.02381000
24h volume:
73812.35539255
-
I now need an applescript to read that file, and return wanted values. But I'm lost on how to write it.
It needs to find the number under BTC, and set it as a variable.
It needs to find the number under GHS, and set it as a variable.
It needs to find the number under Last Price, and set it as a variable.
If anyone could script that really quick for me, or tell me how to do it, that would be amazing. Thank you so much!
Well, if those values will always be in the same paragraph counts, you could just pull them by line number.
set theCEXRaw to read file "Macintosh HD:Users:PRIVATE:Desktop:CEX:CEXRaw"
set theGHS to paragraph 2 of theCEXRaw
set theBTC to paragraph 4 of theCEXRaw
set thePRICE to paragraph 17 of theCEXRaw
You'd need to adjust the paragraph numbers. But, assuming that paragraph numbers aren't reliably consistent, in pure Applescript, you'd use Applescript's Text Item Delimiters.
set theCEXRaw to read file "Macintosh HD:Users:PRIVATE:Desktop:CEX:CEXRaw"
set AppleScript's text item delimiters to {("GHS:
"), ("BTC:
"), ("Last price:
")}
set theCEXRaw to text items of theCEXRaw
set theGHS to paragraph 1 of item 2 of theCEXRaw
set theBTC to paragraph 1 of item 3 of theCEXRaw
set thePRICE to paragraph 1 of item 4 of theCEXRaw
Note that the three delims include a return character inside the quotes. You will want to capture the old delimiter first, so you can restore it, and hopefully you can do the setting of the delimiter outside your repeat loop to save juice.
You could also use do shell script with sed or grep to strip each value.
You could get those values using the offset which searches a string for a substring and returns it's character position.
eg, set pos to the offset of "world" in "hello world" -- returns 7
Here is a solution that uses this principal to find your values and convert them into the Applescript floating point type Number
property line_delimiter : linefeed -- OR return OR return & linefeed pending your data
set results to "GHS:
0.05233439
BTC:
0.00000223
NMC:
0.00002939
LTC:
0.00000000
Last price:
0.02362958"
processCEX(results)
on processCEX(in_text)
set btc_val to searchNumberValueLine("BTC:" & line_delimiter, in_text)
set ghs_val to searchNumberValueLine("GHS:" & line_delimiter, in_text)
set last_price_val to searchNumberValueLine("Last price:" & line_delimiter, in_text)
display dialog ("btc = " & btc_val & return & "ghs = " & ghs_val & return & " last price = " & last_price_val)
end processCEX
on searchNumberValueLine(key_name, input_str)
set start_index to the offset of key_name in input_str
if (start_index is not 0) then
set input_str to text (start_index + ((length of key_name))) thru -1 of input_str
set end_index to the offset of line_delimiter in input_str
if (end_index is 0) then
return input_str as number
else
return (text 1 thru (end_index - 1) of input_str) as number
end if
else
return -1
end if
end searchNumberValueLine
Also i'd recommend against writing to a text file if you don't need to, to avoid any file io issues when reading the same file from a different script, given you are modifying it every 4 seconds.
You could change your code to something like this:
repeat
set the webpage_content to ""
tell application "Safari" to set the webpage_content to the text of document 1
processCEX(webpage_content)
delay 4
end repeat

Applescript: Highlight most recent file from group

I'd like to know how you'd go about creating an action where you could highlight a group of files and get the modification date from them, then have it highlight/select/label the file with the most recent date.
UPDATE: I want to do it on Applescript because I've gotten further in that. Here's what I have so far
set dateList to {}
tell application "Finder"
set inputList to get selection
repeat with i from 1 to count (inputList)
set end of dateList to get modification date of item i of inputList
end repeat
end tell
dateList
--Compare section...
set boolList to {}
set j to 1
repeat with i from 1 to count (dateList)
if i is (count (dateList)) then
set j to 0
end if
set end of boolList to item i of dateList > item (i + j) of dateList
end repeat
boolList
Looking at your existing applescript code this should sort any files you've selected by last modified date and return the latest result into a dialog box for you:
set dateList to {}
tell application "Finder"
set inputList to get selection
repeat with i from 1 to count (inputList)
set end of dateList to get modification date of item i of inputList
end repeat
end tell
--Compare section...
set modDate to item 1 of dateList
repeat with i from 1 to count (inputList)
if dateList's item i > modDate then
set modDate to dateList's item i
set theResult to displayed name of item i of inputList
set theResultDate to item i of dateList
end if
end repeat
--Display Result…
display alert "Most recently modified file in selection:" message "" & theResult & "
" & theResultDate
Dick got it, but I just fixed something and made it so it labels the file instead of a popup.
set dateList to {}
tell application "Finder"
set inputList to get selection
repeat with i from 1 to count (inputList)
set end of dateList to get modification date of item i of inputList
end repeat
end tell
--Compare section...
set theResult to item 1 of inputList as alias
set theResultDate to item 1 of dateList
set modDate to item 1 of dateList
repeat with i from 1 to count (inputList)
if dateList's item i > modDate then
set modDate to dateList's item i
set theResult to item i of inputList as alias
set theResultDate to item i of dateList
end if
end repeat
--Display Result…
--display alert "Most recently modified file in selection:" message "" & theResult & "
--" & theResultDate
tell application "Finder" to set label index of (theResult as alias) to 6
This will label it green, if you want a different color fiddle around with the index number 1-8, they're apparently not in order. Finder is also apparently smart enough to not count the selections in other open windows.
Thanks!
And finally, to make it useful as a right-click item, open Automator, make a Service, select at the top to use this on files/folders, drag Run Applescript in there, paste script, save. Now it will be available on right click. One downside is it seems the files need to stay selected until something is labeled. So no clicking while it's working.
You are making it more complicated than it needs to be:
tell application "Finder" to reveal item 1 of (sort (get selection) by modification date)
There is a bug in 10.7 and 10.8 that can make all of the suggested scripts almost unusable depending on the way they are run. If you open a new Finder window and select some files, tell application "Finder" to selection returns files selected in some window behind the frontmost window (or an empty list).
One workaround is to switch focus to another application and back:
activate app "SystemUIServer"
tell application "Finder"
activate
set label index of item 1 of (sort selection by modification date) to 6
end tell
You could also create an Automator service with a Run AppleScript action like this:
on run {input, parameters}
tell application "Finder"
sort (input as alias list) by modification date
set label index of item 1 of result to 6
end tell
end run

Setting the width of a variable in applescript

I am trying to write an apple script that sequentially names my TV shows with "S0XEYY" where YY is a two digit number. I have the following:
tell application "iTunes"
set p to selection
set c to count of p's items
set fixed indexing to true
repeat with i from 1 to c
set t to item i of p
try
set the episode number of t to i
set the episode ID of t to "S" & (text returned of a) & "E" & i
set the season number of t to text returned of a
end try
end repeat
However, I need the
(text returned of a)
as well as
i
to be of width 2, padded with a 0. How do I go about doing this?
Here is how I went about it:
if i ≤ 9 then
set the episode ID of t to episode ID of t & "0" & i
else
set the episode ID of t to episode ID of t & i
which I am sure is not the best way to do it, because it assumes no more then 2 characters are needed, but its the best solution I have, which works for this application because TV seasons never (that I have seen) exceed 20-30 episodes.

Resources