Looks like no one’s replied in a while. To start the conversation again, simply ask a new question.

AppleScript Numbers: Process data from Quicken

Environment:

  • Mojave
  • Numbers 6.1
  • Automator 2.9
  • Both Numbers and Automator are set via System Preferences > Security & Privacy > Accessibility


Rationale For Post:

I'd been working on this for a number of days, and got a lot of valuable help (from SG and others), and I figured providing a somewhat detailed summation of the code was a way of paying-back / paving the way for others.


General Description of Project:

Periodically I get dumps of my mother's financial data from her Quicken, and I wanted to be able to look at the information in various ways to get a clearer picture of what it all means. I started with what I considered to be a relatively simple spreadsheet. One sheet had the detailed information broken down by date, amount, and categorization key:


A second sheet then tallied the amounts associated with each key on a month-by-month basis:


I proceeded to populate each cell in the table with a formula like:

=SUMIFS($Amount,$Date,">=01/01/2016",$Date,"<02/01/2016",$Key,"=IN:MISC*")

Where '$Amount', '$Date', and '$Key' are Numbers' variables refering the columns of the "Details" sheet


This was all pretty nice and I was reasonably proud of myself (:-)) but I then realized I had to make a distinct formula for each 'key' row, and then I had to also make a distinct version of the formula for each month column. With approximately 40 'key's and 12 months in the year we're talking about over 480 manual edits to the formulas.


Well, I realized then that I could put all the key-row formalas for a given month into a text-file and use query-replace to change the month information and then copy/paste it into the Numbers table. Not terrible, but still a bit time-consuming, and it was only going to get worse as I added more and more years of data to the "Details" sheet.


I then thought that might be able to use column-row references like $AK4 or AK$4 and then just copy-paste the formulas around without going through the query-replace step. To this end, I modified the table to include month numbers and key strings to reference:


Unfortunately, I then realized that I couldn't use such a reference within a string - i.e. ">=$E2" and "<$F2"


Enter AppleScript:

So I started thinking about AppleScript and ran into a number of questions about coding and started posting questions in the Apple discussion forums (thanks again to SG and others for responding):


In the course of doing so and plodding through ideas while waiting for answers, the design model morphed a bit:

(thanks again to SG and others for responding)


So, ultimately I decided to construct formulas using the information I put in the row and column headings, setting the cell to the formula, and then replacing it with the result of the formula (to significantly reduce the number of formulas within the table). I'm still playing around with the code a bit, but this works and I hope provides a reasoable example for others who are trying to do the same or similar type things.

(Due to size limitations on post, the code will be put into one or more Reply's)

MacBook Air 13", macOS 10.14

Posted on Jul 15, 2019 1:38 PM

Reply
Question marked as Best reply

Posted on Jul 15, 2019 1:41 PM

The code...(part 2 of 2)

(* The next two functions could simply return the value rather than
   creating a variable and returning that, but when trying to debug
   code it helps to have the steps separated *)
on getRegExString(targetCell)
  tell application "Numbers"
    tell table 1 of active sheet of front document
      set regex to value of targetCell's row's cell (regexColumn)
      return regex
    end tell
  end tell
end getRegExString

on getMonthString(targetCell)
  tell application "Numbers"
    tell table 1 of active sheet of front document
      set monthNum to (value of targetCell's column's cell (monthRow) as integer)
      return monthNum
    end tell
  end tell
end getMonthString

(* The year value appears in one cell over one of the twleve month
   columns, so if there isn't any value in the row/column pair
   associated with the current target cell, we need to go back (to the
   left) one column, and continue to do so until we find a value *)
on getYearString(targetCell)
  tell application "Numbers"
    tell table 1 of active sheet of front document
      repeat with x from 0 to 11
        set yearCol to address of targetCell's column
        set yearCell to cell (yearCol - x) of row yearRow
        set yearNum to value of yearCell
        if yearNum is not missing value then
          return yearNum as integer
        end if
      end repeat
      display notification "ERROR: unable to find year in row " & yearRow
    end tell
  end tell
end getYearString


5 replies
Question marked as Best reply

Jul 15, 2019 1:41 PM in response to astoller

The code...(part 2 of 2)

(* The next two functions could simply return the value rather than
   creating a variable and returning that, but when trying to debug
   code it helps to have the steps separated *)
on getRegExString(targetCell)
  tell application "Numbers"
    tell table 1 of active sheet of front document
      set regex to value of targetCell's row's cell (regexColumn)
      return regex
    end tell
  end tell
end getRegExString

on getMonthString(targetCell)
  tell application "Numbers"
    tell table 1 of active sheet of front document
      set monthNum to (value of targetCell's column's cell (monthRow) as integer)
      return monthNum
    end tell
  end tell
end getMonthString

(* The year value appears in one cell over one of the twleve month
   columns, so if there isn't any value in the row/column pair
   associated with the current target cell, we need to go back (to the
   left) one column, and continue to do so until we find a value *)
on getYearString(targetCell)
  tell application "Numbers"
    tell table 1 of active sheet of front document
      repeat with x from 0 to 11
        set yearCol to address of targetCell's column
        set yearCell to cell (yearCol - x) of row yearRow
        set yearNum to value of yearCell
        if yearNum is not missing value then
          return yearNum as integer
        end if
      end repeat
      display notification "ERROR: unable to find year in row " & yearRow
    end tell
  end tell
end getYearString


Jul 15, 2019 1:40 PM in response to astoller

The code...(part 1 of N [currently N=2])

-- Set properties to reference specific rows or columns to use in formulas
property yearRow : 0
property monthRow : 2
property regexColumn : 4

-- Overall Service main function
on run {input, parameters}
  tell application "Numbers"
    (* Since running as service, 'front document' and 'active sheet'
       are the current sheet in the current document *)
    tell table 1 of active sheet of front document
      set selRng to selection range
      (* 'selRng' will be the curret, or currently selected range of,
          cell(s). It's stored in a variable for reference because
          otherwise as soon as you edit the contents of a cell, the
          next call in the loop would be referencing an index into a
          range that is no longer selected *)
      repeat with currentCell in cells of the selRng
        my processCell(currentCell)
      end repeat
    end tell
  end tell
  return input
end run

(* NOTE: When creating functions that are going to interact with the
         data in sheet, you need to re-specify the context (tell ...)
         within the function *)
on processCell(targetCell)
  tell application "Numbers"
    tell table 1 of active sheet of front document

      set yearString to my getYearString(targetCell) -- '2016', '2017', etc.
      set month1 to my getMonthString(targetCell) -- '1', '2', etc.
      set month2 to month1 + 1
      set pattern to "=" & my getRegExString(targetCell) -- e.g.: "=EXP:MISC*"
      set dateString1 to ">=" & month1 & "/01/" & yearString -- ">=01/01/2016"

      (* The second date string is one-month greater than the first,
         but if the value is greater than 12 (December), we have to
         change it to 01 and increment the year by one *)
      if month2 > 12 then
        set dateString2 to "<01/01/" & yearString + 1
      else
        set dateString2 to "<" & month2 & "/01/" & yearString
      end if

      set regexString to "=SUMIFS($Amount,$Date,\"" & dateString1 &¬
                         "\",$Date,\"" & dateString2 & ¬
                         "\",$Key,\"" & pattern & "\")"


      (* Put formula into the cell, which will calculate the desired result *)
      set value of targetCell to regexString

      (* Copy the cell's value onto itself, thus replacing the formula
         with the static value; reducing processing overhead *)
      set value of targetCell to value of targetCell

      (* Set format of cell to 'currency', unfortunately there does
         not seem to be a way to enable the Thousands Separator and/or
         Accounting Style options that are avaiblable within the
         GUI. There are ways to simulate this, but I haven't dealt
         with that [yet] *)
      set the format of targetCell to currency
    end tell
  end tell
end processCell

Jul 15, 2019 2:49 PM in response to astoller

"Unfortunately, I then realized that I couldn't use such a reference within a string - i.e. ">=$E2" and "<$F2""


A formula comment:


This looks like a SUMIFS condition(s) statement. The comparison needs to be presented as text, but the cell reference supplying part of that text must be outside the quotation marks. Only one condition can be stated in each test-values,condition pair.


Try: ">="&$E2

and "<"&$F2


Regards,

Barry

Jul 16, 2019 8:48 AM in response to Barry

Hmm - it's conceivable that might have worked, I think I ran into this problem before I realized '&' was the means of concatenating strings.

I still would have needed column D to include the regex strings, and row 2 would have included date strings like "01/01/2016", "02/01/2016", etc. (American date format MM/DD/YYYY) to become:

=SUMIFS($Amount,$Date,">="&E$2,$Date,"<"&F$2,$Key,"="&$D6)

Which, appears to work - however, at the time I was trying to get this to work, I wasn't thinking about AppleScript, and this would have resulted in over 2000 formulas within a single table and performance would have been horrid every time Numbers re-calculate the contents of all those cells - so it was probably a good thing I didn't realize I could do that ;-)

AppleScript Numbers: Process data from Quicken

Welcome to Apple Support Community
A forum where Apple customers help each other with their products. Get started with your Apple ID.