Record Death Data UK.fh_lua

--[[
@Title: Record Death Data (UK)
@Type: Source-driven data entry
@Subtype: "Civil Registration Certificate"
@Author: Calico Pie
@Version: 1.3
@GH #10 #14 #15 #23 #54 #76 #112 #119
@Keywords: 
@LastUpdated: November 2021
@Description: Based on the format of the England and Wales Death Certificate since 1837, this plugin allows entry of all details including informant, residences, etc.
]]

fhInitialise(7)
fh = require 'fhUtils'
fh.setIupDefaults()

-- Local Globals
local form, templateName, templateDefault
local iForm = 1
local sPluginName = fhGetContextInfo 'CI_PLUGIN_NAME'
local s = {} -- Autofill table
local pCite -- Citation Object
local tUpdatedFields = {} -- Added and updated fields
local cells = {} -- Main Dialog sections
s['PLAC'] = fh.createPlaceList()
s['BIRTPLAC'] = s['PLAC']

--- Builds dialog and drives process.
function main()
	-- ------------------------------------------------------------Load citation and associated fields
	pCite = fh.loadPreparedCitation()

	if not pCite.result then
		fh.getParam(sPluginName, pCite.err)
		return
	end
	-- Ensure all needed fields have been completed
	if
		not (
			pCite:checkRequired('EN-TYPE', 'NM-PRINCIPAL', 'DT-DATE', 'PL-LOCATION')
		)
	then
		fh.getParam(
			sPluginName,
			'Not all required fields are set,\nplease ensure you have entered type, date, location and principal. '
		)
		return
	end
	if pCite:getValue 'EN-TYPE' ~= 'Death' then
		fh.getParam(sPluginName, 'Only Death is supported by this plugin. ')
		return
	end
	-- Get form and load values from Citation
	local masterForms = loadMasterForms()
	form = tablex.deepcopy(masterForms[iForm]['sections'])
	templateName = masterForms[iForm]['templateName']
	templateDefault = masterForms[iForm]['templateDefault']

	form.principal.fields[1].value = pCite:getValue 'NM-PRINCIPAL'

	dtEventDateString = pCite:getDisplayValue 'DT-DATE'
	dtEventDate = pCite:getValue 'DT-DATE'

	-- ------------------------------------------------------------ Get currently selected person
	local ptrPrincipal = fh.getCurrentIndividual()
	if ptrPrincipal:IsNotNull() then
		form.principal.fields[field(form.principal.fields, 'RECORD')].value =
			ptrPrincipal:Clone()
		form.principal.fields[field(form.principal.fields, 'ACTION')].value =
			'select'
		form.principal.fields[field(form.principal.fields, 'RECORD')].protect =
			false
		form.principal.fields[field(form.principal.fields, 'SEX')].value =
			fhGetItemText(
				ptrPrincipal,
				'~.SEX'
			)
	end
	-- ------------------------------------------------------------ Create Dialog
	for k, v in pairs(form) do
		grid = iup.gridbox {
			numdiv = 2,
			cgaplin = '1',
			cgapcol = '1',
			margin = '4x4',
			expand = 'YES',
		}
		for l, f in ipairs(v.fields) do
			if f.label then
				local label = '      '
				local value = '                                       '
				if f.gridDisplay or v.gridDisplay then
					label = f.label .. ':'
					value = fh.getParamValueForDisplay(f, true)
				end
				f.gridLabel = iup.label { title = label, size = '80', expand = 'NO' }
				iup.Append(grid, f.gridLabel)
				f.iupgrid = iup.label { title = value, expand = 'YES' }
				iup.Append(grid, f.iupgrid)
			end
		end
		v.button = iup.button { title = 'Edit', active = 'NO' }
		if v.active then
			v.button.active = 'YES'
		end
		v.button.action = function()
			doEdit(k, v.title, v.seq)
		end
		cells[v.seq] = iup.vbox {
			EXPANDCHILDREN = 'YES',
			EXPAND = 'YES',
			iup.frame {
				title = v.title,
				EXPANDCHILDREN = 'YES',
				EXPAND = 'YES',
				iup.vbox {
					EXPANDCHILDREN = 'YES',
					EXPAND = 'YES',
					v.button,
					grid,
				},
			},
		}
	end

	local placeList = fh.createPlaceList()
	s['PLAC'] = placeList
	s['OCCU'] = fhGetDataList 'OCCUPATIONS'
	local addressList = {}

	txtMessage = iup.label {
		title = ' Hint: please edit the Principal section to enable the other sections',
	}

	btnOk = iup.button { title = 'OK', padding = '20x4', expand = 'HORIZONTAL' }

	function btnOk.action()
		local sType = 'principal'
		local fields = form[sType]['fields']
		if
			form[sType]['fields'][field(fields, 'AGE')].value == ''
			and form[sType]['fields'][field(fields, 'DATE')].value:IsNull()
		then
			fhMessageBox 'Age and Date of Birth can not both be blank'
			return
		end
		if form.principal.valid then
			local sVenue = pCite:getValue 'TX-CHURCH'
			if not sVenue then
				sVenue = pCite:getValue 'AD-ADDRESS'
			end
			processDeath(pCite:getValue 'PL-LOCATION', sVenue)
			return iup.CLOSE
		else
			fhMessageBox 'Information has not been entered on both Principal'
		end
	end
	btnCancel =
		iup.button { title = 'Cancel', padding = '4x4', expand = 'HORIZONTAL' }

	function btnCancel.action()
		return iup.CLOSE
	end

	local sTitle = 'Death Entry: ' .. fhGetDisplayText(pCite.source)
	dlg = iup.dialog {
		title = sTitle,
		minsize = '1000',
		iup.vbox {
			EXPANDCHILDREN = 'YES',
			EXPAND = 'YES',

			iup.gridbox {
				numdiv = 2,
				cgaplin = '1',
				cgapcol = '1',
				margin = '4x4',
				expand = 'YES',
				EXPANDCHILDREN = 'YES',
				table.unpack(cells),
			},
			iup.hbox {
				margin = '4x4',
				expand = 'NO',
				btnOk,
				btnCancel,
				fh.helpButton 'record-Death-data-uk',
				txtMessage,
			},
		},
	}
	iup.SetAttribute(dlg, 'NATIVEPARENT', fhGetContextInfo 'CI_PARENT_HWND') -- Set the parent window handle	 iup.SetHandle("main", dlg)
	iup.SetGlobal('PARENTDIALOG', 'main')

	iup.Popup(dlg)
	iup.Destroy(dlg)
end
-- --------------------------------------------------------------- doEdit
--- Called from the Edit buttons to update the sections
-- @param sType string, section name from form table
-- @param sTitle string Name of dialog
-- @param seq  number, sequence number of section
function doEdit(sType, sTitle, seq)
	local fields = form[sType].fields
	if sType == 'principal' then
		local oldRecord =
			form[sType]['fields'][field(fields, 'RECORD')].value:Clone()
		local r = fh.getParam(
			'Enter ' .. sTitle,
			nil,
			fields,
			{ 'OK', 'Cancel' },
			s
		)
		if r.ok then
			form[sType].valid = true
			-- enable all Edit buttons
			for k, v in pairs(form) do
				v.button.active = 'YES'
			end
		end
	else
		local r = fh.getParam(
			'Enter ' .. sTitle,
			nil,
			fields,
			{ 'OK', 'Cancel' },
			s
		)
		if r.ok then
			form[sType].valid = true
			form[sType].gridDisplay = true
		end
	end
	updateGrid()
end

-- --------------------------------------------------------------- updateGrid
--- Update All Grid Fields from table
function updateGrid()
	for sType, sSection in pairs(form) do
		for l, f in ipairs(sSection.fields) do
			local label = ' '
			local value = ' '
			if f.gridDisplay or form[sType].gridDisplay then
				label = f.label
				value = fh.getParamValueForDisplay(f, true)
			end
			if f.gridLabel then
				f.gridLabel.title = label
			end
			if f.iupgrid then
				f.iupgrid.title = value
			end
		end
	end
end

function processDeath(sPlace, sAddress, sCelebrant)
	local fields = {}
	local data = {}
	for k, s in pairs(form) do
		data[k] = {}
		for i, f in ipairs(s.fields) do
			data[k][f.tag] = { value = f.value, type = f.type, dr = f.df }
		end
	end

	-- Create needed Individual Records
	for _, f in pairs(data) do
		if f.ACTION and f.ACTION.value == 'create' then
			local sex
			if f.SEX and f.SEX.value then
				sex = f.SEX.value
			end
			local ptr = fh.createIndi(f.NAME.value, sex)
			local ptrName = fhGetItemPtr(ptr, '~.NAME')
			addCitation(ptr, 'Created')
			addCitation(ptrName, 'Cited')
			f.RECORD.value:MoveTo(ptr)
		end
	end

	local eventDate = pCite:getValue 'DT-DATE'
	local sPlace = pCite:getValue 'PL-LOCATION'
	local sAddress = pCite:getValue 'AD-ADDRESS'
	-- Create / Update Death Event
	local ptrDeath = fhGetItemPtr(data.principal.RECORD.value, '~.DEAT')
	local sAction = 'Added'
	if ptrDeath:IsNotNull() then
		ptrDeath, sAction = fh.createUpdateFact(
			data.principal.RECORD.value,
			'DEAT',
			'Death',
			sPlace,
			eventDate,
			sAddress
		)
	else
		ptrDeath = fh.createFact(
			data.principal.RECORD.value,
			'DEAT',
			sPlace,
			eventDate,
			sAddress
		)
	end

	if ptrDeath then
		addCitation(ptrDeath, sAction)
		-- Add Age
		local ptrAge = fhCreateItem('AGE', ptrDeath, true)
		if ptrAge and fh.isSet(data.principal.AGE.value) then
			fhSetValueAsText(ptrAge, data.principal.AGE.value)
		end
		-- Add Cause
		local ptrCause = fhCreateItem('CAUS', ptrDeath, true)
		if ptrCause and fh.isSet(data.principal.CAUSE.value) then
			fhSetValueAsText(ptrCause, data.principal.CAUSE.value)
		end
	end
	-- Create Update Birth Event
	local dtBirth = data.principal.DATE.value
	if dtBirth:IsNull() and fh.isSet(data.principal.AGE.value) then
		dtBirth = fh.calcBirth(eventDate, data.principal.AGE.value)
	end
	if not (dtBirth:IsNull()) then
		local ptrBirth = fhGetItemPtr(data.principal.RECORD.value, '~.BIRT')
		local sPlace = ''
		if data.principal.BIRTPLAC.value ~= 'N/A' then
			sPlace = data.principal.BIRTPLAC.value
		end
		if ptrBirth:IsNotNull() then
			ptrBirth, sAction = fh.createUpdateFact(
				data.principal.RECORD.value,
				'BIRT',
				'Birth',
				sPlace,
				dtBirth
			)
		else
			sAction = 'Added'
			ptrBirth = fh.createFact(
				data.principal.RECORD.value,
				'BIRT',
				sPlace,
				dtBirth
			)
		end
		if ptrBirth then
			addCitation(ptrBirth, sAction)
		end
	end
	for k, f in pairs(data) do
		if f.RECORD and fh.isSet(f.RECORD.value) then
			if f.OCCU and f.OCCU.value:len() > 0 then
				local ptrFact
				ptrFact, sAction = fh.createFact(
					f.RECORD.value,
					'OCCU',
					nil,
					eventDate,
					nil,
					f.OCCU.value
				)
				if ptrFact then
					addCitation(ptrFact, 'Added')
				end
			end
			if f.PLAC and f.PLAC.value:len() > 0 then
				local ptrFact, sAction = fh.createFact(
					f.RECORD.value,
					'RESI',
					f.PLAC.value,
					eventDate,
					f.ADDR.value
				)
				if ptrFact then
					addCitation(ptrFact, 'Added')
				end
			end
		end
		if ptrDeath and k == 'informant' then
			if f.ACTION.value == 'name' then
				if fh.isSet(f.NAME.value) then
					fh.addWitness(ptrDeath, f.NAME.value, 'Informant')
				end
			elseif fh.isSet(f.RECORD.value) then
				fh.addWitness(ptrDeath, f.RECORD.value, 'Informant')
			end
		end
	end

	local fields = {}
	for k, s in pairs(form) do
		for i, f in ipairs(s.fields) do
			local inx = string.upper(k .. '.' .. tostring(f.tag))
			fields[inx] = fh.getParamValueForDisplay(f)

			if f.tag == 'NAME' and data[k].RECORD then
				local ptr = data[k].RECORD.value
				if ptr:IsNotNull() then
					-- Add record link
					fields[inx] = fh.richTextRecordLink(ptr, fields[inx])
				end
			end
		end
	end
	if not (pCite:checkRequired 'AD-ADDRESS') then
		fields['AD-ADDRESS'] = ''
	end
	sText = fh.formatTextFromSource(templateName, templateDefault, pCite, fields)
	fh.createTextFromSource(pCite, sText, 'source')

	fh.outputUpdatedFields(tUpdatedFields, pCite)
end

function addCitation(ptr, action)
	local action = action or ''
	table.insert(tUpdatedFields, { ptr:Clone(), action })
	pCite:appendCitation(ptr)
end

function selectOrCreate(value)
	if value == 'select' then
		return false, 1
	else
		return true, 0
	end
end

function updateSex(ptr)
	if ptr:IsNull() then
		return { 'Male', 'Female', 'Unknown' }
	else
		return fhGetItemText(ptr, '~.SEX')
	end
end

function address(place)
	local addr = fh.createAddressList(place)
	return addr
end

function field(fields, tag)
	local i
	for i, v in ipairs(fields) do
		if tag == v.tag then
			return i
		end
	end
end

function loadMasterForms()
	ptrFamily = fhNewItemPtr()
	masterForms = {}
	masterForms[1] = {
		templateName = 'Death Certificate (UK).ftf',
		templateDefault = [[Death certificate {NM-PRINCIPAL} {DT-DATE}

Certified Copy of an entry of Death given at {REG.LOCATION} Registration District {REG.DISTRICT} in sub-district of {REG.SUB} in the county of {REG.PLAC}



 When and Where died | Name and Surname | Sex | Age / Birth Date | Occupation | Cause of Death | Signature, description and residence of informant | When Registered | Signature of Registrar 
 {DT-DATE}

{AD-ADDRESS}
{PL-LOCATION} | {PRINCIPAL.NAME} 
{PRINCIPAL.ADDR}
{PRINCIPAL.PLAC}
| {PRINCIPAL.SEX} | {PRINCIPAL.AGE}
{PRINCIPAL.DATE} |{PRINCIPAL.OCCU} |{PRINCIPAL.CAUSE}
Certified by {PRINCIPAL.CERTIFED_BY} | {INFORMANT.NAME}
{INFORMANT.DESCRIPTION} 
{INFORMANT.ADDR}
{INFORMANT.PLAC} | {REG.DATE} | {REG.NAME} 


Maiden Name: {PRINCIPAL.MAIDEN}
Birth Place: {PRINCIPAL.BIRTPLAC}

]],
		sections = {
			reg = {
				fields = {
					{
						tag = 'LOCATION',
						type = 'STRING',
						label = 'Location',
						value = 'General Register Office',
					},
					{ tag = 'DISTRICT', type = 'STRING', label = 'District', value = '' },
					{ tag = 'SUB', type = 'STRING', label = 'Sub-District', value = '' },
					{ tag = 'PLAC', type = 'STRING', label = 'County of', value = '' },
					{ tag = 'NAME', type = 'STRING', label = 'Registrars Name', value = '' },
					{
						tag = 'DATE',
						type = 'DATE',
						label = 'Registration Date',
						value = fhNewDate(),
					},
				},
				title = 'Registration Information',
				label = 'principal',
				seq = 1,
				active = true,
			},
			principal = {
				fields = {
					{
						tag = 'NAME',
						label = 'Name',
						type = 'STRING',
						dr = 'NAME',
						value = pCite:getValue 'NM-PRINCIPAL',
						minlength = 1,
						protect = true,
					},
					{
						tag = 'ACTION',
						label = 'Add principal as',
						type = 'LIST',
						value = 'create',
						values = { 'create', 'select' },
						prompts = { 'Create New Record', 'Use Existing Record' },
						child = 'RECORD',
						childUpdate = function(...)
							return selectOrCreate(...)
						end,
					},
					{
						tag = 'RECORD',
						label = 'Record',
						type = 'RECORD',
						dr = 'PTR',
						value = fhNewItemPtr(),
						protect = true,
						child = 'SEX',
						childUpdate = updateSex,
					},
					{
						tag = 'SEX',
						type = 'LIST',
						label = 'Sex',
						dr = 'SEX',
						value = 'Male',
						values = { 'Male', 'Female', 'Unknown' },
					},
					{
						tag = 'CAUSE',
						dr = 'OCCU',
						type = 'STRING',
						label = 'Cause of Death',
						value = '',
					},
					{
						tag = 'CERTIFED_BY',
						dr = 'OCCU',
						type = 'STRING',
						label = 'Certified By',
						value = '',
					},

					{ tag = 'OCCU', dr = 'OCCU', type = 'STRING', label = 'Occupation', value = '' },
					{
						tag = 'AGE',
						type = 'STRING',
						label = 'Age',
						value = '',
						mask = '(/d+[dwmy])',
						minlength = 0,
					},
					{ tag = 'DATE', type = 'DATE', label = 'Date of Birth', value = fhNewDate() },

					{
						tag = 'PLAC',
						type = 'STRING',
						label = 'Place of Residence',
						value = '',
						child = 'ADDR',
						childUpdate = address,
					},
					{ tag = 'ADDR', type = 'STRING', label = 'Address of Residence', value = '' },

					{ tag = 'BIRTPLAC', type = 'STRING', label = 'Birth Place', value = 'N/A' },
					{ tag = 'MAIDEN', type = 'STRING', label = 'Maiden Name', value = 'N/A' },
				},
				title = 'Principal',
				label = 'principal',
				seq = 2,
				active = true,
				gridDisplay = true,
			},

			informant = {
				fields = {
					{
						tag = 'NAME',
						label = 'Name',
						type = 'STRING',
						dr = 'NAME',
						value = '',
						minlength = 1,
					},
					{
						tag = 'DESCRIPTION',
						label = 'Description',
						type = 'STRING',
						value = '',
						minlength = 1,
					},
					{
						tag = 'ACTION',
						label = 'Add informant as',
						type = 'LIST',
						value = 'name',
						values = { 'name', 'create', 'select' },
						prompts = { 'Name Only', 'Create New Record', 'Use Existing Record' },
						child = 'RECORD',
						childUpdate = selectOrCreate,
					},
					{
						tag = 'RECORD',
						label = 'Record',
						type = 'RECORD',
						recordtype = 'INDI',
						value = fhNewItemPtr(),
						protect = true,
					},
					{
						tag = 'PLAC',
						type = 'STRING',
						label = 'Residence Place',
						dr = 'RESI.PLAC',
						value = '',
					},
					{
						tag = 'ADDR',
						type = 'STRING',
						label = 'Residence Address',
						dr = 'RESI.ADDR',
						value = '',
					},
				},
				title = 'Informant',
				seq = 3,
				active = true,
			},
		},
	}

	return masterForms
end
-------------------------------------------------------------------------------------------------------- Start Plugin
main()

Source:Record-Death-Data-UK-4.fh_lua