events_table.py
Nov 25, 2022 3:09:49 GMT -8
Post by Uncle Buddy on Nov 25, 2022 3:09:49 GMT -8
<drive>:\treebard\app\python\events_table.py Last Changed 2024-04-16
# events_table.py
import tkinter as tk
import sqlite3
from files import appwide_db_path, current_drive, get_current_tree
from redraw import redraw_person_tab, redraw_gui
from utilities import split_sorter
from widgets import (
NEUTRAL_COLOR, LabelDots, LabelButtonText, LabelEntryDynamic, configall,
make_formats_dict, open_message, EntryAutoSource, Separator,
CellAutoEventType, Cell, TabBook, EntryAutoEventType, CellAutoPlace)
from dates import (
validate_date, format_stored_date, OK_MONTHS, get_date_formats,
make_date_sorter)
from roles import RolesDialog
from notes import NotesDialog
from persons import get_name_from_id
from places import ValidatePlace, EntryAutoPlace
from assertions import AssertionsDialog
from messages import events_msg
from dates import ChangeDate
from query_strings import (
select_event_nested_place, select_all_event_types_couple,
select_all_events_current_person, select_events_details_generic,
select_event_couple_details, select_family_events_details_couple_generic,
select_all_events_notes_ids, update_event_particulars,
select_all_events_roles_ids_distinct, select_all_event_types,
select_count_event_id_sources, select_event_event_type,
select_event_type_id, insert_event_new, select_couple_count_person1,
select_events_for_person, select_couple_by_event_id,
update_event_age1, select_event_couple_id, update_assertion_delete_event,
select_event_type_couple_bool, update_event_types, update_event_age,
delete_event_by_id, delete_events_roles_event, delete_events_notes_event,
select_event_type_after_death, update_event_age2, select_person,
select_event_persons, update_event_date, select_event_person_id,
select_event_id_offspring_by_parent, update_person_gender,
select_event_details_offspring_alt_parentage, select_event_type_id_by_event_id,
select_person_gender_by_id, select_event_age1, select_event_age2,
select_couple_count_person2, select_all_birth_event_people,
select_event_types_for_kintips, select_event_types_couple,
)
import dev_tools as dt
from dev_tools import look, seeline
def get_event_types_for_kintips(conn, cur):
cur.execute(select_event_types_for_kintips)
return cur.fetchall()
HEADS = (
'event', 'date', 'place', 'particulars', 'age',
'roles', 'notes', 'sources')
class EventsTable(tk.Frame):
def __init__(
self, master, formats, family_tree, treebard, main, family_tree_id,
current_person_id, place_data, *args, **kwargs):
tk.Frame.__init__(self, master, *args, **kwargs)
self.master = master
self.formats = formats
self.family_tree = family_tree
self.treebard = treebard
self.main = main
self.family_tree_id = family_tree_id
self.current_person_id = current_person_id
self.place_data = place_data
self.main_canvas = main.master
self.inwidg = None
self.headers = []
self.date_prefs = get_date_formats()
self.initial = None
self.event_type_id = None # added 20240331
self.screen_height = self.winfo_screenheight()
self.column_padding = 2
self.new_row = 0
EntryAutoEventType.create_lists()
CellAutoEventType.create_lists()
self.family_tree.person_autofill_values = self.family_tree.update_person_autofill_values()
conn = sqlite3.connect(appwide_db_path)
conn.execute("PRAGMA foreign_keys = 1")
cur = conn.cursor()
self.current_file = get_current_tree(self.family_tree_id, cur)
self.current_tree = self.current_file[0]
cur.execute("ATTACH ? as tree", (self.current_tree,))
# this has to be renewed if a new couple event type is made
self.event_types_for_kintips = get_event_types_for_kintips(conn, cur)
self.couple_event_types = self.get_couple_event_types(cur)
self.after_death_events = self.get_after_death_event_types(cur)
if self.after_death_events is None:
return
self.events_only_even_without_dates = [
"birth", "death"] + self.after_death_events
self.family_tree.bind(
"<Control-F5>", lambda evt: redraw_gui(
main=self.main, formats=self.formats, family_tree=self.family_tree,
family_tree_id=self.family_tree_id, current_file=self.current_file))
self.is_couple_event = False
self.unknown_event_type = False
self.populate_events_table(cur)
cur.execute("DETACH tree")
cur.close()
conn.close()
def populate_events_table(self, cur):
self.make_header()
table_rows = self.make_table_cells(cur)
self.set_cell_content(table_rows)
self.show_table_cells()
def make_table_cells(self, cur):
self.events_data, current_roles, current_notes = self.get_events(cur)
table_rows = []
for event_id in self.events_data:
row = []
for column in range(8):
if column < 5:
if column in (1, 3, 4):
cell = Cell(self)
elif column == 0:
cell = CellAutoEventType(
self, values=CellAutoEventType.autofill_values)
elif column == 2:
self.edit_input_nested_place = cell = CellAutoPlace(
self, self.family_tree,
values=self.family_tree.place_autofill_values)
cell.event = None
cell.bind("<FocusIn>", self.get_initial, add="+")
cell.bind(
"<FocusOut>",
lambda evt, widg=cell, col=column:
self.ok_edit_cell(evt, widg, col),
add="+")
elif column == 5:
cell = LabelDots(
self, self.family_tree, self.family_tree_id, self.treebard,
RolesDialog)
elif column == 6:
cell = LabelDots(
self,
self.family_tree, self.family_tree_id,
self.treebard, NotesDialog, linked_element="event")
elif column == 7:
cell = LabelButtonText(
self, width=8, anchor='w')
row.append(cell)
table_rows.append(row)
self.new_event_frame = tk.Frame(self)
self.new_event_input = EntryAutoEventType(
self.new_event_frame,
width=32,
autofill=True,
values=EntryAutoEventType.autofill_values)
self.add_event_button = tk.Button(
self.new_event_frame,
text="NEW EVENT OR ATTRIBUTE",
command=self.make_new_conclusion)
return table_rows
def set_cell_content(self, table_rows):
self.attributes = {}
event_ids = list(self.events_data.keys())
self.cell_pool = []
for idx, row in enumerate(table_rows):
self.cell_pool.append([event_ids[idx], row])
for row in self.cell_pool:
widg = row[1][0]
event_id = row[0]
widg.event_id = event_id
for row in self.cell_pool:
widg = row[1][5]
event_id = row[0]
widg.event_id = event_id
widg.current_person_id = self.current_person_id
# Why the commas? These are not tuples. Removing the commas breaks
# the code, but I forgot why there are commas at the ends of
# these 3 lines.
event_type = self.events_data[event_id]["event"],
date = self.events_data[event_id]["date"],
place = self.events_data[event_id]["place"],
particulars = self.events_data[event_id]["particulars"]
header = [i[0] for i in (event_type, date, place) if len(i[0]) != 0]
if len(particulars) != 0:
header.append(particulars)
header = "\n".join(header)
widg.header = f"Roles for event #{event_id}: {header}"
for row in self.cell_pool:
widg = row[1][6]
event_id = row[0]
widg.event_id = event_id
widg.current_person_id = self.current_person_id
event_type = (self.events_data[event_id]["event"],)
date = (self.events_data[event_id]["date"],)
place = (self.events_data[event_id]["place"],)
particulars = self.events_data[event_id]["particulars"]
widg.header = [i[0] for i in (event_type, date, place) if len(i[0]) != 0]
if len(particulars) != 0:
widg.header.append(particulars)
widg.header = "\n".join(widg.header)
header_text = "Notes for event #{}: {}".format(event_id, widg.header)
widg.header = header_text
def show_table_cells(self):
row_order = self.sort_by_date()
copy = []
for event_id in row_order:
for row in self.cell_pool:
if row[0] == event_id:
copy.append(row)
self.cell_pool = copy
rownum = 2
for row in self.cell_pool:
event_id = row[0]
column = 0
for cell in row[1]:
widg = row[1][column]
if column < 5:
text = self.events_data[event_id][HEADS[column]]
elif column == 7:
text = self.events_data[event_id]["source_count"]
widg.bind(
"<Button-1>",
lambda evt, event_id=event_id, widg=widg:
self.open_assertions_dialog(event_id, widg))
else:
text = " "
if column == 5 and self.events_data[event_id].get("roles"):
text = " ... "
elif column == 6 and self.events_data[event_id].get("notes"):
text = " ... "
if column in (5, 6):
widg.config(text=text)
widg.grid(
column=column, row=rownum, sticky='w', pady=(3,0), padx=(2,0))
elif column == 7:
widg.config(text=text)
widg.grid(
column=column, row=rownum, sticky='w', pady=(3,0), padx=(2,0))
else:
widg.grid(
column=column, row=rownum, sticky='ew', padx=(0,18), pady=(3,0))
if column < 5:
widg.insert("1.0", text)
self.make_kintip(widg, event_id, column, text)
widg.set_height()
column += 1
rownum += 1
self.fix_tab_traversal()
for row_num in range(self.grid_size()[1]):
self.rowconfigure(row_num, weight=0)
self.new_row = row_num + 1
self.new_event_frame.grid(
column=0, row=self.new_row, pady=6, columnspan=5, sticky='ew')
self.new_event_input.grid(column=0, row=0, padx=(0,12), sticky='w')
self.add_event_button.grid(column=1, row=0, sticky='w')
def make_kintip(self, widg, event_id, column, text):
event_to_kin = {
("birth", "fosterage", "guardianship", "adoption"): "Parents",
tuple(self.couple_event_types): "Partner",
("offspring",): "Child"}
if column == 0:
event_type = widg.get("1.0", "end-1c")
if (event_type in self.couple_event_types or
event_type in (
"birth", "adoption", "fosterage",
"guardianship", "offspring")):
widg["cursor"] = "hand2"
widg.bind("<Enter>", self.show_kintip, add="+")
widg.bind("<Leave>", self.unshow_kintip, add="+")
widg.tip["event_id"] = event_id
for tup, kin_text in event_to_kin.items():
if text in tup:
widg.tip["kin_type"] = kin_text
break
if kin_text == "Partner" and self.events_data[
event_id].get("partner_id"):
widg.tip["id"] = self.events_data[event_id][
"partner_id"]
widg.tip["name"] = self.events_data[event_id][
"partner_name"]
elif kin_text == "Child" and self.events_data[
event_id].get("child_id"):
widg.tip["id"] = self.events_data[event_id][
"child_id"]
widg.tip["name"] = self.events_data[event_id][
"child_name"]
elif kin_text == "Parents":
left_name = right_name = "unknown"
ids = self.events_data[event_id]["id"]
left_id, right_id = ids.split()
if left_id == "None":
left_id = None
if right_id == "None":
right_id = None
if left_id:
left_name = get_name_from_id(
int(left_id), self.family_tree)[0]["name"]
if right_id:
right_name = get_name_from_id(
int(right_id), self.family_tree)[0]["name"]
widg.tip["id"] = f"{left_id} {left_name}"
widg.tip["name"] = f"& #{right_id} {right_name}"
else:
pass
def show_kintip(self, evt):
cell = evt.widget
text = f"{cell.tip['kin_type']}: #{cell.tip['id']} {cell.tip['name']}"
self.kintip = tk.Toplevel(self)
x, y, cx, cy = cell.bbox("insert")
x = x + cell.winfo_rootx() + 27
y = y + cy + cell.winfo_rooty() + 27
self.kintip.wm_overrideredirect(1)
self.kintip.wm_geometry("+%d+%d" % (x, y))
lab = tk.Label(
self.kintip, text=text, bg=NEUTRAL_COLOR, fg="white",
justify='left', relief='solid', bd=1,)
lab.pack(ipadx=6, ipady=6)
def unshow_kintip(self, evt):
self.kintip.destroy()
def get_all_event_types(self):
conn = sqlite3.connect(self.current_tree)
cur = conn.cursor()
cur.execute(select_all_event_types)
event_types = [i[0] for i in cur.fetchall()]
cur.close()
conn.close()
return event_types
def get_initial(self, evt):
""" On focus into a table cell, store:
--a reference to the cell,
--the original cell contents.
"""
widg = evt.widget
self.initial = widg.get("1.0", "end-1c")
self.inwidg = widg
def get_final(self, widg, col):
""" All without the use of an OK button:
--Compare the initial and final contents of a table cell.
--Validate the new contents for dates/places/event types.
--Open any dialogs needed:
--new & duplicate place dialog
--date clarification dialog for ambiguous dates
--couple/marital/after-death events dialog for new event types
--Update the database if the new content is valid.
--Return the original content if new content is not valid.
"""
if self.initial is None:
return
final = widg.get("1.0", "end-1c")
if final != self.initial:
self.final = final
for row in self.cell_pool:
for column in row[1]:
if column == widg:
self.event = row[0]
self.update_db(widg, col)
def delete_event(self, event_id, inwidg, initial):
def err_done1():
msg1[0].grab_release()
msg1[0].destroy()
inwidg.focus_set()
inwidg.config(text=initial)
def ok_delete_event(conn, cur):
cur.execute(delete_events_roles_event, (event_id,))
cur.execute(delete_events_notes_event, (event_id,))
cur.execute(update_assertion_delete_event, (event_id,))
conn.commit()
cur.execute(delete_event_by_id, (event_id,))
conn.commit()
ChangeDate(self.current_person_id, tag="person", conn=conn, cur=cur)
redraw_person_tab(
main=self.main, current_person_id=self.current_person_id,
family_tree=self.family_tree, current_file=self.current_file,
cur=cur, conn=conn, family_tree_id=self.family_tree_id)
print("line", look(seeline()).lineno, "len(self.cell_pool)", len(self.cell_pool))
TabBook.resize_scrolled_dialog_with_tabbook(
self.family_tree, self.main.canvas, self.main)
conn = sqlite3.connect(appwide_db_path)
cur = conn.cursor()
cur.execute("ATTACH ? AS tree", (self.current_tree,))
cur.execute(select_event_event_type, (event_id,))
event_type_id = cur.fetchone()[0]
if event_type_id == 1:
msg1 = open_message(
self,
events_msg[9],
"Birth Events Undeletable Error",
"OK")
msg1[0].grab_set()
msg1[2].config(command=err_done1)
return
ok_delete_event(conn, cur)
cur.execute("DETACH tree")
cur.close()
conn.close()
def update_db(self, widg, col_num):
def update_event_type():
def err_done3():
msg[0].destroy()
self.focus_set()
def err_done4():
msg[0].destroy()
self.focus_set()
widg.delete("1.0", 'end')
widg.insert("1.0", 'offspring')
def err_done7():
msg[0].destroy()
self.focus_set()
widg.delete("1.0", 'end')
widg.insert("1.0", initval)
def update_to_existing_type():
def err_done5():
msg4[0].destroy()
self.focus_set()
initial_value = self.initial
cur.execute(select_event_type_id, (initial_value,))
result = cur.fetchone()
if result:
old_event_type_id, couple_event_old = result
event_type_id = None
couple_event_new = None
cur.execute(select_event_type_id, (self.final,))
result = cur.fetchone()
if result:
event_type_id, couple_event_new = result
if couple_event_old != couple_event_new:
event_type_ok = False
msg4 = open_message(
self,
events_msg[3],
"Incompatible Event Type Error",
"OK")
msg4[0].grab_set()
msg4[2].config(command=err_done5)
return
if couple_event_new in (0, 1):
cur.execute(
update_event_types, (event_type_id, self.event))
conn.commit()
ChangeDate(self.current_person_id, tag="person", conn=conn, cur=cur)
else:
print("line", look(seeline()).lineno, "case not handled:")
initval = self.initial
event_types = self.get_all_event_types()
self.final = self.final.strip().lower()
if (self.initial == 'offspring' and len(self.final) != 0):
msg = open_message(
self,
events_msg[2],
"Offspring Event Edit Error",
"OK")
msg[0].grab_set()
msg[2].config(command=err_done4)
return
if self.final == 'offspring':
msg = open_message(
self,
events_msg[6],
"Change to Offspring Event Error",
"OK")
msg[0].grab_set()
msg[2].config(command=err_done7)
return
if self.final in event_types:
update_to_existing_type()
elif len(self.final) == 0:
initial = self.initial
self.delete_event(self.event, widg, initial)
else:
msg = open_message(
self.treebard,
events_msg[8],
"Unknown Event Type",
"OK")
msg[0].grab_set()
msg[2].config(command=err_done3)
return
def update_date():
self.final = validate_date(
self.treebard, self.inwidg, self.final, self.formats)
if not self.final:
self.final = "-0000-00-00-------"
sorter = make_date_sorter(self.final)
if self.final == "----------":
self.final = "-0000-00-00-------"
cur.execute(
update_event_date, (self.final, sorter, self.event))
conn.commit()
ChangeDate(self.current_person_id, tag="person", conn=conn, cur=cur)
formatted_date = format_stored_date(
self.final, date_prefs=self.date_prefs)
widg.delete("1.0", "end")
widg.insert("1.0", formatted_date)
redraw_person_tab(
main=self.main, current_person_id=self.current_person_id,
family_tree=self.family_tree, current_file=self.current_file,
cur=cur, conn=conn, family_tree_id=self.family_tree_id)
def update_place():
if self.final not in self.family_tree.place_autofill_values:
self.final = f"{self.final}+"
self.final = ValidatePlace(
self.treebard,
self.inwidg,
self.initial,
self.final,
self.place_data,
self.formats,
self.family_tree,
self.family_tree_id,
self.current_file,
self.main,
event=self.event)
def update_age(offspring_event, row):
if (event_string == "birth" and
self.final not in (0, "0", "0d 0m 0y")):
return
if couple is False and offspring_event is False:
cur.execute(update_event_age, (self.final, self.event))
conn.commit()
else:
cur.execute(select_event_persons, (self.event,))
right_person = cur.fetchone()
if right_person[0] == self.current_person_id:
cur.execute(
update_event_age1,
(self.final, self.event))
elif right_person[1] == self.current_person_id:
cur.execute(
update_event_age2,
(self.final, self.event))
conn.commit()
ChangeDate(self.current_person_id, tag="person", conn=conn, cur=cur)
conn = sqlite3.connect(appwide_db_path)
conn.execute('PRAGMA foreign_keys = 1')
cur = conn.cursor()
cur.execute("ATTACH ? as tree", (self.current_tree,))
if col_num == 0:
event_type_ok = True
if event_type_ok:
update_event_type()
elif col_num == 1:
update_date()
elif col_num == 2:
update_place()
elif col_num == 3:
self.update_particulars(self.final, self.event, conn, cur)
elif col_num == 4:
couple = False
offspring_event = False
for row in self.cell_pool:
if row[0] == self.event:
event_string = self.events_data[self.event]["event"]
cur.execute(select_event_type_couple_bool, (event_string,))
couple_or_not = cur.fetchone()[0]
if couple_or_not == 1:
couple = True
else:
couple = False
if event_string == "offspring":
offspring_event = True
row = row
break
update_age(offspring_event, row)
cur.execute("DETACH tree")
cur.close()
conn.close()
def ok_edit_cell(self, evt, widg, col):
self.get_final(widg, col)
ValidatePlace.initial = ""
def sort_by_date(self):
after_death = []
for event_id in self.events_data:
event_type = self.events_data[event_id]['event']
sorter = self.events_data[event_id]['sorter']
if event_type in self.after_death_events:
after_death.append([event_id, event_type, sorter])
after_death = sorted(after_death, key=lambda i: i[2])
m = 0
for lst in after_death:
lst[2] = [20000 + m, 0, 0]
m += 1
undated = []
row_order = []
for event_id in self.events_data:
event_type = self.events_data[event_id]['event']
date = self.events_data[event_id]['date']
if event_type == "birth":
sorter = [-10000, 0, 0]
row_order.append([event_id, event_type, sorter])
elif event_type == "death":
sorter = [10000, 0, 0]
row_order.append([event_id, event_type, sorter])
elif event_type in (self.after_death_events):
pass
elif len(date) == 0:
undated.append([event_id, event_type, sorter])
else:
sorter = self.events_data[event_id]['sorter']
row_order.append([event_id, event_type, sorter])
undated = sorted(undated, key=lambda i: i[1])
n = 0
for lst in undated:
lst[2] = [30000 + n, 0, 0]
n += 1
row_order = sorted(row_order, key = lambda i: i[2])
all_sorted = row_order + after_death + undated
new_order = []
for lst in all_sorted:
new_order.append(lst[0])
return new_order
def open_assertions_dialog(self, event_id=None, widg=None):
position = None
if event_id in self.family_tree.open_assertions_dialogs_tracker["event_ids"]:
assertion_dialog = self.family_tree.open_assertions_dialogs_tracker[
"event_ids"][event_id]
position = assertion_dialog.close_dlg()
AssertionsDialog(
self.family_tree, self.family_tree_id, self.treebard, self,
widg, self.current_person_id,
event_id=event_id, position=position)
self.family_tree.create_source_lists()
def make_header(self):
y = 0
for heading in HEADS:
head = tk.Label(self, text=heading.upper(), anchor='w')
head.grid(column=y, row=0, sticky='ew')
self.headers.append(head)
y += 1
sep = Separator(self, height=2)
sep.grid(column=0, row=1, columnspan=9, sticky='ew')
def fix_tab_traversal(self):
def third_and_second_items(pos):
return pos[2], pos[1]
row_fixer = []
for lst in self.cell_pool:
for cell in lst[1]:
grid_info = cell.grid_info()
column = grid_info["column"]
row = grid_info["row"]
row_fixer.append((cell, column, row))
row_fixer_2 = sorted(row_fixer, key=third_and_second_items)
widgets = []
for tup in row_fixer_2:
widgets.append(tup[0])
for widg in widgets:
widg.lift()
def count_birth_death_events(self, new_conclusion, cur):
too_many = False
cur.execute(select_events_for_person, (self.current_person_id,))
all_events = [i[0] for i in cur.fetchall()]
if new_conclusion in all_events:
too_many = True
return too_many
def make_new_conclusion(self):
""" Disallow creation of second birth or death event. """
def err_done6():
self.new_event_input.delete(0, 'end')
msg[0].destroy()
self.focus_set()
conn = sqlite3.connect(appwide_db_path)
conn.execute("PRAGMA foreign_keys = 1")
cur = conn.cursor()
cur.execute("ATTACH ? as tree", (self.current_tree,))
too_many = False
new_conclusion = self.new_event_input.get().strip().lower()
if new_conclusion in ("birth", "death"):
too_many = self.count_birth_death_events(new_conclusion, cur)
if too_many is True:
msg = open_message(
self,
events_msg[5],
"Multiple Birth or Death Conclusions",
"OK")
msg[0].grab_set()
msg[2].config(command=err_done6)
return
self.new_conclusion = new_conclusion
self.get_new_event_data(cur)
if self.unknown_event_type is False:
if self.is_couple_event is False:
if self.new_conclusion == "offspring":
msg = open_message(
self.treebard,
events_msg[7],
"Offspring Event Creation Error",
"OK")
msg[0].grab_set()
return
self.add_event()
self.new_event_input.delete(0, 'end')
cur.execute("DETACH tree")
cur.close()
conn.close()
def add_event(self):
redraw = True
conn = sqlite3.connect(appwide_db_path)
conn.execute('PRAGMA foreign_keys = 1')
cur = conn.cursor()
cur.execute("ATTACH ? as tree", (self.current_tree,))
if self.is_couple_event is False and self.event_type_id is not None:
cur.execute(
insert_event_new, (self.event_type_id, self.current_person_id))
conn.commit()
elif self.is_couple_event:
cur.execute(select_person_gender_by_id, (self.current_person_id,))
side = cur.fetchone()[0]
if side == "unknown":
cur.execute(select_couple_count_person1, (self.current_person_id,))
result = cur.fetchone()[0]
if result != 0:
side = "left"
person_col = "person_id1"
else:
cur.execute(select_couple_count_person2, (self.current_person_id,))
result = cur.fetchone()[0]
if result != 0:
side = "right"
person_col = "person_id2"
else: # Current person is not in any couples.
side = self.ask_user_for_gender()
if side in ("male", "female"):
cur.execute(
update_person_gender,
(side, self.current_person_id))
conn.commit()
stuff = self.decide_gender(side)
if stuff:
side, person_col = self.decide_gender(side)
else:
return None
else:
side, person_col = self.decide_gender(side)
insert_couple_half = f'''
INSERT INTO couple ({person_col}) VALUES (?)
'''
insert_event_new_couple = f'''
INSERT INTO event (event_type_id, couple_id)
VALUES (?, ?)
'''
cur.execute(insert_couple_half, (self.current_person_id,))
conn.commit()
new_couple_id = cur.lastrowid
cur.execute(
insert_event_new_couple, (self.event_type_id, new_couple_id))
else:
redraw = False
ChangeDate(self.current_person_id, tag="person", conn=conn, cur=cur)
if redraw:
redraw_person_tab(
main=self.main, family_tree_id=self.family_tree_id,
current_person_id=self.current_person_id, family_tree=self.family_tree,
current_file=self.current_file, conn=conn, cur=cur)
TabBook.resize_scrolled_dialog_with_tabbook(
self.family_tree, self.main.canvas, self.main)
cur.execute("DETACH tree")
cur.close()
conn.close()
def decide_gender(self, side):
if side in ("left", "male", 0, 2):
side = "left"
person_col = "person_id1"
elif side in ("right", "female", 1, 3):
side = "right"
person_col = "person_id2"
else:
return None
return side, person_col
def ask_user_for_gender(self):
def ok():
got = self.gender.get()
if got in (0, 1, 2, 3):
msg.destroy()
self.treebard.grab_set()
elif got == 99:
radframe.winfo_children()[0].focus_set()
else:
print("line", look(seeline()).lineno, "case not handled:",)
def cancel():
msg.destroy()
self.treebard.grab_set()
def show():
gotten = self.gender.get()
return gotten
msg = tk.Toplevel(self.treebard)
msg.grab_set()
msg.title("Gender is Unknown")
msg.protocol("WM_DELETE_WINDOW", cancel)
lab = tk.Label(
msg,
text="Tell Treebard how to display the current person in the "
"parent area of their childrens' families tables.",
justify='left', font=("courier", 14, "bold"), wraplength=450,
bd=1, relief="raised")
lab.grid(
column=0, row=0, sticky='news', padx=12, pady=12,
columnspan=2, ipadx=6, ipady=3)
radframe = tk.Frame(msg)
radframe.grid(column=0, row=1, padx=12)
self.gender = tk.IntVar(None, 99)
RADS = (
("male", 0, 0),
("female", 1, 0),
("display on left side", 0, 1),
("display on right side", 1, 1))
for idx, tup in enumerate(RADS):
text, col, row = tup
rad = tk.Radiobutton(
radframe, text=text, variable=self.gender, value=idx)
rad.grid(column=col, row=row, padx=48, pady=48, sticky="news")
if idx == 0:
rad.focus_set()
buttonbox = tk.Frame(msg.window)
buttonbox.grid(column=0, row=2, sticky='e', padx=(0,12), pady=12)
ok_butt = tk.Button(
buttonbox, text="OK", command=cancel, width=7)
ok_butt.grid(column=0, row=0, padx=6, sticky='e')
msg.resize_window()
self.treebard.wait_window(msg)
gotten = show()
return gotten
def get_new_event_data(self, cur):
def err_done2():
msg[0].destroy()
self.new_event_input.focus_set()
self.new_event_input.delete(0, 'end')
cur.execute(select_event_type_id, (self.new_conclusion,))
result = cur.fetchone()
if result is not None:
self.event_type_id, is_couple_event = result
if is_couple_event == 1:
self.is_couple_event = True
elif is_couple_event == 0:
self.is_couple_event = False
else:
self.unknown_event_type = True
msg = open_message(
self.treebard,
events_msg[8],
"Unknown Event Type",
"OK")
msg[0].grab_set()
msg[2].config(command=err_done2)
return
def update_particulars(self, input_string, event, conn, cur):
cur.execute(
update_event_particulars,
(input_string, event))
conn.commit()
def get_after_death_event_types(self, cur):
cur.execute(select_event_type_after_death)
after_death_event_types = [i[0] for i in cur.fetchall()]
return after_death_event_types
def get_couple_event_types(self, cur):
cur.execute(select_all_event_types_couple)
couple_event_types = [i[0] for i in cur.fetchall()]
return couple_event_types
def get_place_string(self, event_id, cur):
place_string = ""
cur.execute(select_event_nested_place, event_id)
result = cur.fetchone()
if result:
place_string = ", ".join([i for i in result if i != "unknown"])
return place_string
def get_generic_events(
self, dkt, cur, event_id, events_data, non_empty_roles, non_empty_notes):
cur.execute(select_events_details_generic, event_id)
generic_details = [i for i in cur.fetchone()]
dkt["date"] = format_stored_date(generic_details[3], date_prefs=self.date_prefs)
dkt["event"], dkt["particulars"], dkt["age"] = generic_details[0:3]
dkt["sorter"] = split_sorter(generic_details[4])
place = self.get_place_string(event_id, cur)
dkt["place"] = place
if event_id[0] in non_empty_roles:
self.get_role_events(dkt, event_id[0], cur)
if event_id[0] in non_empty_notes:
self.get_note_events(dkt, event_id[0], cur)
cur.execute(select_count_event_id_sources, event_id)
source_count = cur.fetchone()[0]
dkt["source_count"] = source_count
events_data[event_id[0]] = dkt
def get_couple_events(
self, cur, rowtotype, events_data, non_empty_roles, non_empty_notes):
couple_event_type_ids = self.get_couple_event_types(cur)
curr_per_evt_types = tuple([self.current_person_id] + couple_event_type_ids)
select_event_parent1 = '''
SELECT event_id
FROM event
JOIN couple ON event.couple_id = couple.couple_id
JOIN event_type ON event_type.event_type_id = event.event_type_id
WHERE person_id1 = ?
AND event_types IN ({})
'''.format(
','.join('?' * (len(curr_per_evt_types) - 1)))
cur.execute(select_event_parent1, curr_per_evt_types)
couple_events1 = [i[0] for i in cur.fetchall()]
select_event_parent2 = '''
SELECT event_id
FROM event
JOIN couple ON event.couple_id = couple.couple_id
JOIN event_type ON event_type.event_type_id = event.event_type_id
WHERE person_id2 = ?
AND event_types in ({})
'''.format(
','.join('?' * (len(curr_per_evt_types) - 1)))
cur.execute(select_event_parent2, curr_per_evt_types)
couple_events2 = [i[0] for i in cur.fetchall()]
couple_events = couple_events1 + couple_events2
for event_id in couple_events:
event_id = (event_id,)
dkt = dict(rowtotype)
cur.execute(select_event_couple_details, event_id)
gotgot = cur.fetchone()
if gotgot:
name = ""
if gotgot[0] == self.current_person_id:
iD = gotgot[2]
if iD:
name = self.family_tree.person_autofill_values[
iD][0]["name"]
dkt["age"] = gotgot[1]
dkt["partner_id"] = iD
dkt["partner_name"] = name
elif gotgot[2] == self.current_person_id:
iD = gotgot[0]
if iD:
name = self.family_tree.person_autofill_values[
iD][0]["name"]
dkt["age"] = gotgot[3]
dkt["partner_id"] = iD
dkt["partner_name"] = name
cur.execute(select_family_events_details_couple_generic, event_id)
couple_generics = list(cur.fetchone())
couple_generics[1] = format_stored_date(
couple_generics[1], date_prefs=self.date_prefs)
place = self.get_place_string(event_id, cur)
dkt["place"] = place
sorter = split_sorter(couple_generics[2])
couple_generic_details = [
couple_generics[0],
couple_generics[1],
sorter,
couple_generics[3]]
if event_id[0] in non_empty_roles:
self.get_role_events(dkt, event_id[0], cur)
if event_id[0] in non_empty_notes:
self.get_note_events(dkt, event_id[0], cur)
cur.execute(select_count_event_id_sources, event_id)
source_count = cur.fetchone()[0]
dkt["source_count"] = source_count
dkt["event"], dkt["date"], dkt["sorter"], dkt["particulars"] = couple_generic_details
dkt["side"] = self.get_partner_side(event_id, cur)
events_data[event_id[0]] = dkt
def get_partner_side(self, event_id, cur):
side = None
cur.execute(select_event_couple_id, event_id)
self.couple_id = cur.fetchone()[0]
select_couple_partner1 = '''
SELECT person_id1
FROM couple
WHERE person_id1 = ?
AND couple_id = ?
'''
select_couple_partner2 = '''
SELECT person_id2
FROM couple
WHERE person_id2 = ?
AND couple_id = ?
'''
cur.execute(
select_couple_partner1, (self.current_person_id, self.couple_id))
result1 = cur.fetchone()
if result1 is None:
cur.execute(
select_couple_partner2, (self.current_person_id, self.couple_id))
result2 = cur.fetchone()
if result2:
side = "right"
else:
pass
else:
side = "left"
return side
def autocreate_parent_events(self, dkt, cur, events_data, event_id):
""" Get birth & alt_birth events to autocreate rows when parent
is current person.
"""
def get_event_type_string(iD):
cur.execute(select_event_event_type, (iD,))
event_type_id = cur.fetchone()[0]
conversion = {
1: "offspring", 83: "adopted a child", 95: "fostered a child",
48: "acted as guardian"}
for k,v in conversion.items():
if event_type_id == k:
event_type = v
return event_type
side = self.get_partner_side(event_id, cur)
cur.execute(select_event_id_offspring_by_parent, (
self.current_person_id, self.current_person_id))
offspring_event_ids = [list(i) for i in cur.fetchall()]
age_query = None
if side == "left":
age_query = select_event_age1
elif side == "right":
age_query = select_event_age2
else:
pass
for idx, lst in enumerate(list(offspring_event_ids)):
if age_query:
cur.execute(age_query, (lst[0], self.current_person_id))
result = cur.fetchone()
if result:
offspring_event_ids[idx].append(result[0])
else:
offspring_event_ids[idx].append("")
for idx, child in enumerate(list(offspring_event_ids)):
cur.execute(select_person, (child[0],))
results = cur.fetchone()
if results:
offspring_event_ids[idx].append(results[0])
for lst in offspring_event_ids:
offspring_event_id, parent_age, child_id = lst
cur.execute(
select_event_details_offspring_alt_parentage,
(child_id, offspring_event_id))
offspring_details = cur.fetchone() # date, date sorter, particulars
event_type = get_event_type_string(offspring_event_id)
child_name = self.family_tree.person_autofill_values[
child_id][0]["name"]
sorter = split_sorter(offspring_details[1])
date = format_stored_date(
offspring_details[0], date_prefs=self.date_prefs)
particulars = offspring_details[2]
place = self.get_place_string((offspring_event_id,), cur)
cur.execute(select_count_event_id_sources, (offspring_event_id,))
source_count = cur.fetchone()[0]
events_data[offspring_event_id] = {}
events_data[offspring_event_id]["event"] = event_type
events_data[offspring_event_id]["date"] = date
events_data[offspring_event_id]["place"] = place
events_data[offspring_event_id]["particulars"] = particulars
events_data[offspring_event_id]["age"] = parent_age
events_data[offspring_event_id]["source_count"] = source_count
events_data[offspring_event_id]["child_id"] = child_id
events_data[offspring_event_id]["child_name"] = child_name
events_data[offspring_event_id]["sorter"] = sorter
def get_role_events(
self, dkt, event_id, cur, events_data=None):
current_roles = []
current_roles.append(event_id)
if events_data is None:
dkt["roles"] = current_roles
else:
events_data[event_id]["roles"] = current_roles
def get_note_events(
self, dkt, event_id, cur, events_data=None):
current_notes = []
current_notes.append(event_id)
if events_data is None:
dkt["notes"] = current_notes
else:
events_data[event_id]["notes"] = current_notes
def get_events(self, cur):
events_data = {}
rowtotype = {
"event": "", "date": "", "place": "", "particulars": "", "age": ""}
cur.execute(select_all_events_current_person, (self.current_person_id,))
generic_event_ids = cur.fetchall()
cur.execute(select_all_events_roles_ids_distinct)
non_empty_roles = [i[0] for i in cur.fetchall()]
cur.execute(select_all_events_notes_ids)
non_empty_notes = [i[0] for i in cur.fetchall()]
self.get_couple_events(
cur, rowtotype, events_data, non_empty_roles, non_empty_notes)
for event_id in generic_event_ids:
dkt = dict(rowtotype)
self.get_generic_events(
dkt, cur, event_id, events_data, non_empty_roles, non_empty_notes)
if dkt["event"] == "birth":
self.autocreate_parent_events(
dkt, cur, events_data, event_id)
elif dkt["event"] == "adoption":
self.autocreate_parent_events(
dkt, cur, events_data, event_id)
elif dkt["event"] == "fosterage":
self.autocreate_parent_events(
dkt, cur, events_data, event_id)
elif dkt["event"] == "guardianship":
self.autocreate_parent_events(
dkt, cur, events_data, event_id)
self.get_parents(cur, event_id[0], events_data)
return events_data, non_empty_roles, non_empty_notes
def get_parents(self, cur, event_id, events_data):
""" For kintips. """
left_id = None
right_id = None
cur.execute(select_couple_by_event_id, (event_id,))
result = cur.fetchone()
if result:
left_id, right_id = result
events_data[event_id]["id"] = f"{str(left_id)} {str(right_id)}"