* Sort of a sort

For users to report plugin bugs and request plugin enhancements; and for authors to test new/new versions of plugins, and to discuss plugin development (in the Programming Technicalities sub-forum). If you want advice on choosing or using a plugin, please ask in General Usage or an appropriate sub-forum.
Post Reply
User avatar
Ron Melby
Megastar
Posts: 928
Joined: 15 Nov 2016 15:40
Family Historian: V6.2

Sort of a sort

Post by Ron Melby »

Perhaps the readers remember this bit of code and me whinging on about spacing and justification, and the result was that to insure that I use monospaced fonts.


0. Global Birth and Death gotten here, and in certain circumstances filled with forced blanks x'0040'
1. local Dates = eyb .. "(" .. Birth .. " - " .. Death .. ")" .. eyd

thats line will result in something that looks like this:

f(1640-1820)f

Nevermind than we ALL dont like those dates, what it is telling you is that birth is calculated from the file, and death date is calculated from the file.
there are other methods to calculate and they use other letters such as e, m, l, for earliest, mid, latest, and there are more.

3. table.insert(tblDates, Dates)
4 table.insert(tblsort, Birth)

there is no intervening monkey business between 0--4 I have debugged and checked that what I want in dates is what I want, and what I want in Birth is what I want. that is a tonumber(1640) or 4 forced spaces.

fhOutputResultSetColumn("Dates", "text",tblDates, #tblDates, cwdat , "align_mid")
fhOutputResultSetColumn("Lnk2", "item",tblsort, #tblsort, cwptr, "align_left",1, true, "default", "buddy")

the only thing you may question in these innocuous outputs is
cwdat = 64 (arrived at that as optimized showing of that type of column
cwptr = 0 because I dont want to display it, and I have tested it completely with clones and so fort.

on the output display the tblDates show up as if they are "live" fields that you could grab up in the actual record (which we would expect for an underlying item field. as you can surmise, tblsort[Birth] is not unique but is never nil. the issue is I am trying to sort on tblsort, but it actually sorts on tblDates. (one lousy exception record has unraveled me here) am I coding the impossible?
FH V.6.2.7 Win 10 64 bit
User avatar
tatewise
Megastar
Posts: 28414
Joined: 25 May 2010 11:00
Family Historian: V7
Location: Torbay, Devon, UK
Contact:

Re: Sort of a sort

Post by tatewise »

See the Help for the fhOutputResultSetColumn(...) function.
The options are all very similar to Query Columns, so take a look there.

Ron, you are misusing the "Lnk2" column.
It is defined as an "item" and a "buddy" which only works for pointer entries in the table, whereas tblsort holds integers.
I suspect that column needs to use "integer" and "hide" then the sorting should work.
The column can be anywhere before or after the Dates column as it is simply defining the order of the rows.

If you also want to be able to double-click on the Dates column entries and open the Property Box then that will need an "item" and "buddy" column of a table of pointers to the INDIvidual record.

BTW: You don't need to set its width to 0 to hide that column as "hide" and "buddy" columns are never shown as clearly specified in the Help.
Mike Tate ~ researching the Tate and Scott family history ~ tatewise ancestry
User avatar
Ron Melby
Megastar
Posts: 928
Joined: 15 Nov 2016 15:40
Family Historian: V6.2

Re: Sort of a sort

Post by Ron Melby »

Yanno, I looked at that a gabillion times, and know I changed that at least several million more times than that.

I feel so cheap, I have been staring at that, and dreaming about that dates thing for so long that is was surely in my DNA to see "integer" instead of "item" thanks. That solved it, immediately. Thanks, sometimes uninterested outsiders are the bomb.

And, unfortunately, it has also confirmed what I have suspected, because it made clear that under some unknown as yet circumstances, I am not trying to estimate unknown Birth (and perhaps Death) dates. So, that drudgery needs to be stared at and corrected, since I am using this (now, very slightly updated here logic) in most of my plugins. And it needs to be bulletproof. there should always be a letter before and after the (-) formated dates, if there is not, it is an error, and probably did not get thru the estimation process correctly.

I will include the code here, if anyone has absolutely nothing at all else to do in their life and wants a look at it, though I wont be put out if you dont. :D You will note that it asks for a source, (in my case I want to know if there is a FaG Web Page there) but it is informational only, and does not affect the gravamen of the program, so any source will do and will not affect other operations. You will also note that I have included things in there that may seem like they are unnecessary, but are for the purpose of helping debug :geek:

BTW 0 lengths are for consistency, I am trying in bits and pieces to have pretty standard template like code. its as meaningless as anyother placeholder, like _

Code: Select all

--[[
@title: allRelations
@author: Ron Melby
@lastupdated: Dec 2018
@version: 1.0
@description: FindaGrave allRelations

]]

-- Tables for Result Set
local tblParm      = {} -- FaG src
local tblINDI      = {} -- record pointer
local tblName      = {} -- formatted name
local tblDates     = {} -- Birth Death
local tblMarrName  = {} -- formatted name
local tblsort      = {} -- est birth
local tblRel       = {} -- relation
local tblFaG       = {} -- Basing Pointer
local tblPAGE      = {} -- http://www.FaG
local tblMML       = {} -- Memorial Number

function EstimatedBirthDates(ptrINDI,intGens)
  intGens = intGens or 2
  local dateMin = fhCallBuiltInFunction("EstimatedBirthDate",ptrINDI,"EARLIEST",intGens)
  local dateMax = fhCallBuiltInFunction("EstimatedBirthDate",ptrINDI,"LATEST",intGens)
  local dateMid = fhNewDatePt()
  if not ( dateMin:IsNull() or dateMax:IsNull() ) then
    local ptrFact = fhNewItemPtr()
    ptrFact:MoveToFirstChildItem(ptrINDI)
    while ptrFact:IsNotNull() do        -- Find 1st Fact with a Date
      if fhIsFact(ptrFact) then
        local datFact = fhGetValueAsDate(fhGetItemPtr(ptrFact,"~.DATE"))
        if not datFact:IsNull() then
          local datLast = datFact:GetDatePt1()  -- Last date = DatePt1 for Simple, Range, and Before
          local strType = datFact:GetSubtype()  -- Between = DatePt2 and After = DatePt1 + 10yrs
          if strType == "Between" then datLast = datFact:GetDatePt2()
          elseif strType == "After" then 
            datLast:SetValue(datLast:GetYear()+10,datLast:GetMonth(),datLast:GetDay(),datLast:GetYearDD()) 
          end
          if dateMax:Compare(datLast) > 0 then
            dateMax = datLast
          end
          if dateMin:Compare(dateMax) > 0 then
            dateMin = dateMax
          end
          if strType ~= "After" then
            break
          end  -- Now EARLIEST <= LATEST <= Last date
        end
      end
      ptrFact:MoveNext("ANY")
    end
    local intDays = ( GetDayNumber(dateMax) - GetDayNumber(dateMin)) / 2
    local intYear,remYear = math.modf( intDays / 365.2422 )  -- Offset year @ 365.2422 days per year, and remainder fraction
    local intMnth = math.floor( remYear * 12 )    -- Offset month is remainder fraction of year * 12
    dateMid = fhCallBuiltInFunction("CalcDate",dateMin,intYear,intMnth)
  end
  return { Min=dateMin; Mid=dateMid; Max=dateMax; }    -- Return EARLIEST, MID, LATEST dates
end -- function EstimatedBirthDates

-- Obtain the Day Number for any Date Point --
function GetDayNumber(datDate)
  local intDay = fhCallBuiltInFunction("DayNumber",datDate) -- Only works for Gregorian dates that were not skipped nor BC dates
  if not intDay then
    local intMonth = math.min( datDate:GetMonth(), 12 ) -- Limit month to 12, and day to last of each month
    local intDayNo = math.min( datDate:GetDay(), ({0;31;28;31;30;31;30;31;31;30;31;30;31;})[intMonth+1] )
    local intYear  = datDate:GetYear()
    if datDate:GetCalendar() == "Hebrew" and intYear > 3761 then
      intYear = intYear - 3761
    end
    if intYear == 1752 and intMonth ==  9 then
      intDayNo = 2 -- Use 2 Sep 1752 for 3 - 13 Sep 1752 skipped dates
    elseif intYear == 1582 and intMonth == 10 then
      intDayNo = 4 -- Use 4 Oct 1582 for 5 - 14 Oct 1582 skipped dates
    end
    local setDate = fhNewDatePt(intYear,intMonth,intDayNo,datDate:GetYearDD())
    intDay = fhCallBuiltInFunction("DayNumber",setDate)   -- Remove BC and Julian, Hebrew, French calendars
    if not intDay then
      intDay = 0
    end
    local oldDate = fhNewDate()    oldDate:SetSimpleDate(datDate)  -- Report problem to user
    local newDate = fhNewDate()    newDate:SetSimpleDate(setDate)
    local strIsBC = ""
    if datDate:GetBC() then
      intDay = -intDay  strIsBC = "and Day Number negated"
    end
    fhMessageBox("\n Get Day Number issue for \n "..oldDate:GetDisplayText().." \n So replaced it with \n "..newDate:GetDisplayText().." \n "..strIsBC)
  end
  return intDay
end -- function GetDayNumber

-- Make EstimatedDeathDate EARLIEST <= LATEST <= DEAT/BURI/CREM Date --
function EstimatedDeathDates(ptrINDI,intGens)
  intGens = intGens or 2
  local dateMin = fhCallBuiltInFunction("EstimatedDeathDate",ptrINDI,"EARLIEST",intGens)
  local dateMax = fhCallBuiltInFunction("EstimatedDeathDate",ptrINDI,"LATEST",intGens)
  local dateMid = fhNewDatePt()
  if dateMin:IsNull() 
  or dateMax:IsNull() then
    dateMin:SetNull()
    dateMax:SetNull()
  else
    local anyDate = false
    for intFact, strFact in ipairs ({"~.DEAT.DATE";"~.BURI.DATE";"~.CREM.DATE";}) do
      local datFact = fhGetValueAsDate(fhGetItemPtr(ptrINDI,strFact))
      if not datFact:IsNull() then  -- Find 1st Death/Burial/Cremation Date
        anyDate = true
        local datLast = datFact:GetDatePt1()  -- Last date = DatePt1 for Simple, Range, and Before
        local strType = datFact:GetSubtype()  -- Between = DatePt2 and After = DatePt1 + 100yrs
        if   strType == "Between" then
          datLast = datFact:GetDatePt2()
        elseif strType == "After" then
          datLast:SetValue(datLast:GetYear()+100,datLast:GetMonth(),datLast:GetDay(),datLast:GetYearDD()) end
          if dateMax:Compare(datLast) > 0 then
            dateMax = datLast
          end
          if dateMin:Compare(dateMax) > 0 then
            dateMin = dateMax
          end
          if strType ~= "After" then
            break
          end  -- Now EARLIEST <= LATEST <= Last date
        end
      end
      if anyDate then            -- Need approximate MID year & month
        local intDays = ( GetDayNumber(dateMax) - GetDayNumber(dateMin) ) / 2
        local intYear,remYear = math.modf( intDays / 365.2422 )  -- Offset year @ 365.2422 days per year, and remainder fraction
        local intMnth = math.floor( remYear * 12 )  -- Offset month is remainder fraction of year * 12
        dateMid = fhCallBuiltInFunction("CalcDate", dateMin, intYear, intMnth)
      else
        dateMin:SetNull()        -- No Death/Burial/Cremation Date exists
        dateMax:SetNull()
      end
    end
    return { Min=dateMin; Mid=dateMid; Max=dateMax; }    -- Return EARLIEST, MID, LATEST dates
  end -- function EstimatedDeathDates

  function estBirth(ptrINDI)
    local aBirthDate = EstimatedBirthDates(ptrINDI, 12)

    strDates = aBirthDate.Min
    eBirth = strDates:GetYear()
    if eBirth == 0 then
      eBirth = BLANKS
    end
    strDates = aBirthDate.Mid
    mBirth = strDates:GetYear()
    if mBirth == 0 then
      mBirth = BLANKS
    end
    strDates = aBirthDate.Max
    lBirth = strDates:GetYear()
    if lBirth == 0 then
      lBirth = BLANKS
    end

    if mBirth == BLANKS then
      estEvtBirth(ptrINDI)
      --     eyb ="e"
      return cBirth
    else
      eyb = "m"
      return mBirth
    end
  end  --fn estBirth

  function estDeath(ptrINDI)
    local aDeathDate = EstimatedDeathDates(ptrINDI, 12)

    strDates = aDeathDate.Min or 0
    eDeath = strDates:GetYear()
    if eDeath == 0 then
      eDeath = BLANKS
    end
    strDates = aDeathDate.Mid or 0
    mDeath = strDates:GetYear()
    if mDeath == 0 then
      mDeath = BLANKS
    end
    strDates = aDeathDate.Max or 0
    lDeath = strDates:GetYear()

    if lDeath == 0 then
      lDeath = BLANKS
      estEvtDeath(ptrINDI)
      eyd ="c"
      return cDeath
    else
      eyd = "l"
      return lDeath
    end
  end  --fn estDeath

  function estEvtBirth(ptrINDI)
    strDates = fhCallBuiltInFunction("LifeDates2", ptrINDI,"EXT")
    fmtrawDates(strDates)
  end --fn estEvtBirth

  function estEvtDeath(ptrINDI)
    strDates = fhCallBuiltInFunction("LifeDates2", ptrINDI,"EXT")
    fmtrawDates(strDates)
  end  -- estEvtDeath

  function fmtrawDates(strDates)
    strDates = strDates:gsub( ", "  , "-")            -- Fix bug where it produces comma space instead of hyphen
    strDates = strDates:gsub( "%d%d%d%d %" , "%1 " .. BLANKS )      -- Fix bug where only one (date) to (date- )
    strDates = strDates:gsub( " "  , BLANKS )                     -- Remove missing date space to multi-NBSP
    strDates = strDates:gsub( "%~"  , "" )            -- Remove ~
    strDates = strDates:gsub( "%?"  , "" )            -- Remove App,Cal,Est question marks
    strDates = strDates:gsub( "c%. ", "" )            -- Remove circa
    strDates = strDates:gsub( "aft" , "" )            -- Remove after
    strDates = strDates:gsub( "bef" , "" )            -- Remove before
    strDates = strDates:gsub( "frm" , "" )            -- Remove from
    strDates = strDates:gsub( "to"  , "" )            -- Remove to
    strDates = strDates:gsub( "%bap%." , "" )         -- Remove bap.
    strDates = strDates:gsub( "%chr%." , "" )         -- Remove chr.
    strDates = strDates:gsub( "%bur%.", "" )          -- Remove bur.

    cBirth, cDeath = strDates:match("(%d+)-(%d+)")

    if cBirth == nil then
      cBirth = BLANKS
    end
    if cDeath == nil then
      cDeath = BLANKS
    end
  end  -- fmtrawDates

-- rtvGraveTbl
  function rtvGraveTbl(ptrSOUR)
    local ptr       = fhNewItemPtr()
    local ptrLink   = fhNewItemPtr()
    local ptrWork   = fhNewItemPtr()
    local ptrBURI   = fhNewItemPtr()
    local ptrCREM   = fhNewItemPtr()
    ptr = ptrINDI:Clone()

    strWebPage = spc
    strMemorial = spc
    ptrBURI:MoveTo(ptr,"~.BURI.SOUR>")
    if ptrBURI:IsSame(ptrSOUR) then
      ptrWork = fhGetItemPtr(ptr,"~.BURI.SOUR.PAGE")
    else
      ptrCREM:MoveTo(ptr,"~.CREM.SOUR>")
      if ptrCREM:IsSame(ptrSOUR) then
        ptrWork = fhGetItemPtr(ptr,"~.CREM.SOUR.PAGE")
      end
    end
    strWebPage = fhGetValueAsText(ptrWork)
    table.insert(tblFaG, strWebPage)
    cwFaG = math.max(cwFaG, #strWebPage)
    table.insert(tblPAGE, ptrWork:Clone())
    local strMemorial = strWebPage:gsub(".-(%d+).*","%1")
    table.insert(tblMML, strMemorial)
    cwmml = math.max(cwmml, #strMemorial)
  end --fn rtvGraveTbl

  function getItemCount(ptrItem)   -- Count number of instances of Item
    local ent = 0
    while ptrItem:IsNotNull() do
      ent = ent + 1
      ptrItem:MoveNext("SAME_TAG")
    end
    return ent
  end -- fn getItemCount

-- Main Code
  local temp = os.date("*t")
  local now = temp.year
  fhSetStringEncoding("UTF-8")
  spc = fhConvertANSItoUTF8(string.char(0x00A0))
  BLANKS = string.rep(spc,4)
  cwnam    = 31                    -- name field 
  cwnm5    = 31                    -- name field 
  cwptr    = 0                     -- link field 
  cwdat    = 64                    -- *(    -    )* format dates 
  cwrel    = 0                     -- relationship 
  cwFaG    = 0                     -- findagrave...
  cwmml    = 0                     -- memorial number

  fBirth = BLANKS
  fDeath = BLANKS
  local tblfB        = {} -- Memorial Number
  local tblfD        = {} -- Memorial Number

  Birth  = BLANKS
  cBirth = BLANKS
  eBirth = BLANKS
  mBirth = BLANKS
  lBirth = BLANKS

  Death  = BLANKS
  cDeath = BLANKS
  eDeath = BLANKS
  mDeath = BLANKS
  lDeath = BLANKS

  eyb = spc
  eyd = spc

  local ptrRoot = fhCallBuiltInFunction("FileRoot")
  ptrINDI = fhNewItemPtr()
  ptrSOUR = fhNewItemPtr()
  local ptrNAM = fhNewItemPtr()
  localtblParm   = {} -- Source to find

-- Prompt for Source
  tblParm = fhPromptUserForRecordSel('SOUR',1)

-- nothing chosen
  if #tblParm == 0 then
    fhMessageBox('Plugin Cancelled')
    return
  end

--if citation has no links
  local iCitations = fhCallBuiltInFunction('LinksTo', tblParm[1])
  if iCitations == 0 then
    fhMessageBox('No Citations Found')
    return
  end

  ptrSOUR = tblParm[1]

  ptrINDI:MoveToFirstRecord("INDI")
  while ptrINDI:IsNotNull() do                         -- Process each Individual record
    relation = fhCallBuiltInFunction("Relationship", ptrRoot, ptrINDI, "TEXT", 1)
    if relation ~= "" then
      local Name = fhGetItemText(ptrINDI, "~.NAME:SURNAME_FIRST")
      local NameSuffix = fhGetItemText(ptrINDI,"~.NAME.NSFX")
      if NameSuffix ~= "" then
        Name = (Name.. ' ' ..NameSuffix)
      end
      table.insert(tblName, Name)
      cwnam = math.max(cwnam, #Name)
      table.insert(tblINDI, ptrINDI:Clone())

      Name = ""
      local sex = fhGetItemText(ptrINDI, "~.SEX")
      if sex == "Female" then
        ptrNAM:MoveTo(ptrINDI,"~.NAME")
        nc = getItemCount(ptrNAM)
        if nc > 1 then
          strDataOffset = "~.NAME[" .. nc .. "]:SURNAME_FIRST"
          Name = fhGetItemText(ptrINDI, strDataOffset)
        end
      end
      table.insert(tblMarrName, Name)
      cwnm5 = math.max(cwnm5, #Name)
      Birth  = BLANKS
      cBirth = BLANKS
      eBirth = BLANKS
      mBirth = BLANKS
      lBirth = BLANKS

      Death  = BLANKS
      cDeath = BLANKS
      eDeath = BLANKS
      mDeath = BLANKS
      lDeath = BLANKS


      eyb = 'f'
      fBirth = fhGetItemText(ptrINDI,"~.BIRT.DATE:YEAR")
      Birth = fBirth
      eyd = 'f'
      fDeath = fhGetItemText(ptrINDI,"~.DEAT.DATE:YEAR")
      Death = fDeath

      if Birth == "" then
        eyb   = spc
        Birth = BLANKS
        Birth = estBirth(ptrINDI)
      end
      if Birth ~= BLANKS then
        Birth = tonumber(Birth)
        if Birth > now then
          eyb   = "o" 
          Birth = BLANKS
        end
      end

      if Death == "" then
        eyd   = spc
        Death = BLANKS
        Death = estDeath(ptrINDI)
      end
      if Death ~= BLANKS then
        Death = tonumber(Death)
        if Death > now then
          eyd   = "o" 
          Death = BLANKS
        end
      end

      local Dates =  eyb .. "(" .. Birth .. " - " .. Death .. ")" .. eyd
      table.insert(tblDates, Dates)
      table.insert(tblsort, Birth or 0)
      table.insert(tblfB, fBirth or 0)
      table.insert(tblfD, fDeath or 0)

      table.insert(tblRel, relation)
      cwrel = math.max(cwrel, #relation)

      rtvGraveTbl(ptrSOUR)
    end -- while relation not null
    ptrINDI:MoveNext()                               -- Move to next Individual record
  end  --while INDI not null

  cwnam    = (  4 * 31)                -- name field 
  cwnm5    = (  4 * cwnm5)             -- name field 
  cwptr    =    0                      -- link field 
  cwdat    =   64                      -- *(    -    )* format dates 
  cwrel    = (  4 * cwrel)             -- relationship 
  cwFaG    = (  4 * cwFaG) + 4         -- findagrave...
  cwmml    = (  4 * cwmml) + 4         -- memorial number

  fhOutputResultSetTitles("Relations")
  fhOutputResultSetColumn("Name",     "text", tblName,     #tblName,     cwnam, "align_left",1)
  fhOutputResultSetColumn("ptr",      "item", tblINDI,     #tblINDI,     cwptr, "align_left",0, true, "default", "buddy")
  fhOutputResultSetColumn("Dates",    "text", tblDates,    #tblDates,    cwdat, "align_mid")
  fhOutputResultSetColumn("sort",     "integer", tblsort,     #tblsort,         32, "align_mid") -- ,0, true, "default", "hide")
  fhOutputResultSetColumn("fB", "text", tblfB, #tblfB, 16, "align_mid")
  fhOutputResultSetColumn("fD", "text", tblfD, #tblfD, 16, "align_mid")
  fhOutputResultSetColumn("AName",    "text", tblMarrName, #tblMarrName, cwnm5, "align_left")
  fhOutputResultSetColumn("Relation", "text", tblRel,      #tblRel,      cwrel, "align_left")
  fhOutputResultSetColumn("FaG",      "text", tblFaG,      #tblFaG,      cwFaG, "align_left")
  fhOutputResultSetColumn("ptr",      "item", tblPAGE,     #tblPAGE,     cwptr, "align_left",0, true, "default", "buddy")
  fhOutputResultSetColumn("Mem#",     "text", tblMML,      #tblMML,      cwmml, "align_right")

  return
FH V.6.2.7 Win 10 64 bit
User avatar
tatewise
Megastar
Posts: 28414
Joined: 25 May 2010 11:00
Family Historian: V7
Location: Torbay, Devon, UK
Contact:

Re: Sort of a sort

Post by tatewise »

I don't have time to debug your code, but can explain how I would approach it.

In the Result Set display you know which rows have no letter before/after the formatted dates.
So make a note of the Record Id of those Individual records.
You should also review the Birth Date, etc, for those Individual records to see if there is a common theme.

Then in the Plugin Debug Editor insert the following code inside the main loop just before you set eyb = 'f' but instead of 1234 use one of the Record Id noted above.

Code: Select all

if fhGetRecordId(ptrINDI) == 1234 then
    print("Initial", eyb)
end
Set a Break point on that print("Initial", eyb) statement and run the script.
When it reaches that Break point you can check in the lower right pane what value eyb holds.

Now use Step Into &/or Step Over to step through the script and monitor the eyb values.
e.g. Use Step Into to step through every statement, but use Step Over to skip over a function call.

You need to be inspecting what birth date values are causing the eyb values.
That should reveal where the logic in the code is going wrong.

Alternatively, you could insert code similar to that above, but with different text labels, at each point where eyb is changed, and just run the script without Break points. Then the bottom left pane will show each print output, which should give clues of where to look for logical errors.
Mike Tate ~ researching the Tate and Scott family history ~ tatewise ancestry
User avatar
Ron Melby
Megastar
Posts: 928
Joined: 15 Nov 2016 15:40
Family Historian: V6.2

Re: Sort of a sort

Post by Ron Melby »

That was pretty much how i did it. the code fixes were in the lns 374-424 area.

the
If Name == " [HOWDEN], Mary " debug @ 344 is the fact that her dates are
m(1794-1939)c
she is a clean profile, husband is (1795-aft1895)

1794 is accurate to my way of thinking. But 144 years old is too much. the check of cDeath @ 408 or so, is sort of a scary thing program wise, its obscure unless like me, you have thousands of hours in this program, but it checks if the estBirth function was called and uses the death date from that if any, in her case it was called and nothing came back . So, that is rather a bugaboo because in all the cases it happens, its around 150 years, but it picks birth pretty good, still bad at death. Another (all day all night * ?) debug session. barring that, its a pretty good program, but is my setup of FaG --- SOUR, rtvgravetbls which if people wanted to do something useful with that would probably need to change that to something they have an interest in.
Here is code as it stands now.

Code: Select all

--[[
@title: allRelations
@author: Ron Melby
@lastupdated: Dec 2018
@version: 1.0
@description: FindaGrave allRelations

]]

-- Tables for Result Set
local tblParm      = {} -- FaG src
local tblINDI      = {} -- record pointer
local tblName      = {} -- formatted name
local tblDates     = {} -- Birth Death
local tblMarrName  = {} -- formatted name
local tblsort      = {} -- est birth
local tblRel       = {} -- relation
local tblFaG       = {} -- Basing Pointer
local tblPAGE      = {} -- http://www.FaG
local tblMML       = {} -- Memorial Number

function EstimatedBirthDates(ptrINDI,intGens)
  intGens = intGens or 2
  local dateMin = fhCallBuiltInFunction("EstimatedBirthDate",ptrINDI,"EARLIEST",intGens)
  local dateMax = fhCallBuiltInFunction("EstimatedBirthDate",ptrINDI,"LATEST",intGens)
  local dateMid = fhNewDatePt()
  if not ( dateMin:IsNull() or dateMax:IsNull() ) then
    local ptrFact = fhNewItemPtr()
    ptrFact:MoveToFirstChildItem(ptrINDI)
    while ptrFact:IsNotNull() do        -- Find 1st Fact with a Date
      if fhIsFact(ptrFact) then
        local datFact = fhGetValueAsDate(fhGetItemPtr(ptrFact,"~.DATE"))
        if not datFact:IsNull() then
          local datLast = datFact:GetDatePt1()  -- Last date = DatePt1 for Simple, Range, and Before
          local strType = datFact:GetSubtype()  -- Between = DatePt2 and After = DatePt1 + 10yrs
          if strType == "Between" then datLast = datFact:GetDatePt2()
          elseif strType == "After" then 
            datLast:SetValue(datLast:GetYear()+10,datLast:GetMonth(),datLast:GetDay(),datLast:GetYearDD()) 
          end
          if dateMax:Compare(datLast) > 0 then
            dateMax = datLast
          end
          if dateMin:Compare(dateMax) > 0 then
            dateMin = dateMax
          end
          if strType ~= "After" then
            break
          end  -- Now EARLIEST <= LATEST <= Last date
        end
      end
      ptrFact:MoveNext("ANY")
    end
    local intDays = ( GetDayNumber(dateMax) - GetDayNumber(dateMin)) / 2
    local intYear,remYear = math.modf( intDays / 365.2422 )  -- Offset year @ 365.2422 days per year, and remainder fraction
    local intMnth = math.floor( remYear * 12 )    -- Offset month is remainder fraction of year * 12
    dateMid = fhCallBuiltInFunction("CalcDate",dateMin,intYear,intMnth)
  end
  return { Min=dateMin; Mid=dateMid; Max=dateMax; }    -- Return EARLIEST, MID, LATEST dates
end -- function EstimatedBirthDates

-- Obtain the Day Number for any Date Point --
function GetDayNumber(datDate)
  local intDay = fhCallBuiltInFunction("DayNumber",datDate) -- Only works for Gregorian dates that were not skipped nor BC dates
  if not intDay then
    local intMonth = math.min( datDate:GetMonth(), 12 ) -- Limit month to 12, and day to last of each month
    local intDayNo = math.min( datDate:GetDay(), ({0;31;28;31;30;31;30;31;31;30;31;30;31;})[intMonth+1] )
    local intYear  = datDate:GetYear()
    if datDate:GetCalendar() == "Hebrew" and intYear > 3761 then
      intYear = intYear - 3761
    end
    if intYear == 1752 and intMonth ==  9 then
      intDayNo = 2 -- Use 2 Sep 1752 for 3 - 13 Sep 1752 skipped dates
    elseif intYear == 1582 and intMonth == 10 then
      intDayNo = 4 -- Use 4 Oct 1582 for 5 - 14 Oct 1582 skipped dates
    end
    local setDate = fhNewDatePt(intYear,intMonth,intDayNo,datDate:GetYearDD())
    intDay = fhCallBuiltInFunction("DayNumber",setDate)   -- Remove BC and Julian, Hebrew, French calendars
    if not intDay then
      intDay = 0
    end
    local oldDate = fhNewDate()    oldDate:SetSimpleDate(datDate)  -- Report problem to user
    local newDate = fhNewDate()    newDate:SetSimpleDate(setDate)
    local strIsBC = ""
    if datDate:GetBC() then
      intDay = -intDay  strIsBC = "and Day Number negated"
    end
    fhMessageBox("\n Get Day Number issue for \n "..oldDate:GetDisplayText().." \n So replaced it with \n "..newDate:GetDisplayText().." \n "..strIsBC)
  end
  return intDay
end -- function GetDayNumber

-- Make EstimatedDeathDate EARLIEST <= LATEST <= DEAT/BURI/CREM Date --
function EstimatedDeathDates(ptrINDI,intGens)
  intGens = intGens or 2
  local dateMin = fhCallBuiltInFunction("EstimatedDeathDate",ptrINDI,"EARLIEST",intGens)
  local dateMax = fhCallBuiltInFunction("EstimatedDeathDate",ptrINDI,"LATEST",intGens)
  local dateMid = fhNewDatePt()
  if dateMin:IsNull() 
  or dateMax:IsNull() then
    dateMin:SetNull()
    dateMax:SetNull()
  else
    local anyDate = false
    for intFact, strFact in ipairs ({"~.DEAT.DATE";"~.BURI.DATE";"~.CREM.DATE";}) do
      local datFact = fhGetValueAsDate(fhGetItemPtr(ptrINDI,strFact))
      if not datFact:IsNull() then  -- Find 1st Death/Burial/Cremation Date
        anyDate = true
        local datLast = datFact:GetDatePt1()  -- Last date = DatePt1 for Simple, Range, and Before
        local strType = datFact:GetSubtype()  -- Between = DatePt2 and After = DatePt1 + 100yrs
        if   strType == "Between" then
          datLast = datFact:GetDatePt2()
        elseif strType == "After" then
          datLast:SetValue(datLast:GetYear()+100,datLast:GetMonth(),datLast:GetDay(),datLast:GetYearDD()) end
          if dateMax:Compare(datLast) > 0 then
            dateMax = datLast
          end
          if dateMin:Compare(dateMax) > 0 then
            dateMin = dateMax
          end
          if strType ~= "After" then
            break
          end  -- Now EARLIEST <= LATEST <= Last date
        end
      end
      if anyDate then            -- Need approximate MID year & month
        local intDays = ( GetDayNumber(dateMax) - GetDayNumber(dateMin) ) / 2
        local intYear,remYear = math.modf( intDays / 365.2422 )  -- Offset year @ 365.2422 days per year, and remainder fraction
        local intMnth = math.floor( remYear * 12 )  -- Offset month is remainder fraction of year * 12
        dateMid = fhCallBuiltInFunction("CalcDate", dateMin, intYear, intMnth)
      else
        dateMin:SetNull()        -- No Death/Burial/Cremation Date exists
        dateMax:SetNull()
      end
    end
    return { Min=dateMin; Mid=dateMid; Max=dateMax; }    -- Return EARLIEST, MID, LATEST dates
  end -- function EstimatedDeathDates

  function estBirth(ptrINDI)
    local aBirthDate = EstimatedBirthDates(ptrINDI, 12)

    strDates = aBirthDate.Min
    eBirth = strDates:GetYear()
    if eBirth == 0 then
      eBirth = BLANKS
    end
    strDates = aBirthDate.Mid
    mBirth = strDates:GetYear()
    if mBirth == 0 then
      mBirth = BLANKS
    end
    strDates = aBirthDate.Max
    lBirth = strDates:GetYear()
    if lBirth == 0 then
      lBirth = BLANKS
    end

    if mBirth == BLANKS then
      estEvtBirth(ptrINDI)
      eyb ="e"
      return cBirth
    else
      eyb = "m"
      return mBirth
    end
  end  --fn estBirth

  function estDeath(ptrINDI)
    local aDeathDate = EstimatedDeathDates(ptrINDI, 12)

    strDates = aDeathDate.Min or 0
    eDeath = strDates:GetYear()
    if eDeath == 0 then
      eDeath = BLANKS
    end
    strDates = aDeathDate.Mid or 0
    mDeath = strDates:GetYear()
    if mDeath == 0 then
      mDeath = BLANKS
    end
    strDates = aDeathDate.Max or 0
    lDeath = strDates:GetYear()
    if lDeath == 0 then
      lDeath = BLANKS
      estEvtDeath(ptrINDI)
      eyd ="c"
      return cDeath
    else
      eyd = "l"
      return lDeath
    end
  end  --fn estDeath

  function estEvtBirth(ptrINDI)
    strDates = fhCallBuiltInFunction("LifeDates2", ptrINDI,"EXT")
    cBirth, cDeath = fmtrawDates(strDates)
    return cBirth
  end --fn estEvtBirth

  function estEvtDeath(ptrINDI)
    strDates = fhCallBuiltInFunction("LifeDates2", ptrINDI,"EXT")
    cbirth, cDeath = fmtrawDates(strDates)
    return cDeath
  end  -- estEvtDeath

  function fmtrawDates(strDates)

    --   debug, _ = string.find(strDates, "1730")
    --   if debug ~= nil then 
    --     debug = debug
    --   end

    strDates = strDates:gsub( ", "  , "-")            -- Fix bug where it produces comma space instead of hyphen
    strDates = strDates:gsub( "%d%d%d%d %" , "%1 " .. BLANKS )      -- Fix bug where only one (date) to (date- )
    strDates = strDates:gsub( " "  , BLANKS )                     -- Remove missing date space to multi-NBSP
    strDates = strDates:gsub( "%~"  , "" )            -- Remove ~
    strDates = strDates:gsub( "%?"  , "" )            -- Remove App,Cal,Est question marks
    strDates = strDates:gsub( "c%. ", "" )            -- Remove circa
    strDates = strDates:gsub( "aft" , "" )            -- Remove after
    strDates = strDates:gsub( "bef" , "" )            -- Remove before
    strDates = strDates:gsub( "frm" , "" )            -- Remove from
    strDates = strDates:gsub( "to"  , "" )            -- Remove to
    strDates = strDates:gsub( "%bap%." , "" )         -- Remove bap.
    strDates = strDates:gsub( "%chr%." , "" )         -- Remove chr.
    strDates = strDates:gsub( "%bur%.", "" )          -- Remove bur.

    cBirth, cDeath = strDates:match("(%d+)-(%d+)")

    if cBirth == nil then
      cBirth = BLANKS
    end
    if cDeath == nil then
      cDeath = BLANKS
    end
    return cBirth, cDeath
  end  -- fmtrawDates

-- rtvGraveTbl
  function rtvGraveTbl(ptrSOUR)
    local ptr       = fhNewItemPtr()
--    local ptrLink   = fhNewItemPtr()
    local ptrWork   = fhNewItemPtr()
    local ptrBURI   = fhNewItemPtr()
    local ptrCREM   = fhNewItemPtr()
    ptr = ptrINDI:Clone()

    strWebPage = spc
    strMemorial = spc
    ptrBURI:MoveTo(ptr,"~.BURI.SOUR>")
    if ptrBURI:IsSame(ptrSOUR) then
      ptrWork = fhGetItemPtr(ptr,"~.BURI.SOUR.PAGE")
    else
      ptrCREM:MoveTo(ptr,"~.CREM.SOUR>")
      if ptrCREM:IsSame(ptrSOUR) then
        ptrWork = fhGetItemPtr(ptr,"~.CREM.SOUR.PAGE")
      end
    end
    strWebPage = fhGetValueAsText(ptrWork)
    table.insert(tblFaG, strWebPage)
    cwFaG = math.max(cwFaG, #strWebPage)
    table.insert(tblPAGE, ptrWork:Clone())
    local strMemorial = strWebPage:gsub(".-(%d+).*","%1")
    table.insert(tblMML, strMemorial)
    cwmml = math.max(cwmml, #strMemorial)
  end --fn rtvGraveTbl

  function getItemCount(ptrItem)   -- Count number of instances of Item
    local ent = 0
    while ptrItem:IsNotNull() do
      ent = ent + 1
      ptrItem:MoveNext("SAME_TAG")
    end
    return ent
  end -- fn getItemCount

-- Main Code
  local temp = os.date("*t")
  local now = tonumber(temp.year)
  fhSetStringEncoding("UTF-8")
  spc = fhConvertANSItoUTF8(string.char(0x00A0))
  BLANKS = string.rep(spc,4)
  cwnam    = 31                    -- name field 
  cwnm5    = 31                    -- name field 
  cwptr    = 0                     -- link field 
  cwdat    = 64                    -- *(    -    )* format dates 
  cwrel    = 0                     -- relationship 
  cwFaG    = 0                     -- findagrave...
  cwmml    = 0                     -- memorial number

  fBirth = BLANKS
  fDeath = BLANKS
  local tblfB        = {} -- Memorial Number
  local tblfD        = {} -- Memorial Number

  Birth  = BLANKS
  cBirth = BLANKS
  eBirth = BLANKS
  mBirth = BLANKS
  lBirth = BLANKS

  Death  = BLANKS
  cDeath = BLANKS
  eDeath = BLANKS
  mDeath = BLANKS
  lDeath = BLANKS

  eyb = spc
  eyd = spc

  local ptrRoot = fhCallBuiltInFunction("FileRoot")
  ptrINDI = fhNewItemPtr()
  ptrSOUR = fhNewItemPtr()
  local ptrNAM = fhNewItemPtr()

-- Prompt for Source
  tblParm = fhPromptUserForRecordSel('SOUR',1)

-- nothing chosen
  if #tblParm == 0 then
    fhMessageBox('Plugin Cancelled')
    return
  end

--if citation has no links
  local iCitations = fhCallBuiltInFunction('LinksTo', tblParm[1])
  if iCitations == 0 then
    fhMessageBox('No Citations Found')
    return
  end

  ptrSOUR = tblParm[1]

  ptrINDI:MoveToFirstRecord("INDI")
  while ptrINDI:IsNotNull() do                         -- Process each Individual record
    relation = fhCallBuiltInFunction("Relationship", ptrRoot, ptrINDI, "TEXT", 1)
    if relation ~= "" then
      local Name = fhGetItemText(ptrINDI, "~.NAME:SURNAME_FIRST")
      local NameSuffix = fhGetItemText(ptrINDI,"~.NAME.NSFX")
      if NameSuffix ~= "" then
        Name = (Name.. ' ' ..NameSuffix)
      end
      table.insert(tblName, Name)
      cwnam = math.max(cwnam, #Name)
      table.insert(tblINDI, ptrINDI:Clone())
if Name == "[HOWDEN], Mary " then
debug = debug
end 
      Name = ""
      local sex = fhGetItemText(ptrINDI, "~.SEX")
      if sex == "Female" then
        ptrNAM:MoveTo(ptrINDI,"~.NAME")
        nc = getItemCount(ptrNAM)
        if nc > 1 then
          strDataOffset = "~.NAME[" .. nc .. "]:SURNAME_FIRST"
          Name = fhGetItemText(ptrINDI, strDataOffset)
        end
      end
      table.insert(tblMarrName, Name)
      cwnm5 = math.max(cwnm5, #Name)

      Birth  = BLANKS
      cBirth = BLANKS
      eBirth = BLANKS
      mBirth = BLANKS
      lBirth = BLANKS

      Death  = BLANKS
      cDeath = BLANKS
      eDeath = BLANKS
      mDeath = BLANKS
      lDeath = BLANKS
      sort   = 0
      debug = nil

      eyb = 'f'
      fBirth = fhGetItemText(ptrINDI,"~.BIRT.DATE:YEAR")
      Birth = fBirth
      table.insert(tblfB, fBirth)

      if Birth == "" then
        eyb   = spc
        Birth = BLANKS
        sort =  1
        Birth = estBirth(ptrINDI)
        if Birth ~= BLANKS then
          if tonumber(Birth) > now then
            eyb   = "o" 
            Birth = BLANKS
            sort = 99999
          else  
            Birth = tonumber(Birth)
            sort = Birth
          end
        end  
      elseif (Birth ~= BLANKS) then
        Birth = tonumber(Birth)
        sort = Birth
      end
      len = 16 - string.len(sort)
      sort = (string.rep(spc,len).. sort)
      table.insert(tblsort, sort)


      eyd = 'f'
      fDeath = fhGetItemText(ptrINDI,"~.DEAT.DATE:YEAR")
      Death = fDeath
      table.insert(tblfD, fDeath)
      
      if Death == "" then
        if tonumber(cDeath) ~= nil  then
          eyd   = "*" 
          Death = cDeath
        else
          eyd   = spc
          Death = BLANKS
          Death = estDeath(ptrINDI)
          if Death ~= BLANKS then
            Death = tonumber(Death)
            if Death > now then
              eyd   = "o" 
              Death = BLANKS
            end
          end
        end
      end


      local Dates =  eyb .. "(" .. Birth .. " - " .. Death .. ")" .. eyd
      table.insert(tblDates, Dates)

      table.insert(tblRel, relation)
      cwrel = math.max(cwrel, #relation)

      rtvGraveTbl(ptrSOUR)
    end -- while relation not null
    ptrINDI:MoveNext()                               -- Move to next Individual record
  end  --while INDI not null

  cwnam    = (  4 * 31)                -- name field 
  cwnm5    = (  4 * cwnm5)             -- name field 
  cwptr    =    0                      -- link field 
  cwdat    =   64                      -- *(    -    )* format dates 
  cwyr     =   24                      -- year field
  cwrel    = (  4 * cwrel)             -- relationship 
  cwFaG    = (  4 * cwFaG) + 4         -- findagrave...
  cwmml    = (  4 * cwmml) + 4         -- memorial number
  chsf     = string.rep("<", 18)  

  fhOutputResultSetTitles("Relations")
  fhOutputResultSetColumn("Name",     "text", tblName,     #tblName,     cwnam, "align_left")
  fhOutputResultSetColumn("ptr",      "item", tblINDI,     #tblINDI,     cwptr, "align_left", 1, true, "default", "buddy")
  fhOutputResultSetColumn("Dates",    "text", tblDates,    #tblDates,    cwdat, "align_right")
  fhOutputResultSetColumn(chsf,       "text", tblsort,     #tblsort,        12, "align_left")
--  fhOutputResultSetColumn("fB", "text", tblfB, #tblfB, 20, "align_mid")
--  fhOutputResultSetColumn("fD", "text", tblfD, #tblfD, 20, "align_mid")
  fhOutputResultSetColumn("AName",    "text", tblMarrName, #tblMarrName, cwnm5, "align_left")
  fhOutputResultSetColumn("Relation", "text", tblRel,      #tblRel,      cwrel, "align_left")
  fhOutputResultSetColumn("FaG",      "text", tblFaG,      #tblFaG,      cwFaG, "align_left")
  fhOutputResultSetColumn("ptr",      "item", tblPAGE,     #tblPAGE,     cwptr, "align_left",0, true, "default", "buddy")
  fhOutputResultSetColumn("Mem#",     "text", tblMML,      #tblMML,      cwmml, "align_right")

  return
FH V.6.2.7 Win 10 64 bit
User avatar
tatewise
Megastar
Posts: 28414
Joined: 25 May 2010 11:00
Family Historian: V7
Location: Torbay, Devon, UK
Contact:

Re: Sort of a sort

Post by tatewise »

Ron, in Tools > Preferences > Estimates what is your setting for Average Life span and Maximum Life.
The estimation functions use those Estimates and others when no better Dates exist.
If either are about 144 then that explains why estimated lifetime is so large.
Mike Tate ~ researching the Tate and Scott family history ~ tatewise ancestry
User avatar
Ron Melby
Megastar
Posts: 928
Joined: 15 Nov 2016 15:40
Family Historian: V6.2

Re: Sort of a sort

Post by Ron Melby »

avg = 65
max = 90

max marr 70 both
FH V.6.2.7 Win 10 64 bit
User avatar
tatewise
Megastar
Posts: 28414
Joined: 25 May 2010 11:00
Family Historian: V7
Location: Torbay, Devon, UK
Contact:

Re: Sort of a sort

Post by tatewise »

OK, there must be some other reason why 1939 is the estimated Death date.

Using LifeDates2, if death date is not available, the function will use the burial date, but if that is not available it will estimate the latest possible death date using various means.

One possibility is it uses the latest possible birth date and then adds Tools > Preferences > Estimates > Maximum Life.
So is it possible the maximum estimated birth date is 1849 and by adding 90 gives 1939 ?

In your example, birth date 1794 is a Mid point estimate as it is prefixed by m.
If you debug your estBirth(...) function for that person, what .Min and .Max dates are given i.e. eBirth and lBirth?
Mike Tate ~ researching the Tate and Scott family history ~ tatewise ancestry
User avatar
Ron Melby
Megastar
Posts: 928
Joined: 15 Nov 2016 15:40
Family Historian: V6.2

Re: Sort of a sort

Post by Ron Melby »

dont remember e birth but lbirth was 1849
FH V.6.2.7 Win 10 64 bit
User avatar
tatewise
Megastar
Posts: 28414
Joined: 25 May 2010 11:00
Family Historian: V7
Location: Torbay, Devon, UK
Contact:

Re: Sort of a sort

Post by tatewise »

QED

You will get odd dates if you choose Mid Birth and then use LifeDates2 to estimate Death date which may be latest.
Mike Tate ~ researching the Tate and Scott family history ~ tatewise ancestry
User avatar
Ron Melby
Megastar
Posts: 928
Joined: 15 Nov 2016 15:40
Family Historian: V6.2

Re: Sort of a sort

Post by Ron Melby »

it appears that lifedates2 isnt much of a program

I think the best solution will be to use mid.birth as birth and late.birth as death.
FH V.6.2.7 Win 10 64 bit
User avatar
tatewise
Megastar
Posts: 28414
Joined: 25 May 2010 11:00
Family Historian: V7
Location: Torbay, Devon, UK
Contact:

Re: Sort of a sort

Post by tatewise »

Did you really mean "... and late.birth as death" which makes no sense?

Or should that say "... and late.death as death" ?

But if you do that you'll get the same 1794-1939 dates that you didn't like earlier, as LifeDates2 estimated death will be the same as late.death.

You will only get rational lifespans if you use early or mid or late for both birth and death dates.
I would go for mid.birth and mid.death.
Mike Tate ~ researching the Tate and Scott family history ~ tatewise ancestry
User avatar
Ron Melby
Megastar
Posts: 928
Joined: 15 Nov 2016 15:40
Family Historian: V6.2

Re: Sort of a sort

Post by Ron Melby »

I have went thru and inspected all my code and did minor fixes to standardize how birth and death are calculated

I now chose mid.birth and mid.death

mid.birth seems to be pretty accurate. even way out mid.death is often BLANK (transformed)

lifedates2 is wild for the scant info profiles.

so, at present the resolution is:

if its not file, and I have to run thru guessing routines,
if I have a birth, after gyrations, and I usually do, if death is greater than now year, or age comparison is over 89 add 89 to birth and use as death, if its still past now year, its a child or someone still probably alive(such as myself 64 years old) , blank it.

But I have seen my death in FH, and it aint pretty.

of 4132 people I have 6 known deaths over one hundred, so, I am not going to miss too many ages day to day. I of course have known issues, not everyone lives to 89 but using that as my estimate. the major idea is to get the birth within 5-10 +or- to try and hunt them down, and the death for at least 50% within the same. I assume (and probably incorrectly), that the functions generations is ancestors, I may try and build a descendants estimate and work forward to try and work backward, but after all that all relations plugin, I need to catch a little never-mind for a bit.
FH V.6.2.7 Win 10 64 bit
Post Reply