Migrate Census Family to Individual Events.fh_lua--[[
@Title: Migrate Census Family to Individual Events
@Type: Standard
@Author: Mike Tate
@Contributors:
@Version: 1.3
@Keywords:
@LastUpdated: 05 Feb 2026
@Licence: This plugin is copyright (c) 2026 Mike Tate & contributors and is licensed under the MIT License which is hereby incorporated by reference (see https://pluginstore.family-historian.co.uk/fh-plugin-licence)
@Description: Migrate Census (family) Events to Individual Census Events
@V1.3: Handle fhCreateItem() error on invalid tag; fhInitialise();
@V1.2: Handle rich text notes and citation metafields;
@V1.1: FH V7 Lua 3.5 IUP 3.28;
@V1.0: Initial Plugin Store Version;
]]
local strVersion = "1.3"
if fhGetAppVersion() > 5 then fhSetStringEncoding("UTF-8") end -- Cater for FH V6 Unicode
function CopyChildBranch(ptrSource,ptrTarget)
local strTag = fhGetTag(ptrSource)
if strTag == "_FMT" then return end -- Skip rich text format tag -- V1.2
if strTag == "_FIELD" then
strTag = fhGetMetafieldShortcut(ptrSource) -- Handle citation metafield -- V1.2
end
if strTag == "HUSB" or strTag == "WIFE" then return end -- Escape on invalid tag such as Family Census HUSB & WIFE -- V1.3
local ptrNew = fhCreateItem(strTag,ptrTarget,true)
if ptrNew:IsNull() then return end -- Escape if item not created -- V1.2
fhSetValue_Copy(ptrNew,ptrSource)
CopyChildren(ptrSource,ptrNew)
return ptrNew
end
function CopyChildren(ptrSource,ptrTarget)
local ptrFrom = ptrSource:Clone()
ptrFrom:MoveToFirstChildItem(ptrFrom)
while ptrFrom:IsNotNull() do
CopyChildBranch(ptrFrom,ptrTarget)
ptrFrom:MoveNext()
end
end
function Main()
local arrInd = {} -- Result Set tables
arrInd[1] = {}
arrInd[2] = {}
local arrCen = {}
arrCen[1] = {}
arrCen[2] = {}
local arrFam = {}
local intFam = 0
local tblSpouse = { "~.HUSB[1]>", "~.WIFE[1]>", "~.HUSB[2]>", "~.WIFE[2]>" }
local ptrRef = fhNewItemPtr()
local ptrFam = fhNewItemPtr()
ptrFam:MoveToFirstRecord("FAM") -- Loop through each Family Record
while ptrFam:IsNotNull() do
ptrRef:MoveTo(ptrFam,"~.CENS") -- Loop through each instance of each Census (family) Event
while ptrRef:IsNotNull() do
local intInd = 0
table.insert(arrFam,ptrFam:Clone())
for intRef, strRef in pairs(tblSpouse) do -- Find each Husband and Wife Individual
local ptrInd = fhGetItemPtr(ptrFam,strRef)
if ptrInd:IsNotNull() then
local ptrNew = CopyChildBranch(ptrRef,ptrInd) -- Copy entire Census Event to each Individual
intInd = intInd + 1
table.insert(arrInd[intInd],ptrInd:Clone())
table.insert(arrCen[intInd],ptrNew:Clone())
local strAge = strRef:gsub(">",".AGE")
local ptrAge = fhGetItemPtr(ptrRef,strAge) -- Copy the Spouse Age to new Census Event
if ptrAge:IsNotNull() then
ptrNew = fhCreateItem("AGE",ptrNew)
fhSetValue_Copy(ptrNew,ptrAge)
end
end
end
if intInd == 1 then
table.insert(arrInd[2],fhNewItemPtr())
table.insert(arrCen[2],fhNewItemPtr())
end
local ptrOld = ptrRef:Clone()
ptrRef:MoveNext("SAME_TAG")
local isOK = fhDeleteItem(ptrOld) -- Delete old Census (family) Event
intFam = intFam + 1
end
ptrFam:MoveNext()
end
if #arrFam > 0 then -- Output Result Set
fhOutputResultSetTitles("Migrate Census Family to Individual Events "..strVersion)
fhOutputResultSetColumn("Spouse 1","item",arrInd[1] ,#arrFam,120,"align_left")
fhOutputResultSetColumn("Census 1","item",arrCen[1] ,#arrFam,100,"align_left")
fhOutputResultSetColumn("Spouse 2","item",arrInd[2] ,#arrFam,120,"align_left")
fhOutputResultSetColumn("Census 2","item",arrCen[2] ,#arrFam,100,"align_left")
fhOutputResultSetColumn("Family" ,"item",arrFam ,#arrFam,200,"align_left")
end
if intFam > 0 then
fhMessageBox("\n Migrated "..intFam.." Census (family) to Individual Events. \n\n Undo changes with 'Edit > Undo Plugin Updates' \n or 'File > Backup/Restore > Revert to Snapshot'.\n")
else
fhMessageBox("\n No Census (family) Events found. \n")
end
end
fhInitialise(5,0,0,"save_recommended") -- V1.3
Main()
--[[
@Title: Migrate Census Family to Individual Events
@Type: Standard
@Author: Mike Tate
@Contributors:
@Version: 1.3
@Keywords:
@LastUpdated: 05 Feb 2026
@Licence: This plugin is copyright (c) 2026 Mike Tate & contributors and is licensed under the MIT License which is hereby incorporated by reference (see https://pluginstore.family-historian.co.uk/fh-plugin-licence)
@Description: Migrate Census (family) Events to Individual Census Events
@V1.3: Handle fhCreateItem() error on invalid tag; fhInitialise();
@V1.2: Handle rich text notes and citation metafields;
@V1.1: FH V7 Lua 3.5 IUP 3.28;
@V1.0: Initial Plugin Store Version;
]]
local strVersion = "1.3"
if fhGetAppVersion() > 5 then fhSetStringEncoding("UTF-8") end -- Cater for FH V6 Unicode
function CopyChildBranch(ptrSource,ptrTarget)
local strTag = fhGetTag(ptrSource)
if strTag == "_FMT" then return end -- Skip rich text format tag -- V1.2
if strTag == "_FIELD" then
strTag = fhGetMetafieldShortcut(ptrSource) -- Handle citation metafield -- V1.2
end
if strTag == "HUSB" or strTag == "WIFE" then return end -- Escape on invalid tag such as Family Census HUSB & WIFE -- V1.3
local ptrNew = fhCreateItem(strTag,ptrTarget,true)
if ptrNew:IsNull() then return end -- Escape if item not created -- V1.2
fhSetValue_Copy(ptrNew,ptrSource)
CopyChildren(ptrSource,ptrNew)
return ptrNew
end
function CopyChildren(ptrSource,ptrTarget)
local ptrFrom = ptrSource:Clone()
ptrFrom:MoveToFirstChildItem(ptrFrom)
while ptrFrom:IsNotNull() do
CopyChildBranch(ptrFrom,ptrTarget)
ptrFrom:MoveNext()
end
end
function Main()
local arrInd = {} -- Result Set tables
arrInd[1] = {}
arrInd[2] = {}
local arrCen = {}
arrCen[1] = {}
arrCen[2] = {}
local arrFam = {}
local intFam = 0
local tblSpouse = { "~.HUSB[1]>", "~.WIFE[1]>", "~.HUSB[2]>", "~.WIFE[2]>" }
local ptrRef = fhNewItemPtr()
local ptrFam = fhNewItemPtr()
ptrFam:MoveToFirstRecord("FAM") -- Loop through each Family Record
while ptrFam:IsNotNull() do
ptrRef:MoveTo(ptrFam,"~.CENS") -- Loop through each instance of each Census (family) Event
while ptrRef:IsNotNull() do
local intInd = 0
table.insert(arrFam,ptrFam:Clone())
for intRef, strRef in pairs(tblSpouse) do -- Find each Husband and Wife Individual
local ptrInd = fhGetItemPtr(ptrFam,strRef)
if ptrInd:IsNotNull() then
local ptrNew = CopyChildBranch(ptrRef,ptrInd) -- Copy entire Census Event to each Individual
intInd = intInd + 1
table.insert(arrInd[intInd],ptrInd:Clone())
table.insert(arrCen[intInd],ptrNew:Clone())
local strAge = strRef:gsub(">",".AGE")
local ptrAge = fhGetItemPtr(ptrRef,strAge) -- Copy the Spouse Age to new Census Event
if ptrAge:IsNotNull() then
ptrNew = fhCreateItem("AGE",ptrNew)
fhSetValue_Copy(ptrNew,ptrAge)
end
end
end
if intInd == 1 then
table.insert(arrInd[2],fhNewItemPtr())
table.insert(arrCen[2],fhNewItemPtr())
end
local ptrOld = ptrRef:Clone()
ptrRef:MoveNext("SAME_TAG")
local isOK = fhDeleteItem(ptrOld) -- Delete old Census (family) Event
intFam = intFam + 1
end
ptrFam:MoveNext()
end
if #arrFam > 0 then -- Output Result Set
fhOutputResultSetTitles("Migrate Census Family to Individual Events "..strVersion)
fhOutputResultSetColumn("Spouse 1","item",arrInd[1] ,#arrFam,120,"align_left")
fhOutputResultSetColumn("Census 1","item",arrCen[1] ,#arrFam,100,"align_left")
fhOutputResultSetColumn("Spouse 2","item",arrInd[2] ,#arrFam,120,"align_left")
fhOutputResultSetColumn("Census 2","item",arrCen[2] ,#arrFam,100,"align_left")
fhOutputResultSetColumn("Family" ,"item",arrFam ,#arrFam,200,"align_left")
end
if intFam > 0 then
fhMessageBox("\n Migrated "..intFam.." Census (family) to Individual Events. \n\n Undo changes with 'Edit > Undo Plugin Updates' \n or 'File > Backup/Restore > Revert to Snapshot'.\n")
else
fhMessageBox("\n No Census (family) Events found. \n")
end
end
fhInitialise(5,0,0,"save_recommended") -- V1.3
Main()
Source:Migrate-Census-Family-to-Individual-Events-3.fh_lua