2008-04-18 06:40:50 +05:30
|
|
|
#
|
|
|
|
# Gramps - a GTK+/GNOME based genealogy program
|
|
|
|
#
|
|
|
|
# Copyright (C) 2000-2007 Donald N. Allingham
|
|
|
|
# Copyright (C) 2007-2008 Brian G. Matherly
|
|
|
|
#
|
|
|
|
# This program is free software; you can redistribute it and/or modify
|
|
|
|
# it under the terms of the GNU General Public License as published by
|
|
|
|
# the Free Software Foundation; either version 2 of the License, or
|
|
|
|
# (at your option) any later version.
|
|
|
|
#
|
|
|
|
# This program is distributed in the hope that it will be useful,
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
# GNU General Public License for more details.
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU General Public License
|
|
|
|
# along with this program; if not, write to the Free Software
|
|
|
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
#
|
|
|
|
|
|
|
|
"""
|
|
|
|
Run a query on the tables
|
|
|
|
"""
|
|
|
|
|
|
|
|
from Simple import SimpleAccess, SimpleDoc, SimpleTable
|
|
|
|
from gettext import gettext as _
|
2008-05-19 00:54:28 +05:30
|
|
|
from PluginUtils import PluginManager
|
2008-04-18 06:40:50 +05:30
|
|
|
import Utils
|
|
|
|
from ReportBase import CATEGORY_QR_MISC
|
|
|
|
import DateHandler
|
|
|
|
import gen.lib
|
|
|
|
import Config
|
|
|
|
|
|
|
|
def cleanup_column_name(column):
|
|
|
|
""" Handle column aliases for CSV spreadsheet import and SQL """
|
|
|
|
retval = column
|
|
|
|
# Title case:
|
|
|
|
if retval in ["Lastname",
|
|
|
|
"Surname", _("Surname")]:
|
|
|
|
return "surname"
|
|
|
|
elif retval in ["Firstname",
|
|
|
|
"Given name", _("Given name"),
|
|
|
|
"Given", _("Given")]:
|
|
|
|
return "firstname"
|
|
|
|
elif retval in ["Callname",
|
|
|
|
"Call name", _("Call name"),
|
|
|
|
"Call", _("Call")]:
|
|
|
|
return "callname"
|
|
|
|
elif retval in ["Title", _("Title")]:
|
|
|
|
return "title"
|
|
|
|
elif retval in ["Prefix", _("Prefix")]:
|
|
|
|
return "prefix"
|
|
|
|
elif retval in ["Suffix", _("Suffix")]:
|
|
|
|
return "suffix"
|
|
|
|
elif retval in ["Gender", _("Gender")]:
|
|
|
|
return "gender"
|
|
|
|
elif retval in ["Source", _("Source")]:
|
|
|
|
return "source"
|
|
|
|
elif retval in ["Note", _("Note")]:
|
|
|
|
return "note"
|
|
|
|
elif retval in ["Birthplace",
|
|
|
|
"Birth place", _("Birth place")]:
|
|
|
|
return "birthplace"
|
|
|
|
elif retval in ["Birthdate",
|
|
|
|
"Birth date", _("Birth date")]:
|
|
|
|
return "birthdate"
|
|
|
|
elif retval in ["Birthsource",
|
|
|
|
"Birth source", _("Birth source")]:
|
|
|
|
return "birthsource"
|
|
|
|
elif retval in ["Deathplace",
|
|
|
|
"Death place", _("Death place")]:
|
|
|
|
return "deathplace"
|
|
|
|
elif retval in ["Deathdate",
|
|
|
|
"Death date", _("Death date")]:
|
|
|
|
return "deathdate"
|
|
|
|
elif retval in ["Deathsource",
|
|
|
|
"Death source", _("Death source")]:
|
|
|
|
return "deathsource"
|
|
|
|
elif retval in ["Deathcause",
|
|
|
|
"Death cause", _("Death cause")]:
|
|
|
|
return "deathcause"
|
|
|
|
elif retval in ["Grampsid", "ID",
|
|
|
|
"Gramps id", _("Gramps id")]:
|
|
|
|
return "grampsid"
|
|
|
|
elif retval in ["Person", _("Person")]:
|
|
|
|
return "person"
|
|
|
|
# ----------------------------------
|
|
|
|
elif retval in ["Child", _("Child")]:
|
|
|
|
return "child"
|
|
|
|
elif retval in ["Source", _("Source")]:
|
|
|
|
return "source"
|
|
|
|
elif retval in ["Family", _("Family")]:
|
|
|
|
return "family"
|
|
|
|
# ----------------------------------
|
|
|
|
elif retval in ["Mother", _("Mother"),
|
|
|
|
"Wife", _("Wife"),
|
|
|
|
"Parent2", _("Parent2")]:
|
|
|
|
return "wife"
|
|
|
|
elif retval in ["Father", _("Father"),
|
|
|
|
"Husband", _("Husband"),
|
|
|
|
"Parent1", _("Parent1")]:
|
|
|
|
return "husband"
|
|
|
|
elif retval in ["Marriage", _("Marriage")]:
|
|
|
|
return "marriage"
|
|
|
|
elif retval in ["Date", _("Date")]:
|
|
|
|
return "date"
|
|
|
|
elif retval in ["Place", _("Place")]:
|
|
|
|
return "place"
|
|
|
|
# lowercase
|
|
|
|
elif retval in ["lastname", "last_name",
|
|
|
|
"surname", _("surname")]:
|
|
|
|
return "surname"
|
|
|
|
elif retval in ["firstname", "first_name", "given_name",
|
|
|
|
"given name", _("given name"),
|
|
|
|
"given", _("given")]:
|
|
|
|
return "firstname"
|
|
|
|
elif retval in ["callname", "call_name",
|
|
|
|
"call name",
|
|
|
|
"call", _("call")]:
|
|
|
|
return "callname"
|
|
|
|
elif retval in ["title", _("title")]:
|
|
|
|
return "title"
|
|
|
|
elif retval in ["prefix", _("prefix")]:
|
|
|
|
return "prefix"
|
|
|
|
elif retval in ["suffix", _("suffix")]:
|
|
|
|
return "suffix"
|
|
|
|
elif retval in ["gender", _("gender")]:
|
|
|
|
return "gender"
|
|
|
|
elif retval in ["source", _("source")]:
|
|
|
|
return "source"
|
|
|
|
elif retval in ["note", _("note")]:
|
|
|
|
return "note"
|
|
|
|
elif retval in ["birthplace", "birth_place",
|
|
|
|
"birth place", _("birth place")]:
|
|
|
|
return "birthplace"
|
|
|
|
elif retval in ["birthdate", "birth_date",
|
|
|
|
"birth date", _("birth date")]:
|
|
|
|
return "birthdate"
|
|
|
|
elif retval in ["birthsource", "birth_source",
|
|
|
|
"birth source", _("birth source")]:
|
|
|
|
return "birthsource"
|
|
|
|
elif retval in ["deathplace", "death_place",
|
|
|
|
"death place", _("death place")]:
|
|
|
|
return "deathplace"
|
|
|
|
elif retval in ["deathdate", "death_date",
|
|
|
|
"death date", _("death date")]:
|
|
|
|
return "deathdate"
|
|
|
|
elif retval in ["deathsource", "death_source",
|
|
|
|
"death source", _("death source")]:
|
|
|
|
return "deathsource"
|
|
|
|
elif retval in ["deathcause", "death_cause",
|
|
|
|
"death cause", _("death cause")]:
|
|
|
|
return "deathcause"
|
|
|
|
elif retval in ["grampsid", "id", "gramps_id",
|
|
|
|
"gramps id", _("gramps id")]:
|
|
|
|
return "grampsid"
|
|
|
|
elif retval in ["person", _("person")]:
|
|
|
|
return "person"
|
|
|
|
# ----------------------------------
|
|
|
|
elif retval in ["child", _("child")]:
|
|
|
|
return "child"
|
|
|
|
elif retval in ["source", _("source")]:
|
|
|
|
return "source"
|
|
|
|
elif retval in ["family", _("family")]:
|
|
|
|
return "family"
|
|
|
|
# ----------------------------------
|
|
|
|
elif retval in ["mother", _("mother"),
|
|
|
|
"wife", _("wife"),
|
|
|
|
"parent2", _("parent2")]:
|
|
|
|
return "wife"
|
|
|
|
elif retval in ["father", _("father"),
|
|
|
|
"husband", _("husband"),
|
|
|
|
"parent1", _("parent1")]:
|
|
|
|
return "husband"
|
|
|
|
elif retval in ["marriage", _("marriage")]:
|
|
|
|
return "marriage"
|
|
|
|
elif retval in ["date", _("date")]:
|
|
|
|
return "date"
|
|
|
|
elif retval in ["place", _("place")]:
|
|
|
|
return "place"
|
|
|
|
#----------------------------------------------------
|
|
|
|
return retval
|
|
|
|
|
|
|
|
|
|
|
|
class DBI:
|
|
|
|
def __init__(self, database, document):
|
|
|
|
self.database = database
|
|
|
|
self.document = document
|
|
|
|
|
|
|
|
def parse(self, query):
|
|
|
|
# select col1, col2 from table where exp;
|
|
|
|
# select * from table where exp;
|
|
|
|
# delete from table where exp;
|
|
|
|
self.query = query
|
|
|
|
state = "START"
|
|
|
|
data = None
|
|
|
|
i = 0
|
|
|
|
self.columns = []
|
|
|
|
self.command = None
|
|
|
|
self.where = None
|
|
|
|
self.table = None
|
|
|
|
while i < len(query):
|
|
|
|
c = query[i]
|
|
|
|
#print "STATE:", state, c
|
|
|
|
if state == "START":
|
|
|
|
if c in [' ', '\n', '\t']: # pre white space
|
|
|
|
pass # skip it
|
|
|
|
else:
|
|
|
|
state = "COMMAND"
|
|
|
|
data = c
|
|
|
|
elif state == "COMMAND":
|
|
|
|
if c in [' ', '\n', '\t']: # ending white space
|
|
|
|
self.command = data.lower()
|
|
|
|
data = ''
|
|
|
|
state = "AFTER-COMMAND"
|
|
|
|
else:
|
|
|
|
data += c
|
|
|
|
elif state == "AFTER-COMMAND":
|
|
|
|
if c in [' ', '\n', '\t']: # pre white space
|
|
|
|
pass
|
|
|
|
else:
|
|
|
|
state = "COL_OR_FROM"
|
|
|
|
i -= 1
|
|
|
|
elif state == "COL_OR_FROM":
|
|
|
|
if c in [' ', '\n', '\t', ',']: # end white space or comma
|
|
|
|
if data.upper() == "FROM":
|
|
|
|
data = ''
|
|
|
|
state = "PRE-GET-TABLE"
|
|
|
|
else:
|
|
|
|
self.columns.append(data.lower())
|
|
|
|
data = ''
|
|
|
|
state = "AFTER-COMMAND"
|
|
|
|
else:
|
|
|
|
data += c
|
|
|
|
elif state == "PRE-GET-TABLE":
|
|
|
|
if c in [' ', '\n', '\t']: # pre white space
|
|
|
|
pass
|
|
|
|
else:
|
|
|
|
state = "GET-TABLE"
|
|
|
|
i -= 1
|
|
|
|
elif state == "GET-TABLE":
|
|
|
|
if c in [' ', '\n', '\t', ';']: # end white space or colon
|
|
|
|
self.table = data.lower()
|
|
|
|
data = ''
|
|
|
|
state = "PRE-GET-WHERE"
|
|
|
|
else:
|
|
|
|
data += c
|
|
|
|
elif state == "PRE-GET-WHERE":
|
|
|
|
if c in [' ', '\n', '\t']: # pre white space
|
|
|
|
pass
|
|
|
|
else:
|
|
|
|
state = "GET-WHERE"
|
|
|
|
i -= 1
|
|
|
|
elif state == "GET-WHERE":
|
|
|
|
if c in [' ', '\n', '\t']: # end white space
|
|
|
|
if data.upper() != "WHERE":
|
|
|
|
raise AttributeError("expecting WHERE got '%s'" % data)
|
|
|
|
else:
|
|
|
|
data = ''
|
|
|
|
state = "GET-EXP"
|
|
|
|
else:
|
|
|
|
data += c
|
|
|
|
elif state == "GET-EXP":
|
|
|
|
self.where = query[i:]
|
|
|
|
self.where = self.where.strip()
|
|
|
|
if self.where.endswith(";"):
|
|
|
|
self.where = self.where[:-1]
|
|
|
|
i = len(query)
|
|
|
|
else:
|
|
|
|
raise AttributeError("unknown state: '%s'" % state)
|
|
|
|
i += 1
|
2008-06-16 20:31:46 +05:30
|
|
|
if self.table is None:
|
2008-04-18 06:40:50 +05:30
|
|
|
raise AttributeError("malformed query: no table in '%s'\n" % self.query)
|
|
|
|
|
|
|
|
def close(self):
|
|
|
|
try:
|
|
|
|
self.progress.close()
|
|
|
|
except:
|
|
|
|
pass
|
|
|
|
|
|
|
|
def eval(self):
|
|
|
|
self.sdb = SimpleAccess(self.database)
|
|
|
|
self.stab = SimpleTable(self.sdb)
|
|
|
|
self.select = 0
|
|
|
|
self.progress = Utils.ProgressMeter(_('Processing Query'))
|
2008-04-18 09:06:38 +05:30
|
|
|
self.process_table()
|
2008-04-18 06:40:50 +05:30
|
|
|
if self.select > 0:
|
|
|
|
self.sdoc = SimpleDoc(self.document)
|
|
|
|
self.sdoc.title(self.query)
|
|
|
|
self.sdoc.paragraph("\n")
|
|
|
|
self.sdoc.paragraph("%d rows processed.\n" % self.select)
|
|
|
|
self.stab.write(self.sdoc)
|
|
|
|
self.sdoc.paragraph("")
|
|
|
|
return _("[%d rows processed]") % self.select
|
|
|
|
|
|
|
|
def get_columns(self, table):
|
|
|
|
if table == "people":
|
|
|
|
return ("name", "grampsid", "gender",
|
|
|
|
"birth_date", "birth_place",
|
|
|
|
"death_date", "death_place",
|
|
|
|
"change", "marker", "private")
|
|
|
|
elif table == "events":
|
|
|
|
return ("grampsid", "type", "date",
|
|
|
|
"description", "place",
|
|
|
|
"change", "marker", "private")
|
2008-04-18 09:06:38 +05:30
|
|
|
# families = grampsid, father, mother, relationship, date
|
|
|
|
# sources =
|
|
|
|
# places =
|
|
|
|
# media =
|
|
|
|
# repos =
|
|
|
|
# notes =
|
|
|
|
else:
|
|
|
|
raise AttributeError("unknown table: '%s'" % table)
|
2008-04-18 06:40:50 +05:30
|
|
|
|
2008-04-18 09:06:38 +05:30
|
|
|
def process_table(self):
|
2008-04-18 06:40:50 +05:30
|
|
|
for col_name in self.columns[:]: # copy
|
|
|
|
if col_name == "*":
|
|
|
|
self.columns.remove('*')
|
|
|
|
self.columns.extend( self.get_columns(self.table))
|
|
|
|
self.stab.columns(*map(lambda s: s.replace("_", "__"),
|
|
|
|
self.columns))
|
|
|
|
if self.table == "people":
|
|
|
|
all_people = [person for person in self.sdb.all_people()]
|
|
|
|
if len(all_people) > 100:
|
|
|
|
self.progress.set_pass(_('Matching people...'), len(all_people)/50)
|
|
|
|
count = 0
|
|
|
|
for person in all_people:
|
|
|
|
if len(all_people) > 100:
|
|
|
|
if count % 50 == 0:
|
|
|
|
self.progress.step()
|
|
|
|
count += 1
|
|
|
|
row = []
|
|
|
|
sorts = []
|
|
|
|
env = {_("Date"): gen.lib.date.Date} # env for py.eval
|
|
|
|
for col_name in self.columns:
|
|
|
|
col = cleanup_column_name(col_name)
|
|
|
|
if col == "name":
|
|
|
|
env[col_name] = str(person)
|
|
|
|
row.append(person)
|
|
|
|
elif col == "firstname":
|
|
|
|
env[col_name] = person.get_primary_name().get_first_name()
|
|
|
|
row.append(env[col_name])
|
|
|
|
elif col == "surname":
|
|
|
|
env[col_name] = person.get_primary_name().get_surname()
|
|
|
|
row.append(env[col_name])
|
|
|
|
elif col == "suffix":
|
|
|
|
env[col_name] = person.get_primary_name().get_suffix()
|
|
|
|
row.append(env[col_name])
|
|
|
|
elif col == "title":
|
|
|
|
env[col_name] = person.get_primary_name().get_title()
|
|
|
|
row.append(env[col_name])
|
|
|
|
elif col == "birthdate":
|
|
|
|
env[col_name] = self.sdb.birth_date_obj(person)
|
|
|
|
row.append(env[col_name])
|
|
|
|
elif col == "deathdate":
|
|
|
|
env[col_name] = self.sdb.death_date_obj(person)
|
|
|
|
row.append(env[col_name])
|
|
|
|
elif col == "gender":
|
|
|
|
env[col_name] = self.sdb.gender(person)
|
|
|
|
row.append(env[col_name])
|
|
|
|
elif col == "birthplace":
|
|
|
|
env[col_name] = self.sdb.birth_place(person)
|
|
|
|
row.append(env[col_name])
|
|
|
|
elif col == "deathplace":
|
|
|
|
env[col_name] = self.sdb.death_place(person)
|
|
|
|
row.append(env[col_name])
|
|
|
|
elif col == "change":
|
|
|
|
env[col_name] = person.get_change_display()
|
|
|
|
row.append(env[col_name])
|
|
|
|
elif col == "marker":
|
|
|
|
env[col_name] = person.marker.string
|
|
|
|
row.append(env[col_name])
|
|
|
|
elif col == "private":
|
|
|
|
env[col_name] = {True: "private", False: ""}[person.private]
|
|
|
|
row.append(env[col_name])
|
|
|
|
elif col == "grampsid":
|
|
|
|
env[col_name] = person.gramps_id
|
|
|
|
row.append(env[col_name])
|
|
|
|
else:
|
|
|
|
raise AttributeError("unknown column: '%s'" % col_name)
|
|
|
|
#sorts.append((len(row)-1,sortval))
|
|
|
|
# should we add it?:
|
|
|
|
if self.where:
|
|
|
|
try:
|
|
|
|
result = eval(self.where, env)
|
|
|
|
except:
|
|
|
|
raise AttributeError("malformed where clause: '%s'" % self.where)
|
|
|
|
result = False
|
|
|
|
else:
|
|
|
|
result = True
|
|
|
|
if result:
|
|
|
|
self.select += 1
|
2008-04-18 09:06:38 +05:30
|
|
|
if self.command == "select":
|
|
|
|
self.stab.row(*row)
|
|
|
|
for (col, value) in sorts:
|
|
|
|
self.stab.row_sort_val(col, value)
|
|
|
|
elif self.command == "delete":
|
|
|
|
self.database.active = person
|
|
|
|
trans = self.database.transaction_begin()
|
|
|
|
active_name = _("Delete Person (%s)") % self.sdb.name(person)
|
|
|
|
gen.utils.delete_person_from_database(self.database, person, trans)
|
|
|
|
# FIXME: delete familes, events, notes, resources, etc, if possible
|
|
|
|
self.database.transaction_commit(trans, active_name)
|
|
|
|
else:
|
|
|
|
raise AttributeError("unknown command: '%s'", self.command)
|
2008-04-18 06:40:50 +05:30
|
|
|
|
|
|
|
def run(database, document, query):
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
retval = ""
|
|
|
|
dbi = DBI(database, document)
|
|
|
|
try:
|
|
|
|
q = dbi.parse(query)
|
|
|
|
except AttributeError, msg:
|
|
|
|
return msg
|
|
|
|
try:
|
|
|
|
retval = dbi.eval()
|
|
|
|
except AttributeError, msg:
|
|
|
|
# dialog?
|
|
|
|
retval = msg
|
|
|
|
dbi.close()
|
|
|
|
return retval
|
|
|
|
|
|
|
|
#------------------------------------------------------------------------
|
|
|
|
#
|
|
|
|
# Register the report
|
|
|
|
#
|
|
|
|
#------------------------------------------------------------------------
|
2008-05-19 00:54:28 +05:30
|
|
|
pmgr = PluginManager.get_instance()
|
|
|
|
pmgr.register_quick_report(
|
2008-04-18 06:40:50 +05:30
|
|
|
name = 'query',
|
|
|
|
category = CATEGORY_QR_MISC,
|
|
|
|
run_func = run,
|
|
|
|
translated_name = _("Query"),
|
|
|
|
status = _("Stable"),
|
|
|
|
description= _("Display data that matches a query"),
|
|
|
|
author_name="Douglas Blank",
|
|
|
|
author_email="dblank@cs.brynmawr.edu"
|
|
|
|
)
|
|
|
|
|