* Lua Inspiration needed

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
laz_gen
Famous
Posts: 177
Joined: 03 Apr 2018 14:02
Family Historian: V7
Contact:

Lua Inspiration needed

Post by laz_gen »

My FH project includes a large number of media imported from Legacy. The media records appear to mostly have their Title field empty, except for a few where I have manually entered a title. I have always been methodical in my media file naming so the Path field of each media record holds something that I would like to convert into a Title.

As an example I have a media item for a church that has the following filepath

"D:\Genealogy\Family Historian Projects\Multimedia\Cemeteries\St_Nicholas_Church_Radstock.jpg"

and I would like to discard the filepath and keep the filename, then perhaps replace each underscore character in the filename with a space, perhaps adding a comma between the church and the town, then finally discard the .jpg suffix.

Resulting in a string of "St Nicholas Church, Radstock"

This I could then copy into the Media Title field.

I have taken an initial look at Lua string handling capabilities especially the string.find function and it can find for example the ".jpg" without a problem but it doesn't seem to see or count the backslash characters.

I was thinking I should parse backwards from the end of the string to the last "\" then discard all characters to the start of the string but the function is not seeing these "\" characters at all.

Any ideas on another approach I may take?
User avatar
Jane
Site Admin
Posts: 8507
Joined: 01 Nov 2002 15:00
Family Historian: V7
Location: Somerset, England
Contact:

Re: Lua Inspiration needed

Post by Jane »

Code: Select all

    function SplitFilename(strFilename)
    	-- Returns the Path, Filename, and Extension as 3 values
    	return strFilename:match("^(.-)([^\\/]-)%.([^\\/%.]-)%.?$")
    end

local name = "D:\\Genealogy\\Family Historian Projects\\Multimedia\\Cemeteries\\St_Nicholas_Church_Radstock.jpg"
local p,n,e = SplitFilename(name)
n = n:gsub('_',' ')
print(n)

Assuming you already have the "loop" and test logic to find the missing titles the above will split a string, using the function from the code snippets and remove the under scores.

N.B Remember if you are defining a string directly in Lua you need to double up backslashes.
Jane
My Family History : My Photography "Knowledge is knowing that a tomato is a fruit. Wisdom is not putting it in a fruit salad."
User avatar
laz_gen
Famous
Posts: 177
Joined: 03 Apr 2018 14:02
Family Historian: V7
Contact:

Re: Lua Inspiration needed

Post by laz_gen »

I was experimenting with that function from the knowledgebase but could only get it to partly work. It was failing to split the path and filename properly. Adding the double backslashes does allow it to work properly. I didn't know that thanks.

The source of the path/filenames will be directly from the Path field in each media record so I presume it will cope with single backslashes in that instance OK.

Is there a quick easy way to add a comma after the church name?

I haven't got around to working out the loop and test logic yet, I was concentrating on the string parsing/slicing.
User avatar
tatewise
Megastar
Posts: 28335
Joined: 25 May 2010 11:00
Family Historian: V7
Location: Torbay, Devon, UK
Contact:

Re: Lua Inspiration needed

Post by tatewise »

To replace underscore with space and add a comma after Church you need to focus on the string.gsub function.

I presume you have discovered the plugins:index#developer_guide|> FH Plugins > Developer Guide.

When using string.find and string.gsub you need to understand patterns.
See plugins:understanding_lua_patterns|> Understanding Lua Patterns.

BTW: If using Jane's SplitFilename function you should simply be able to pass the File parameter from the Media record without needing to adjust any backslashes.
e.g.
ptrFile:MoveTo(ptrObje,"~._FILE")
strFile = fhGetValueAsText(ptrFile)
strPath, strName, strType = SplitFilename(strFile)

Then for your example path strName will hold St_Nicholas_Church_Radstock and strType will hold jpg
Mike Tate ~ researching the Tate and Scott family history ~ tatewise ancestry
User avatar
laz_gen
Famous
Posts: 177
Joined: 03 Apr 2018 14:02
Family Historian: V7
Contact:

Re: Lua Inspiration needed

Post by laz_gen »

Thanks Mike, yes I have been reading about string handling on the knowledgebase and in the Lua manual online. I was working my way (very slowly) through string patterns and captures in the Lua manual then found the Split filename functions on the knowledgebase.

I did have an idea for adding the comma by using a For loop and working backwards from the end examining each character until the space is found. Splitting the string into two at that point then concatenating again with a comma in the middle. My old fashioned approach but adding a bit more complexity than using the existing functions.

With further thought and a look at my media records I have I don't think it will be easy inserting a comma into all records as I have numerous filenames with more than one space such as

St_John_the Baptist_Midsomer_Norton
or
St_Vigor_Stratton_on_the_Fosse

and I can't see a way of identifying the end of the church name and the beginning of the place name.

It was just an afterthought to have a comma there anyway. I am more than happy to get the filename extracted from the Path field and copied into the Title field with just spaces.

Thanks again to Jane & Mike
User avatar
tatewise
Megastar
Posts: 28335
Joined: 25 May 2010 11:00
Family Historian: V7
Location: Torbay, Devon, UK
Contact:

Re: Lua Inspiration needed

Post by tatewise »

You are probably correct, but I was hoping you might have a significant number using the word Church.
Then this statement would do the job:
strName = strName:gsub( "_Church_", " Church, " )
followed by Jane's suggestion to replace each _ with space:
strName = strName:gsub( "_", " " )

Or you can be really clever and merge them together:
strName = strName:gsub( "_Church_", " Church, " ):gsub( "_", " " )

You would then have to manually add commas anywhere else you wanted them.

Those kinds of string manipulation are much more efficient than traditional script looping and searching techniques.
Mike Tate ~ researching the Tate and Scott family history ~ tatewise ancestry
User avatar
laz_gen
Famous
Posts: 177
Joined: 03 Apr 2018 14:02
Family Historian: V7
Contact:

Re: Lua Inspiration needed

Post by laz_gen »

Code: Select all

-- Extract Media Title from Media Filename

function SplitFilename(strFilename)
    -- Returns the Path, Filename, and Extension as 3 values
    return strFilename:match("^(.-)([^\\/]-)%.([^\\/%.]-)%.?$")
end

for ptr in records('OBJE') do

	titleptr = fhGetItemPtr(ptr,'TITL')

	if titleptr:IsNull() then
		titleptr = fhCreateItem('TITLE', ptr)
		ptrFile:MoveTo(ptrObje,"~._FILE")
		strFile = fhGetValueAsText(ptrFile)
		strPath, strName, strType = SplitFilename(strFile)
		strName = strName:gsub('_',' ')
		ok = fhSetValueAsText(titleptr, strName)
	end

end
This is my first attempt at putting it all together but I am getting an error -
"attempt to call global 'records' (a nil value)" at line 8
"for ptr in records('OBJE') do"

All help gratefully received.
User avatar
tatewise
Megastar
Posts: 28335
Joined: 25 May 2010 11:00
Family Historian: V7
Location: Torbay, Devon, UK
Contact:

Re: Lua Inspiration needed

Post by tatewise »

One rule to remember is that every word you use in a script should be predefined (although there are a few exceptions).
So considering the statement for ptr in records('OBJE') do
the keywords for and in and do are predefined by Lua,
but ptr and records are undefined.

Take a look at plugins:code_snippets:loop_through_records|> Loop Through Records (code snippet).

Later on you will have similar problems with ptrFile and ptrObje.

I am only giving clues rather than a worked solution, because I think you learn better that way.
Mike Tate ~ researching the Tate and Scott family history ~ tatewise ancestry
User avatar
laz_gen
Famous
Posts: 177
Joined: 03 Apr 2018 14:02
Family Historian: V7
Contact:

Re: Lua Inspiration needed

Post by laz_gen »

Code: Select all

-- Extract Media Title from Media Filename

local ptr = fhNewItemPtr()
local records = fhNewItemPtr()

local ptrFile = fhNewItemPtr()
local ptrObje = fhNewItemPtr()

function SplitFilename(strFilename)
    -- Returns the Path, Filename, and Extension as 3 values
    return strFilename:match("^(.-)([^\\/]-)%.([^\\/%.]-)%.?$")
end

for ptr in records('OBJE') do

	titleptr = fhGetItemPtr(ptr,'TITL')

	if titleptr:IsNull() then
		titleptr = fhCreateItem('TITLE', ptr)
		ptrFile:MoveTo(ptrObje,"~._FILE")
		strFile = fhGetValueAsText(ptrFile)
		strPath, strName, strType = SplitFilename(strFile)
		strName = strName:gsub('_',' ')
		ok = fhSetValueAsText(titleptr,strName)
	end

end
I presume I was trying to use these pointers without declaring them but have now included a "fhNewItemPtr()" for each but am still seeing an error.

I can see them being declared in the debugger as items with a value of null but the error persists at the same line.
Unsure if records should be a pointer or a table? Do I also need to initiate them with a starting value?
User avatar
tatewise
Megastar
Posts: 28335
Joined: 25 May 2010 11:00
Family Historian: V7
Location: Torbay, Devon, UK
Contact:

Re: Lua Inspiration needed

Post by tatewise »

You are heading in the right direction but records is not a pointer, and a for loop is the wrong mechanism to loop through records, so see plugins:code_snippets:loop_through_records|> Loop Through Records (code snippet) as advised and also check the FH Tools > Plugins > How to Write Plugins > Sample Plugin Scripts > Surname Summary record loop.
Mike Tate ~ researching the Tate and Scott family history ~ tatewise ancestry
User avatar
laz_gen
Famous
Posts: 177
Joined: 03 Apr 2018 14:02
Family Historian: V7
Contact:

Re: Lua Inspiration needed

Post by laz_gen »

I must admit to becoming rather confused about which looping constructs are suitable for which records. With your hints and clues you have directed me to that page of code snippets where I find the first and second example uses a "While" loop and the last uses a "For" loop. That last example appears to be passing the record type as a parameter to a function. It is shown with an "INDI" type but doesn't explain which record types it can and cannot handle, with the inference that it can handle any record type.

Further, my last attempt at writing a plugin to move marriage witness details, for which I sought help on this forum, used a "For" loop and was successful in moving the details between facts in the Marriage records.

To save bothering you with my misunderstandings can you direct me to a web page on FH, FHUG or Lua that explains which looping constructs are suitable for each record type.
User avatar
Jane
Site Admin
Posts: 8507
Joined: 01 Nov 2002 15:00
Family Historian: V7
Location: Somerset, England
Contact:

Re: Lua Inspiration needed

Post by Jane »

The one Mike gave you show you how to look through records.

In your first attempt you tried to use records with out including the function which does the loop.

Most code snippets have an example AND a function you need to copy into your code.
Jane
My Family History : My Photography "Knowledge is knowing that a tomato is a fruit. Wisdom is not putting it in a fruit salad."
User avatar
tatewise
Megastar
Posts: 28335
Joined: 25 May 2010 11:00
Family Historian: V7
Location: Torbay, Devon, UK
Contact:

Re: Lua Inspiration needed

Post by tatewise »

To use the for loop method you must include the function records(type) from the code snippet and put that near the top of your Plugin script.

It will work for any type of record, as will the while loop methods.
Just substitute the desired record type tag in place of INDI.
Mike Tate ~ researching the Tate and Scott family history ~ tatewise ancestry
User avatar
laz_gen
Famous
Posts: 177
Joined: 03 Apr 2018 14:02
Family Historian: V7
Contact:

Re: Lua Inspiration needed

Post by laz_gen »

I am used to constructing loops with For/Next or While/Wend or Repeat/Until or Do/While etc. to navigate strings, arrays, tables directly and I have been trying to do the same here.

I can see now the FH designer intended us to only make use of the API functions (and Lua functions) to navigate around and address the database tables and fields.

I am starting to see the light at the end on the tunnel.

Thanks to you both.
User avatar
laz_gen
Famous
Posts: 177
Joined: 03 Apr 2018 14:02
Family Historian: V7
Contact:

Re: Lua Inspiration needed

Post by laz_gen »

My final bit of code to extract media titles from their filenames.
Tidied things up a little and included a results table to see if all is correct.

Credit to Jane for her SplitFileName function and for Mike in pointing me towards the right looping construct to use.

Code: Select all

-- Extract Media Titles from Media Filenames

local ptrObje = fhNewItemPtr()
local records = fhNewItemPtr()
local ptrFile = fhNewItemPtr()
local tblFile = {}
local tblPath = {}
local tblTitle = {}


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 SplitFilename(strFilename)
    -- Returns the Path, Filename, and Extension as 3 values
    return strFilename:match("^(.-)([^\\/]-)%.([^\\/%.]-)%.?$")
end

for ptrObje in records('OBJE') do

	ptrTitle = fhGetItemPtr(ptrObje,'TITL')

	if ptrTitle:IsNull() then
		ptrTitle = fhCreateItem('TITL', ptrObje)
		ptrFile:MoveTo(ptrObje,"~._FILE")
		strFile = fhGetValueAsText(ptrFile)
		strPath, strName, strType = SplitFilename(strFile)
		strName = strName:gsub('_',' ')
		result = fhSetValueAsText(ptrTitle,strName)
		table.insert(tblFile, strFile)
		table.insert(tblPath, strPath)
		table.insert(tblTitle, strName)
	end

end

fhOutputResultSetTitles("Media Titles")

fhOutputResultSetColumn("New Media Title", "text", tblTitle, #tblTitle, 220, "align_left")
fhOutputResultSetColumn("Media Path", "text", tblPath, #tblPath, 240, "align_left")
fhOutputResultSetColumn("Full Path & Filename", "text", tblFile, #tblFile, 400, "align_left")
User avatar
Jane
Site Admin
Posts: 8507
Joined: 01 Nov 2002 15:00
Family Historian: V7
Location: Somerset, England
Contact:

Re: Lua Inspiration needed

Post by Jane »

Well done, I always find when learning something new, the "hours" per line of code can be high.
Jane
My Family History : My Photography "Knowledge is knowing that a tomato is a fruit. Wisdom is not putting it in a fruit salad."
User avatar
tatewise
Megastar
Posts: 28335
Joined: 25 May 2010 11:00
Family Historian: V7
Location: Torbay, Devon, UK
Contact:

Re: Lua Inspiration needed

Post by tatewise »

Excellent. I hope you agree that us giving clues for you to discover the solution yourself was more instructive than just giving you a working script.
Mike Tate ~ researching the Tate and Scott family history ~ tatewise ancestry
Post Reply