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\events_table.py Last Changed 2024-07-25
# events_table.py
import tkinter as tk
import sqlite3
from base import tree_path
from redraw import get_name_from_id, Redraw
from base import split_sorter
from widgets import (
LabelDots, LabelButtonText, open_message, Separator, configall, LabelH3,
CellAutoEventType, Cell, TabBook, EntryAutoEventType, CellAutoPlace,
NEUTRAL_COLOR)
from dates import (
validate_date, format_stored_date, get_date_formats, make_date_sorter)
from roles import RolesDialog
from notes import NotesDialog
from persons import autocreate_unknown_person
from places import ValidatePlace
from assertions import AssertionsDialog
from messages import events_msg
from unigeds_queries import (
select_event_nested_place,
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_age1,
insert_event_new, select_couple_count_person1, insert_event_couple,
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,
update_event_age2, select_person, select_count_offspring_notes,
select_event_persons, update_event_date, select_event_age2,
select_event_id_offspring_by_parent, update_person_gender,
select_event_details_offspring_alt_parentage, select_person_gender_by_id,
select_couple_count_person2, select_couple_partner1, select_couple_partner2
)
import dev_tools as dt
from dev_tools import look, seeline
SELECT_EVENT_TYPE_ID = '''
SELECT traits_tbd.event_type_id, couple
FROM event_type
JOIN traits_tbd ON traits_tbd.event_type_id = event_type.event_type_id
WHERE event_type_text = ?
'''
def get_event_types_for_kintips(conn, cur):
cur.execute(
''' SELECT event_type.event_type_id, event_type_text
FROM event_type
JOIN traits_tbd
ON traits_tbd.event_type_id = event_type.event_type_id
WHERE event_type_text in (
'birth', 'fosterage', 'adoption', 'guardianship')
OR couple = 1
''')
return cur.fetchall()
# Use extra spaces in headings only to set column widths for empty table
# columns so the table cell doesn't keep changing sizes while you try to type
# into it the first time. This simple method doesn't interfere with
# `Cell.set_height`.
HEADS = (
'event', 'date ', 'place ', 'particulars ', 'age',
'roles', 'notes', 'sources')
class EventsTable(tk.Frame):
def __init__(
self, master, formats, tree, treebard, main, *args, **kwargs):
tk.Frame.__init__(self, master, *args, **kwargs)
self.master = master
self.formats = formats
self.tree = tree
self.treebard = treebard
self.main = main
self.main_canvas = main.master
self.inwidg = None
self.headers = []
self.date_prefs = get_date_formats()
self.initial = None
self.event_type_id = None
self.screen_height = self.winfo_screenheight()
self.column_padding = 2
self.new_row = 0
EntryAutoEventType.create_lists()
CellAutoEventType.create_lists()
self.tree.person_autofill_values = self.tree.update_person_autofill_values()
conn = sqlite3.connect(self.tree.file)
conn.execute("PRAGMA foreign_keys = 1")
cur = conn.cursor()
# 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
rdw = Redraw(main=self.main, formats=self.formats, tree=self.tree)
self.tree.bind("<Control-F5>", rdw.redraw_gui)
self.is_couple_event = False
self.unknown_event_type = False
self.populate_events_table(cur)
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(cur)
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.tree,
values=self.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.tree, self.treebard, RolesDialog)
elif column == 6:
cell = LabelDots(
self, self.tree,
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.persid = self.tree.persid
# 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.persid = self.tree.persid
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, cur):
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:
if column == 1:
text = self.events_data[event_id][HEADS[column][0:4]]
elif column == 2:
text = self.events_data[event_id][HEADS[column][0:5]]
elif column == 3:
text = self.events_data[event_id][HEADS[column][0:11]]
else:
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, cur)
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, cur):
event_to_kin = {
("birth", "fosterage", "guardianship", "adoption"): "parents",
tuple(self.couple_event_types): "partner",
("offspring", ): "child",
("adopted a child",): "adopted child",
("fostered a child",): "fostered child",
("acted as guardian",): "ward"}
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", "adopted a child", "fostered a child",
"acted as guardian")):
widg["cursor"] = "hand2"
widg.bind("<Enter>", self.show_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 in (
"child", "adopted child", "fostered child",
"ward") 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), cur)
if right_id:
right_name = get_name_from_id(int(right_id), cur)
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']}"
if text == "parents: #None unknown & #None unknown":
return
self.kin_tip = tk.Toplevel(self, bd=1)
cell.bind("<Leave>", self.unshow_kintip, add="+")
x, y, cx, cy = cell.bbox("insert")
x = x + cell.winfo_rootx() + 27
y = y + cy + cell.winfo_rooty() + 27
self.kin_tip.wm_overrideredirect(1)
self.kin_tip.wm_geometry(f"+{x}+{y}")
lab = tk.Label(
self.kin_tip, text=text, bg=NEUTRAL_COLOR,
fg="black", justify='left', bd=0)
lab.pack(ipadx=6, ipady=6)
def unshow_kintip(self, evt):
self.kin_tip.destroy()
def get_all_event_types(self):
conn = sqlite3.connect(self.tree.file)
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()
rdw = Redraw(main=self.main, tree=self.tree)
rdw.redraw_person_tab()
TabBook.resize_scrolled_dialog_with_tabbook(
self.tree, self.main.canvas, self.main)
conn = sqlite3.connect(self.tree.file)
cur = conn.cursor()
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", formats=self.formats)
msg1[0].grab_set()
msg1[2].config(command=err_done1)
return
ok_delete_event(conn, cur)
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",
formats=self.formats)
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()
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", formats=self.formats)
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", formats=self.formats)
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", formats=self.formats)
msg[0].grab_set()
msg[2].config(command=err_done3)
return
def update_date():
self.final = validate_date(self.treebard, 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()
formatted_date = format_stored_date(
self.final, date_prefs=self.date_prefs)
widg.delete("1.0", "end")
widg.insert("1.0", formatted_date)
def update_place():
if self.final not in self.tree.place_autofill_values:
self.final = f"{self.final}+"
ValidatePlace(
self.treebard, self.inwidg, self.initial, self.final,
self.formats, self.tree, self.main, event=self.event)
if self.tree.placid:
prepend_this = self.final.replace("+", "")
if prepend_this in self.tree.prepended_places:
idx = self.tree.prepended_places.index(prepend_this)
del self.tree.prepended_places[idx]
self.tree.prepended_places.insert(0, prepend_this)
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.tree.persid:
cur.execute(
update_event_age1,
(self.final, self.event))
elif right_person[1] == self.tree.persid:
cur.execute(
update_event_age2,
(self.final, self.event))
conn.commit()
conn = sqlite3.connect(self.tree.file)
conn.execute('PRAGMA foreign_keys = 1')
cur = conn.cursor()
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.close()
conn.close()
def ok_edit_cell(self, evt, widg, col):
self.get_final(widg, col)
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.tree.open_assertions_dialogs_tracker["event_ids"]:
assertion_dialog = self.tree.open_assertions_dialogs_tracker[
"event_ids"][event_id]
position = assertion_dialog.close_dlg()
AssertionsDialog(
self.tree,
# self.tree.tree_id,
self.treebard, self, widg,
# self.tree.persid,
event_id=event_id, position=position)
self.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, self.formats, 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.tree.persid,))
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):
""" Don't allow 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(self.tree.file)
conn.execute("PRAGMA foreign_keys = 1")
cur = conn.cursor()
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:
msg = open_message(self, events_msg[5],
"Multiple Birth or Death Conclusions", "OK",
formats=self.formats)
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",
formats=self.formats)
msg[0].grab_set()
return
self.add_event()
self.new_event_input.delete(0, 'end')
cur.close()
conn.close()
def add_event(self):
re_draw = True
conn = sqlite3.connect(self.tree.file)
conn.execute('PRAGMA foreign_keys = 1')
cur = conn.cursor()
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.tree.persid))
conn.commit()
elif self.is_couple_event:
self.add_couple_event(cur, conn)
else:
re_draw = False
if re_draw:
rdw = Redraw(main=self.main, tree=self.tree)
rdw.redraw_person_tab()
TabBook.resize_scrolled_dialog_with_tabbook(
self.tree, self.main.canvas, self.main)
cur.close()
conn.close()
def add_couple_event(self, cur, conn):
cur.execute(
''' SELECT person_id2, name_text, couple_id
FROM couple
JOIN name ON couple.person_id2 = name.person_id
JOIN main_tbd ON main_tbd.name_id = name.name_id
WHERE person_id1 = ?
''',
(self.tree.persid,))
partners1 = cur.fetchall()
partner_count1 = len(partners1)
cur.execute(
''' SELECT person_id1, name_text, couple_id
FROM couple
JOIN name ON couple.person_id1 = name.person_id
JOIN main_tbd ON main_tbd.name_id = name.name_id
WHERE person_id2 = ?
''',
(self.tree.persid,))
partners2 = cur.fetchall()
partner_count2 = len(partners2)
cur.execute(select_person_gender_by_id, (self.tree.persid,))
side = cur.fetchone()[0]
if side == "unknown":
if partner_count1 != 0:
side = "left"
person_col = "person_id1"
other_person_col = "person_id2"
else:
if partner_count2 != 0:
side = "right"
person_col = "person_id2"
other_person_col = "person_id1"
else: # Current person is not in any couples.
side = self.ask_user_for_gender()
if side in (0, 1):
if side == 0:
gender = "male"
elif side == 1:
gender = "female"
cur.execute(
update_person_gender,
(gender, self.tree.persid))
conn.commit()
stuff = self.decide_gender(side)
if stuff:
person_col, other_person_col = stuff
else:
return None
else:
person_col, other_person_col = self.decide_gender(side)
partners = partners1 + partners2
if len(partners) != 0:
couple_id = self.open_partner_selector(partners, conn, cur)
if couple_id == 999999:
couple_id = autocreate_unknown_person(
self.treebard, self.tree.persid, person_col,
other_person_col, conn, cur)
else:
couple_id = autocreate_unknown_person(
self.treebard, self.tree.persid, person_col,
other_person_col, conn, cur)
cur.execute(
insert_event_couple, (self.event_type_id, couple_id))
conn.commit()
def open_partner_selector(self, partners, conn, cur):
def show():
return self.pardvar.get()
def cancel():
self.cancel_was_pressed = True
dlg.destroy()
def ok():
dlg.destroy()
self.cancel_was_pressed = False
self.pardvar = tk.IntVar()
dlg = tk.Toplevel(self.tree)
dlg.title("Partner Selector")
lab = LabelH3(
dlg, self.formats, text="Select a partner for the new couple event.")
for idx,tup in enumerate(partners, 1):
partner_id, partner_name, couple_id = tup
rad = tk.Radiobutton(
dlg, value=couple_id, variable=self.pardvar,
text=f"person ID #{partner_id}: {partner_name}")
rad.grid(column=0, row=idx, sticky="w", padx=12, pady=12)
idx = idx
rad2 = tk.Radiobutton(
dlg, value=999999, text="Create a new partner with an unknown name.",
variable=self.pardvar)
buttons = tk.Frame(dlg)
oker = tk.Button(buttons, text="OK", width=8, command=ok)
noper = tk.Button(buttons, text="CANCEL", width=8, command=cancel)
rad2.grid(column=0, row=idx+1, sticky="w", padx=12, pady=12)
lab.grid(column=0, row=0, sticky="ew", padx=12, pady=12)
buttons.grid(column=0, row=idx+2, sticky="e", padx=12, pady=12)
oker.grid(column=0, row=0)
noper.grid(column=1, row=0, padx=(6,0))
configall(dlg, self.formats)
self.tree.wait_window(dlg)
if self.cancel_was_pressed:
return
gotten = show()
return gotten
def decide_gender(self, side):
if side in ("left", "male", 0, 2):
side = "left"
person_col = "person_id1"
other_person_col = "person_id2"
elif side in ("right", "female", 1, 3):
side = "right"
person_col = "person_id2"
other_person_col = "person_id1"
else:
return None
return person_col, other_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=("verdana", 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)
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')
configall(msg, self.formats)
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:
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", formats=self.formats)
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_text
FROM event_type
JOIN traits_tbd
ON traits_tbd.event_type_id = event_type.event_type_id
WHERE after_death = 1
''')
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 event_type_text
FROM event_type
JOIN traits_tbd
ON traits_tbd.event_type_id = event_type.event_type_id
WHERE couple = 1
''')
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.tree.persid] + couple_event_type_ids)
select_event_parent1 = f'''
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_type_text IN ({','.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 = f'''
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_type_text in ({','.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.tree.persid:
iD = gotgot[2]
if iD:
name = get_name_from_id(iD, cur)
dkt["age"] = gotgot[1]
dkt["partner_id"] = iD
dkt["partner_name"] = name
elif gotgot[2] == self.tree.persid:
iD = gotgot[0]
if iD:
name = get_name_from_id(iD, cur)
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]
cur.execute(
select_couple_partner1, (self.tree.persid, self.couple_id))
result1 = cur.fetchone()
if result1 is None:
cur.execute(
select_couple_partner2, (self.tree.persid, 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 parentage 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.tree.persid, self.tree.persid))
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.tree.persid))
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()
event_type = get_event_type_string(offspring_event_id)
child_name = get_name_from_id(child_id, cur)
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
cur.execute(select_count_offspring_notes, (child_id,))
result = cur.fetchone()
if result[0] > 0:
# Add 'notes' key to configure LabelDots text = ' ... '.
events_data[offspring_event_id]["notes"] = [True]
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.tree.persid,))
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"] in ("birth", "adoption", "fosterage", "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)}"