Relatedness Report.fh_lua--[[
@Title: DNA Relatedness
@Author: Jane Taubman
@Version: 1.3
@LastUpdated: 30 Nov 2020
@Description: Simple output report showing the relations of a person with the computed shared DNA
1.3 V7 Compatibility
]]
require "iuplua"
iup.SetGlobal("CUSTOMQUITMESSAGE","YES")
-- Settings
local TotalCM = 6800
-- percentage, min %, max %
local cmTable = {
{100,100,100},
{50,32,54},
{25,18,32},
{12.5,8,18.5},
{6.25,3,9},
{3.125,1,5},
{1.56,0.42,3},
{0.78,0,1.52},
{0.4,0,1}
}
function main()
local tblIndi = fhPromptUserForRecordSel('INDI',1)
if #tblIndi == 0 then return end
local pMain = tblIndi[1]
local tblResults = createResultTable()
-- V6 Template
local template = "Gens%. Up=(%d+), Gens%. Down=(%d+), Half=(%d+), Spouse Start=(%d+), Spouse End=(%d+)"
-- Define Columns
tblResults.indi = {title='Record'}
tblResults.id = {title='id',type='integer',align='align_right',width=20}
tblResults.relationship = {title='Relationship'}
tblResults.relatedness = {title='Relatedness',align='align_right',type='text',sort=1,sortAscending=false, width=40}
tblResults.cm = {title='Expected CM',align='align_right',type='integer', width=30}
tblResults.mincm = {title='Min CM',align='align_right',type='integer', width=30}
tblResults.maxcm = {title='Max CM',align='align_right',type='integer',width=35}
local ProgressBar = NewProgressBar()
ProgressBar.Start( "Computing Relationships",countIndi())
for pi in records('INDI') do
ProgressBar.Step() -- Defaults to step by 1
if ProgressBar.Stop() then
break
end
local i = 1
local totalRelatedness = 0
local relationship = ''
repeat
local code = fhCallBuiltInFunction('Relationship',pMain,pi,'CODE',i)
local gensUp,gensDown,half,spouseStart,spouseEnd = string.match(code,template)
if code == "" then -- no more relationships
i = 0
else
if spouseStart == "0" and spouseEnd == "0" then -- related but not by marriage
relationship = relationship..' '..i..'='..fhCallBuiltInFunction('Relationship',pMain,pi,'TEXT',i)
local relatedness = .5^(math.abs(gensUp) + math.abs(gensDown))
if half == "0" and gensUp > "0" and gensDown > "0" then
-- Double if not Ancestor or Decendent and not step relationship
relatedness = relatedness * 2
end
totalRelatedness = totalRelatedness + relatedness
end
i = i + 1
end
until (i == 0)
if totalRelatedness > 0 then
-- Add Row
tblResults:newRow()
-- Set Columns
tblResults.indi:set(pi:Clone())
tblResults.relationship:set(relationship)
tblResults.id:set(fhGetRecordId(pi))
local totalpercent = totalRelatedness * 100
local padvalue = string.format("%12f",totalpercent)
tblResults.relatedness:set(padvalue)
tblResults.cm:set(TotalCM * totalRelatedness)
-- Read table to find range
local breakflg = false
for key,value in ipairs(cmTable) do
if totalpercent >= value[1] then
tblResults.mincm:set(value[2]/100*TotalCM)
tblResults.maxcm:set(value[3]/100*TotalCM)
breakflg = true
end
if breakflg then break end
end
ProgressBar.Message( tblResults:rowCount().." Genetic Relationships Found" )
end
end
ProgressBar.Message( "Outputing Results" )
local sTitle = "Genetic Relationships with "..fhGetDisplayText(pMain)
fhOutputResultSetTitles(sTitle, sTitle, "Output Date: %#x")
tblResults:outputResults()
ProgressBar.Close()
iup.Close()
end
function countIndi()
local count = 0
for pi in records('INDI') do
count = count + 1
end
return count
end
function records(type)
local pi = fhNewItemPtr()
local p2 = fhNewItemPtr()
pi:MoveToFirstRecord(type)
return function ()
p2:MoveTo(pi)
pi:MoveNext()
if p2:IsNotNull() then return p2 end
end
end
function createResultTable()
local tblOutput_mt = {} -- create metatable
local iC = 0 -- Define count of lines
local tblOutput = {} -- Define Columns Table
tblOutput_mt.col = 0
tblOutput_mt.seq = {}
tblOutput_mt.__newindex =
function (t,k,v)
-- Set Values to Defaults if not supplied
if v.content == nil then v.content = {} end
if v.title == nil then v.title = k end
if v.type == nil then v.type = 'notset' end
if v.width == nil then v.width = 140 end
if v.align == nil then v.align = 'align_left' end
if v.sort == nil then v.sort = 0 end
if v.sortAscending == nil then v.sortAscending = true end
if v.sortType == nil then v.sortType = 'default' end
if v.visibility == nil then v. visibility = 'show' end
v.set =
function(self,value)
self.content[iC] = value
if self.type == 'notset' then
if type(value) == 'string' then
self.type = 'text'
elseif type(value) == 'number' then
self.type = 'integer'
self.width = '30'
else
self.type = 'item'
end
end
end
rawset(t,k,v) -- update original table
local m = getmetatable(t)
m.col = m.col + 1
table.insert(m.seq,k)
end
tblOutput_mt.__call =
function (t)
local i = 0
local m = getmetatable(t)
local n = table.getn(m.seq)
return function ()
i = i + 1
if i <= n then
return t[m.seq[i]]
end
end
end
tblOutput.newRow = function(t)
iC = iC + 1
end
tblOutput.rowCount = function(t)
return iC
end
tblOutput.outputResults = function(self)
for l in self() do
fhOutputResultSetColumn(l.title, l.type, l.content, iC, l.width,l.align,l.sort,l.sortAscending,l.sortType,l.visibility )
end
end
setmetatable(tblOutput, tblOutput_mt)
return tblOutput
end
function table.getnx(t)
local count = 0
for _, __ in pairs(t) do
count = count + 1
end
return count
end
--[[
@Title: Progress Bar (drop in)
@Author: Jane Taubman / Mike Tate
@LastUpdated: January 2013
@Description: Allows easy adding of a Progress Bar to any long running Plugin
]]
-- Progress Bar Function Prototype --
function NewProgressBar(tblGauge)
local tblGauge = tblGauge or {} -- Optional table of external parameters
local strFont = tblGauge.Font or nil -- Font dialogue default is current font
local strButton = tblGauge.Button or "255 0 0" -- Button colour default is red
local strBehind = tblGauge.Behind or "255 255 255" -- Background colour default is white
local intShowX = tblGauge.ShowX or iup.CENTER -- Show window default position is central
local intShowY = tblGauge.ShowY or iup.CENTER
local intMax, intVal, intPercent, intStart, intDelta, intScale, strClock, isBarStop
local lblText, barGauge, lblDelta, btnStop, dlgGauge
local function doFocus() -- Bring the Progress Bar window into Focus
dlgGauge.bringfront="YES" -- If used too often, inhibits other windows scroll bars, etc
end -- local function doFocus
local function doUpdate() -- Update the Progress Gauge and the Delta % with clock
barGauge.value = intVal
local s = string.format("%4d %% %s ",math.floor(intPercent),strClock)
lblDelta.title = s
end -- local function doUpdate
local function doReset() -- Reset all dialogue variables and Update display
intVal = 0 -- Current value of Progress Bar
intPercent = 0 -- Percentage of progress
intStart = os.time() -- Start time of progress
intDelta = 0 -- Delta time of progress
intScale = math.ceil( intMax / 1000 ) -- Scale of percentage per second (this guess is corrected in Step function)
strClock = "00 : 00 : 00" -- Clock delta time display
isBarStop = false -- Stop button pressed signal
doUpdate()
doFocus()
end -- local function doReset
local tblProgressBar = {
Start = function(strTitle,intMaximum) -- Create & start Progress Bar window
if not dlgGauge then
strTitle = strTitle or "" -- Dialogue and button title
intMax = intMaximum or 100 -- Maximun range of Progress Bar, default is 100
local strSize = tostring( math.max( 100, string.len(" Stop "..strTitle) * 8 ) ).."x30" -- Adjust Stop button size to Title
lblText = iup.label { title=" ", expand="YES", alignment="ACENTER", tip="Progress Message" }
barGauge = iup.progressbar { rastersize="400x30", value=0, max=intMax, tip="Progress Bar" }
lblDelta = iup.label { title=" ", expand="YES", alignment="ACENTER", tip="Percentage and Elapsed Time" }
btnStop = iup.button { title=" Stop "..strTitle, rastersize=strSize, fgcolor=strButton, tip="Stop Progress Button", action=function() isBarStop = true end } -- Signal Stop button pressed return iup.CLOSE -- Often caused main GUI to close !!!
dlgGauge = iup.dialog { title=strTitle.." Progress ", font=strFont, dialogframe="YES", background=strBehind, -- Remove Windows minimize/maximize menu
iup.vbox{ alignment="ACENTER", gap="10", margin="10x10",
lblText,
barGauge,
lblDelta,
btnStop,
},
move_cb = function(self,x,y) tblGauge.ShowX = x tblGauge.ShowY = y end,
close_cb = btnStop.action, -- Windows Close button = Stop button
}
dlgGauge:showxy(intShowX,intShowY) -- Show the Progress Bar window
doReset() -- Reset the Progress Bar display
end
end,
Message = function(strText) -- Show the Progress Bar message
if dlgGauge then lblText.title = strText end
end,
Step = function(intStep) -- Step the Progress Bar forward
if dlgGauge then
intVal = intVal + ( intStep or 1 ) -- Default step is 1
local intNew = math.ceil( intVal / intMax * 100 * intScale ) / intScale
if intPercent ~= intNew then -- Update progress once per percent or per second, whichever is smaller
intPercent = math.max( 0, intNew ) -- Ensure percentage is greater than zero
if intVal > intMax then intVal = intMax intPercent = 100 end -- Ensure values do not exceed maximum
intNew = os.difftime(os.time(),intStart)
if intDelta < intNew then -- Update clock of elapsed time
intDelta = intNew
intScale = math.ceil( intDelta / intPercent ) -- Scale of seconds per percentage step
local intHour = math.floor( intDelta / 3600 )
local intMins = math.floor( intDelta / 60 - intHour * 60 )
local intSecs = intDelta - intMins * 60 - intHour * 3600
strClock = string.format("%02d : %02d : %02d",intHour,intMins,intSecs)
end
doUpdate() -- Update the Progress Bar display
end
iup.LoopStep()
end
end,
Focus = function()
if dlgGauge then doFocus() end -- Bring the Progress Bar window to front
end,
Reset = function() -- Reset the Progress Bar display
if dlgGauge then doReset() end
end,
Stop = function() -- Check if Stop button pressed
iup.LoopStep()
return isBarStop
end,
Close = function() -- Close the Progress Bar window
isBarStop = false
if dlgGauge then dlgGauge:destroy() dlgGauge = nil end
if iup.Close then
iup.Close()
end
end,
} -- end newProgressBar
return tblProgressBar
end -- function NewProgressBar
if fhGetAppVersion() > 6 then
function table.getn(t)
local count = 0
for _, __ in pairs(t) do
count = count + 1
end
return count
end
function unpack(t)
return table.unpack(t)
end
end
----------------------------------
main()
--[[
@Title: DNA Relatedness
@Author: Jane Taubman
@Version: 1.3
@LastUpdated: 30 Nov 2020
@Description: Simple output report showing the relations of a person with the computed shared DNA
1.3 V7 Compatibility
]]
require "iuplua"
iup.SetGlobal("CUSTOMQUITMESSAGE","YES")
-- Settings
local TotalCM = 6800
-- percentage, min %, max %
local cmTable = {
{100,100,100},
{50,32,54},
{25,18,32},
{12.5,8,18.5},
{6.25,3,9},
{3.125,1,5},
{1.56,0.42,3},
{0.78,0,1.52},
{0.4,0,1}
}
function main()
local tblIndi = fhPromptUserForRecordSel('INDI',1)
if #tblIndi == 0 then return end
local pMain = tblIndi[1]
local tblResults = createResultTable()
-- V6 Template
local template = "Gens%. Up=(%d+), Gens%. Down=(%d+), Half=(%d+), Spouse Start=(%d+), Spouse End=(%d+)"
-- Define Columns
tblResults.indi = {title='Record'}
tblResults.id = {title='id',type='integer',align='align_right',width=20}
tblResults.relationship = {title='Relationship'}
tblResults.relatedness = {title='Relatedness',align='align_right',type='text',sort=1,sortAscending=false, width=40}
tblResults.cm = {title='Expected CM',align='align_right',type='integer', width=30}
tblResults.mincm = {title='Min CM',align='align_right',type='integer', width=30}
tblResults.maxcm = {title='Max CM',align='align_right',type='integer',width=35}
local ProgressBar = NewProgressBar()
ProgressBar.Start( "Computing Relationships",countIndi())
for pi in records('INDI') do
ProgressBar.Step() -- Defaults to step by 1
if ProgressBar.Stop() then
break
end
local i = 1
local totalRelatedness = 0
local relationship = ''
repeat
local code = fhCallBuiltInFunction('Relationship',pMain,pi,'CODE',i)
local gensUp,gensDown,half,spouseStart,spouseEnd = string.match(code,template)
if code == "" then -- no more relationships
i = 0
else
if spouseStart == "0" and spouseEnd == "0" then -- related but not by marriage
relationship = relationship..' '..i..'='..fhCallBuiltInFunction('Relationship',pMain,pi,'TEXT',i)
local relatedness = .5^(math.abs(gensUp) + math.abs(gensDown))
if half == "0" and gensUp > "0" and gensDown > "0" then
-- Double if not Ancestor or Decendent and not step relationship
relatedness = relatedness * 2
end
totalRelatedness = totalRelatedness + relatedness
end
i = i + 1
end
until (i == 0)
if totalRelatedness > 0 then
-- Add Row
tblResults:newRow()
-- Set Columns
tblResults.indi:set(pi:Clone())
tblResults.relationship:set(relationship)
tblResults.id:set(fhGetRecordId(pi))
local totalpercent = totalRelatedness * 100
local padvalue = string.format("%12f",totalpercent)
tblResults.relatedness:set(padvalue)
tblResults.cm:set(TotalCM * totalRelatedness)
-- Read table to find range
local breakflg = false
for key,value in ipairs(cmTable) do
if totalpercent >= value[1] then
tblResults.mincm:set(value[2]/100*TotalCM)
tblResults.maxcm:set(value[3]/100*TotalCM)
breakflg = true
end
if breakflg then break end
end
ProgressBar.Message( tblResults:rowCount().." Genetic Relationships Found" )
end
end
ProgressBar.Message( "Outputing Results" )
local sTitle = "Genetic Relationships with "..fhGetDisplayText(pMain)
fhOutputResultSetTitles(sTitle, sTitle, "Output Date: %#x")
tblResults:outputResults()
ProgressBar.Close()
iup.Close()
end
function countIndi()
local count = 0
for pi in records('INDI') do
count = count + 1
end
return count
end
function records(type)
local pi = fhNewItemPtr()
local p2 = fhNewItemPtr()
pi:MoveToFirstRecord(type)
return function ()
p2:MoveTo(pi)
pi:MoveNext()
if p2:IsNotNull() then return p2 end
end
end
function createResultTable()
local tblOutput_mt = {} -- create metatable
local iC = 0 -- Define count of lines
local tblOutput = {} -- Define Columns Table
tblOutput_mt.col = 0
tblOutput_mt.seq = {}
tblOutput_mt.__newindex =
function (t,k,v)
-- Set Values to Defaults if not supplied
if v.content == nil then v.content = {} end
if v.title == nil then v.title = k end
if v.type == nil then v.type = 'notset' end
if v.width == nil then v.width = 140 end
if v.align == nil then v.align = 'align_left' end
if v.sort == nil then v.sort = 0 end
if v.sortAscending == nil then v.sortAscending = true end
if v.sortType == nil then v.sortType = 'default' end
if v.visibility == nil then v. visibility = 'show' end
v.set =
function(self,value)
self.content[iC] = value
if self.type == 'notset' then
if type(value) == 'string' then
self.type = 'text'
elseif type(value) == 'number' then
self.type = 'integer'
self.width = '30'
else
self.type = 'item'
end
end
end
rawset(t,k,v) -- update original table
local m = getmetatable(t)
m.col = m.col + 1
table.insert(m.seq,k)
end
tblOutput_mt.__call =
function (t)
local i = 0
local m = getmetatable(t)
local n = table.getn(m.seq)
return function ()
i = i + 1
if i <= n then
return t[m.seq[i]]
end
end
end
tblOutput.newRow = function(t)
iC = iC + 1
end
tblOutput.rowCount = function(t)
return iC
end
tblOutput.outputResults = function(self)
for l in self() do
fhOutputResultSetColumn(l.title, l.type, l.content, iC, l.width,l.align,l.sort,l.sortAscending,l.sortType,l.visibility )
end
end
setmetatable(tblOutput, tblOutput_mt)
return tblOutput
end
function table.getnx(t)
local count = 0
for _, __ in pairs(t) do
count = count + 1
end
return count
end
--[[
@Title: Progress Bar (drop in)
@Author: Jane Taubman / Mike Tate
@LastUpdated: January 2013
@Description: Allows easy adding of a Progress Bar to any long running Plugin
]]
-- Progress Bar Function Prototype --
function NewProgressBar(tblGauge)
local tblGauge = tblGauge or {} -- Optional table of external parameters
local strFont = tblGauge.Font or nil -- Font dialogue default is current font
local strButton = tblGauge.Button or "255 0 0" -- Button colour default is red
local strBehind = tblGauge.Behind or "255 255 255" -- Background colour default is white
local intShowX = tblGauge.ShowX or iup.CENTER -- Show window default position is central
local intShowY = tblGauge.ShowY or iup.CENTER
local intMax, intVal, intPercent, intStart, intDelta, intScale, strClock, isBarStop
local lblText, barGauge, lblDelta, btnStop, dlgGauge
local function doFocus() -- Bring the Progress Bar window into Focus
dlgGauge.bringfront="YES" -- If used too often, inhibits other windows scroll bars, etc
end -- local function doFocus
local function doUpdate() -- Update the Progress Gauge and the Delta % with clock
barGauge.value = intVal
local s = string.format("%4d %% %s ",math.floor(intPercent),strClock)
lblDelta.title = s
end -- local function doUpdate
local function doReset() -- Reset all dialogue variables and Update display
intVal = 0 -- Current value of Progress Bar
intPercent = 0 -- Percentage of progress
intStart = os.time() -- Start time of progress
intDelta = 0 -- Delta time of progress
intScale = math.ceil( intMax / 1000 ) -- Scale of percentage per second (this guess is corrected in Step function)
strClock = "00 : 00 : 00" -- Clock delta time display
isBarStop = false -- Stop button pressed signal
doUpdate()
doFocus()
end -- local function doReset
local tblProgressBar = {
Start = function(strTitle,intMaximum) -- Create & start Progress Bar window
if not dlgGauge then
strTitle = strTitle or "" -- Dialogue and button title
intMax = intMaximum or 100 -- Maximun range of Progress Bar, default is 100
local strSize = tostring( math.max( 100, string.len(" Stop "..strTitle) * 8 ) ).."x30" -- Adjust Stop button size to Title
lblText = iup.label { title=" ", expand="YES", alignment="ACENTER", tip="Progress Message" }
barGauge = iup.progressbar { rastersize="400x30", value=0, max=intMax, tip="Progress Bar" }
lblDelta = iup.label { title=" ", expand="YES", alignment="ACENTER", tip="Percentage and Elapsed Time" }
btnStop = iup.button { title=" Stop "..strTitle, rastersize=strSize, fgcolor=strButton, tip="Stop Progress Button", action=function() isBarStop = true end } -- Signal Stop button pressed return iup.CLOSE -- Often caused main GUI to close !!!
dlgGauge = iup.dialog { title=strTitle.." Progress ", font=strFont, dialogframe="YES", background=strBehind, -- Remove Windows minimize/maximize menu
iup.vbox{ alignment="ACENTER", gap="10", margin="10x10",
lblText,
barGauge,
lblDelta,
btnStop,
},
move_cb = function(self,x,y) tblGauge.ShowX = x tblGauge.ShowY = y end,
close_cb = btnStop.action, -- Windows Close button = Stop button
}
dlgGauge:showxy(intShowX,intShowY) -- Show the Progress Bar window
doReset() -- Reset the Progress Bar display
end
end,
Message = function(strText) -- Show the Progress Bar message
if dlgGauge then lblText.title = strText end
end,
Step = function(intStep) -- Step the Progress Bar forward
if dlgGauge then
intVal = intVal + ( intStep or 1 ) -- Default step is 1
local intNew = math.ceil( intVal / intMax * 100 * intScale ) / intScale
if intPercent ~= intNew then -- Update progress once per percent or per second, whichever is smaller
intPercent = math.max( 0, intNew ) -- Ensure percentage is greater than zero
if intVal > intMax then intVal = intMax intPercent = 100 end -- Ensure values do not exceed maximum
intNew = os.difftime(os.time(),intStart)
if intDelta < intNew then -- Update clock of elapsed time
intDelta = intNew
intScale = math.ceil( intDelta / intPercent ) -- Scale of seconds per percentage step
local intHour = math.floor( intDelta / 3600 )
local intMins = math.floor( intDelta / 60 - intHour * 60 )
local intSecs = intDelta - intMins * 60 - intHour * 3600
strClock = string.format("%02d : %02d : %02d",intHour,intMins,intSecs)
end
doUpdate() -- Update the Progress Bar display
end
iup.LoopStep()
end
end,
Focus = function()
if dlgGauge then doFocus() end -- Bring the Progress Bar window to front
end,
Reset = function() -- Reset the Progress Bar display
if dlgGauge then doReset() end
end,
Stop = function() -- Check if Stop button pressed
iup.LoopStep()
return isBarStop
end,
Close = function() -- Close the Progress Bar window
isBarStop = false
if dlgGauge then dlgGauge:destroy() dlgGauge = nil end
if iup.Close then
iup.Close()
end
end,
} -- end newProgressBar
return tblProgressBar
end -- function NewProgressBar
if fhGetAppVersion() > 6 then
function table.getn(t)
local count = 0
for _, __ in pairs(t) do
count = count + 1
end
return count
end
function unpack(t)
return table.unpack(t)
end
end
----------------------------------
main()Source:Relatedness-Report-1.fh_lua