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