Application Architecture - Details on the implementation, including annotated source code.
www.amzi.com - Additional demos, articles, freeware, evaluation copies and information about Amzi! Prolog + Logic Server.
We welcome any and all comments about this demo. Contact us at www.amzi.com
To run it you need:
If you have the tools to modify the database, which was created using Microsoft Access, you can fill it with individuals from your own family tree. Or you can replace the gene database with an ODBC database of your own choosing.
The logic base contains rules that define relationships based on the database. These are rules that express what it means to be a sibling, uncle, ancestor, etc. At the base of each of these rules are Prolog predicates that map directly to the database, using the Logic Server ODBC extension.
The user interface contains list boxes that present the pertinent information from the logic base. That information is obtained from a Visual Basic program that uses logic base services.
There are two tables in the database defined as follows.
Person
*pid number (key) *surname text[40] (indexed) *midname....text[40] *name text[40] *gender text[1] m/f *mother number (indexed) (a pid) *father number (indexed) (a pid) dob text[10] (date yyyy-mm-dd) dod text[10] (date yyyy-mm-dd) birthplace text[50] nation text[40] note text[255]Marriage
*husband number (indexed) (a pid) *wife number (indexed) (a pid) married text[10] (date yyyy-mm-dd) divorced text[10] (date yyyy-mm-dd) note text[255]* - Fields that are used in this demonstration. Other fields are ignored.
Individuals with no middle name are at the top of the tree, also indicated by parents with PID numbers of 0.
The code consists of these primary sections:
The code included is:
''''''''''''''''''''''''''''''''''''''''''''''''
' Display All the Relations for the Highlighted
' Person and Relationship
'
Private Sub DisplayRelations()
Dim Person As String, Relationship As String
Dim StrVal As String
Dim rc As Integer, tf As Integer
Dim Term As Long
Dim PID As Integer
Dim PersonID, Firstname, Midname, Surname As String
' First clear the list of related persons
RelatedPersonsList.Clear
' If we don't have both a person and a relationship highlighted
' then exit this routine
If PersonList.ListIndex < 0 Or RelationshipList.ListIndex < 0 Then
Exit Sub
End If
' Get the highlighted person and relationship
Person = PersonList.List(PersonList.ListIndex)
Relationship = RelationshipList.List(RelationshipList.ListIndex)
' Build the Prolog command "query(<relationship>(X,<person_id>), PID, Surname, Midname, Name)"
PersonID = Mid$(Person, 1, InStr(Person, ":") - 1)
tf = CallStrLS(Term, "query(" + Relationship + "(X, " + PersonID + "), PID, Surname, Midname, Firstname)")
If tf = False Then
RelatedPersonsList.AddItem "No " + Relationship + " for " + Right$(Person, Len(Person) - (InStr(Person, ":") + 1))
End If
' Loop getting all the people who have that relationship
' and add them to the related persons list
While (tf)
PID = GetIntArgLS(Term, 2)
Surname = GetStrArgLS(Term, 3)
Midname = GetStrArgLS(Term, 4)
Firstname = GetStrArgLS(Term, 5)
StrVal = Format$(PID, "###") + ": " + Firstname + " " + Midname + " " + Surname
RelatedPersonsList.AddItem StrVal
tf = RedoLS()
Wend
End Sub
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Close Down Amzi Prolog When the Exit Button is Pressed
'
Private Sub Exit_Click()
Dim tf As Integer
Dim Term As Long
' Close the ODBC database
tf = ExecStrLS(Term, "db_close")
If tf <> True Then
MsgBox "Unable to close ODBC database 'gene'"
End If
' Close the Prolog runtime, releasing all resources
CloseLS
End
End Sub
''''''''''''''''''''''''''''''''''''''''''''
' Main Form Handler Starts up Amzi! Prolog
'
Private Sub Form_Load()
Dim rc As Integer, tf As Integer
Dim Term As Long
Dim xplname As String
' Setup our xpl and help file pathnames
xplname = App.Path + "\DBGENE.XPL"
App.HelpFile = App.Path + "\DBGENEVB.HLP"
' Initialize the runtime and load DBGENE.XPL, which contains
' all the rules and expertise for this application
' Also load the ODBC LSX
InitLS (xplname)
AddLSX ("ls4odbc")
LoadLS (xplname)
' Turn off backslash processing in strings and atoms (so pathnames work)
tf = CallStrLS(Term, "set_mode(string_esc, off)")
If tf <> True Then
MsgBox "Unable to turn of string escape processing"
End If
' Open the ODBC database, gene
tf = ExecStrLS(Term, "db_open('gene')")
If tf <> True Then
MsgBox "Unable to open the ODBC database 'gene'; check your ODBC setup in the control panel"
End If
' Display the Persons and Relations lists
tf = Persons()
tf = Relations()
End Sub
'''''''''''''''''''''''''''''''''''''''''''''''''''''
' Close Down Amzi Prolog When the Main Form Closes
'
Private Sub Form_Unload(CANCEL As Integer)
Dim tf As Integer
Dim Term As Long
' Close the ODBC database
tf = ExecStrLS(Term, "db_close")
If tf <> True Then
MsgBox "Unable to close ODBC database 'gene'"
End If
' Close the Prolog runtime, releasing all resources
Call CloseLS
End Sub
'''''''''''''''''''''''''''''''''''''''''''''''''''''
' Display Help When Button is Pressed
'
Private Sub Help_Click()
Dim retval As Integer
retval = ShellExecute(0, ByVal "open", ByVal "dbgenevb.htm", "", "", 1)
End Sub
'''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Display All the Family Members in the First List Box
'
Private Function Persons() As Integer
Dim rc As Integer, tf As Integer
Dim Term As Long
Dim PID As Integer
Dim StrVal, Firstname, Midname, Surname As String
' Clear the list first
PersonList.Clear
' Issue the Prolog query: person(X)
tf = CallStrLS(Term, "fullname(Pid, Surname, Midname, Firstname)")
' Check if there are any people
If (tf <> True) Then
Persons = 0
Exit Function
End If
' Loop through all the family members, adding them to the list
While (tf = True)
PID = GetIntArgLS(Term, 1)
Surname = GetStrArgLS(Term, 2)
Midname = GetStrArgLS(Term, 3)
Firstname = GetStrArgLS(Term, 4)
StrVal = Format$(PID, "###") + ": " + Firstname + " " + Midname + " " + Surname
PersonList.AddItem StrVal
tf = RedoLS()
Wend
Persons = 1
End Function
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Display Relations When the Query Button is Pressed
'
Private Sub Query_Click()
Query.Enabled = False
Call DisplayRelations
Query.Enabled = True
End Sub
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Display All the Possible Relationships in the Second List Box
'
Private Function Relations() As Integer
Dim rc As Integer, tf As Integer
Dim Term As Long, TList As Long
Dim StrVal As String
' Clear the list first
RelationshipList.Clear
' Issue the Prolog query: relations(X)
tf = CallStrLS(Term, "relations(X)")
' Check if there are any relationships
If (tf <> True) Then
Relations = 0
Return
End If
' Loop through all the relationships adding them to the list
Call GetArgLS(Term, 1, bTERM, TList)
Do
rc = PopListLS(TList, bSTR, StrVal)
If (rc = 0) Then
RelationshipList.AddItem StrVal
End If
Loop While (rc = 0)
Relations = 1
End Function