diff --git a/gramps/gen/db/base.py b/gramps/gen/db/base.py index 12e44233e..f3a91453d 100644 --- a/gramps/gen/db/base.py +++ b/gramps/gen/db/base.py @@ -605,12 +605,15 @@ class DbReadBase: """ raise NotImplementedError - def get_citation_handles(self, sort_handles=False): + def get_citation_handles(self, sort_handles=False, locale=glocale): """ Return a list of database handles, one handle for each Citation in the database. - If sort_handles is True, the list is sorted by Citation title. + :param sort_handles: If True, the list is sorted by Citation title. + :type sort_handles: bool + :param locale: The locale to use for collation. + :type locale: A GrampsLocale object. """ raise NotImplementedError @@ -624,24 +627,30 @@ class DbReadBase: """ raise NotImplementedError - def get_family_handles(self, sort_handles=False): + def get_family_handles(self, sort_handles=False, locale=glocale): """ Return a list of database handles, one handle for each Family in the database. - If sort_handles is True, the list is sorted by surnames. + :param sort_handles: If True, the list is sorted by surnames. + :type sort_handles: bool + :param locale: The locale to use for collation. + :type locale: A GrampsLocale object. .. warning:: For speed the keys are directly returned, so handles are bytes type """ raise NotImplementedError - def get_media_handles(self, sort_handles=False): + def get_media_handles(self, sort_handles=False, locale=glocale): """ Return a list of database handles, one handle for each Media in the database. - If sort_handles is True, the list is sorted by title. + :param sort_handles: If True, the list is sorted by title. + :type sort_handles: bool + :param locale: The locale to use for collation. + :type locale: A GrampsLocale object. .. warning:: For speed the keys are directly returned, so handles are bytes type @@ -658,24 +667,30 @@ class DbReadBase: """ raise NotImplementedError - def get_person_handles(self, sort_handles=False): + def get_person_handles(self, sort_handles=False, locale=glocale): """ Return a list of database handles, one handle for each Person in the database. - If sort_handles is True, the list is sorted by surnames. + :param sort_handles: If True, the list is sorted by surnames. + :type sort_handles: bool + :param locale: The locale to use for collation. + :type locale: A GrampsLocale object. .. warning:: For speed the keys are directly returned, so handles are bytes type """ raise NotImplementedError - def get_place_handles(self, sort_handles=False): + def get_place_handles(self, sort_handles=False, locale=glocale): """ Return a list of database handles, one handle for each Place in the database. - If sort_handles is True, the list is sorted by Place title. + :param sort_handles: If True, the list is sorted by Place title. + :type sort_handles: bool + :param locale: The locale to use for collation. + :type locale: A GrampsLocale object. .. warning:: For speed the keys are directly returned, so handles are bytes type @@ -692,24 +707,30 @@ class DbReadBase: """ raise NotImplementedError - def get_source_handles(self, sort_handles=False): + def get_source_handles(self, sort_handles=False, locale=glocale): """ Return a list of database handles, one handle for each Source in the database. - If sort_handles is True, the list is sorted by Source title. + :param sort_handles: If True, the list is sorted by Source title. + :type sort_handles: bool + :param locale: The locale to use for collation. + :type locale: A GrampsLocale object. .. warning:: For speed the keys are directly returned, so handles are bytes type """ raise NotImplementedError - def get_tag_handles(self, sort_handles=False): + def get_tag_handles(self, sort_handles=False, locale=glocale): """ Return a list of database handles, one handle for each Tag in the database. - If sort_handles is True, the list is sorted by Tag name. + :param sort_handles: If True, the list is sorted by Tag name. + :type sort_handles: bool + :param locale: The locale to use for collation. + :type locale: A GrampsLocale object. .. warning:: For speed the keys are directly returned, so handles are bytes type diff --git a/gramps/gen/db/dummydb.py b/gramps/gen/db/dummydb.py index ebdf0c37e..93f408a98 100644 --- a/gramps/gen/db/dummydb.py +++ b/gramps/gen/db/dummydb.py @@ -68,6 +68,7 @@ from .dbconst import DBLOGNAME from ..errors import HandleError from ..utils.callback import Callback from ..lib import Researcher +from ..const import GRAMPS_LOCALE as glocale LOG = logging.getLogger(DBLOGNAME) @@ -531,12 +532,15 @@ class DummyDb(M_A_M_B("NewBaseClass", (DbReadBase, Callback, object,), {})): LOG.warning("handle %s does not exist in the dummy database", handle) raise HandleError('Handle %s not found' % handle) - def get_family_handles(self, sort_handles=False): + def get_family_handles(self, sort_handles=False, locale=glocale): """ Return a list of database handles, one handle for each Family in the database. - If sort_handles is True, the list is sorted by surnames. + :param sort_handles: If True, the list is sorted by surnames. + :type sort_handles: bool + :param locale: The locale to use for collation. + :type locale: A GrampsLocale object. """ if not self.db_is_open: LOG.warning("database is closed") @@ -584,12 +588,15 @@ class DummyDb(M_A_M_B("NewBaseClass", (DbReadBase, Callback, object,), {})): LOG.warning("database is closed") return [] - def get_media_handles(self, sort_handles=False): + def get_media_handles(self, sort_handles=False, locale=glocale): """ Return a list of database handles, one handle for each Media in the database. - If sort_handles is True, the list is sorted by title. + :param sort_handles: If True, the list is sorted by title. + :type sort_handles: bool + :param locale: The locale to use for collation. + :type locale: A GrampsLocale object. """ if not self.db_is_open: LOG.warning("database is closed") @@ -834,12 +841,15 @@ class DummyDb(M_A_M_B("NewBaseClass", (DbReadBase, Callback, object,), {})): LOG.warning("handle %s does not exist in the dummy database", handle) raise HandleError('Handle %s not found' % handle) - def get_person_handles(self, sort_handles=False): + def get_person_handles(self, sort_handles=False, locale=glocale): """ Return a list of database handles, one handle for each Person in the database. - If sort_handles is True, the list is sorted by surnames. + :param sort_handles: If True, the list is sorted by surnames. + :type sort_handles: bool + :param locale: The locale to use for collation. + :type locale: A GrampsLocale object. """ if not self.db_is_open: LOG.warning("database is closed") @@ -892,12 +902,15 @@ class DummyDb(M_A_M_B("NewBaseClass", (DbReadBase, Callback, object,), {})): LOG.warning("handle %s does not exist in the dummy database", handle) raise HandleError('Handle %s not found' % handle) - def get_place_handles(self, sort_handles=False): + def get_place_handles(self, sort_handles=False, locale=glocale): """ Return a list of database handles, one handle for each Place in the database. - If sort_handles is True, the list is sorted by Place title. + :param sort_handles: If True, the list is sorted by Place title. + :type sort_handles: bool + :param locale: The locale to use for collation. + :type locale: A GrampsLocale object. """ if not self.db_is_open: LOG.warning("database is closed") @@ -1102,12 +1115,15 @@ class DummyDb(M_A_M_B("NewBaseClass", (DbReadBase, Callback, object,), {})): LOG.warning("handle %s does not exist in the dummy database", handle) raise HandleError('Handle %s not found' % handle) - def get_source_handles(self, sort_handles=False): + def get_source_handles(self, sort_handles=False, locale=glocale): """ Return a list of database handles, one handle for each Source in the database. - If sort_handles is True, the list is sorted by Source title. + :param sort_handles: If True, the list is sorted by Source title. + :type sort_handles: bool + :param locale: The locale to use for collation. + :type locale: A GrampsLocale object. """ if not self.db_is_open: LOG.warning("database is closed") @@ -1160,12 +1176,15 @@ class DummyDb(M_A_M_B("NewBaseClass", (DbReadBase, Callback, object,), {})): LOG.warning("handle %s does not exist in the dummy database", handle) raise HandleError('Handle %s not found' % handle) - def get_citation_handles(self, sort_handles=False): + def get_citation_handles(self, sort_handles=False, locale=glocale): """ Return a list of database handles, one handle for each Citation in the database. - If sort_handles is True, the list is sorted by Citation title. + :param sort_handles: If True, the list is sorted by Citation title. + :type sort_handles: bool + :param locale: The locale to use for collation. + :type locale: A GrampsLocale object. """ if not self.db_is_open: LOG.warning("database is closed") @@ -1209,12 +1228,15 @@ class DummyDb(M_A_M_B("NewBaseClass", (DbReadBase, Callback, object,), {})): LOG.warning("tag name %s does not exist in the dummy database", val) return None - def get_tag_handles(self, sort_handles=False): + def get_tag_handles(self, sort_handles=False, locale=glocale): """ Return a list of database handles, one handle for each Tag in the database. - If sort_handles is True, the list is sorted by Tag name. + :param sort_handles: If True, the list is sorted by Tag name. + :type sort_handles: bool + :param locale: The locale to use for collation. + :type locale: A GrampsLocale object. """ if not self.db_is_open: LOG.warning("database is closed") diff --git a/gramps/gen/proxy/filter.py b/gramps/gen/proxy/filter.py index b3a35a456..2b10a71a8 100644 --- a/gramps/gen/proxy/filter.py +++ b/gramps/gen/proxy/filter.py @@ -33,6 +33,7 @@ Proxy class for the Gramps databases. Apply filter from .proxybase import ProxyDbBase from ..lib import (Date, Person, Name, Surname, NameOriginType, Family, Source, Citation, Event, Media, Place, Repository, Note, Tag) +from ..const import GRAMPS_LOCALE as glocale class FilterProxyDb(ProxyDbBase): """ @@ -385,10 +386,15 @@ class FilterProxyDb(ProxyDbBase): else: return None - def get_person_handles(self, sort_handles=False): + def get_person_handles(self, sort_handles=False, locale=glocale): """ Return a list of database handles, one handle for each Person in - the database. If sort_handles is True, the list is sorted by surnames + the database. + + :param sort_handles: If True, the list is sorted by surnames. + :type sort_handles: bool + :param locale: The locale to use for collation. + :type locale: A GrampsLocale object. """ # FIXME: plist is not a sorted list of handles return list(self.plist) @@ -426,10 +432,15 @@ class FilterProxyDb(ProxyDbBase): """ return map(self.get_event_from_handle, self.elist) - def get_family_handles(self, sort_handles=False): + def get_family_handles(self, sort_handles=False, locale=glocale): """ Return a list of database handles, one handle for each Family in - the database. If sort_handles is True, the list is sorted by surnames + the database. + + :param sort_handles: If True, the list is sorted by surnames. + :type sort_handles: bool + :param locale: The locale to use for collation. + :type locale: A GrampsLocale object. """ # FIXME: flist is not a sorted list of handles return list(self.flist) diff --git a/gramps/gen/proxy/proxybase.py b/gramps/gen/proxy/proxybase.py index a2d3009f1..362a8a1b6 100644 --- a/gramps/gen/proxy/proxybase.py +++ b/gramps/gen/proxy/proxybase.py @@ -38,6 +38,7 @@ import types from ..db.base import DbReadBase, DbWriteBase from ..lib import (Citation, Event, Family, Media, Note, Person, Place, Repository, Source, Tag) +from ..const import GRAMPS_LOCALE as glocale class ProxyCursor: """ @@ -199,26 +200,28 @@ class ProxyDbBase(DbReadBase): return ProxyCursor(self.get_raw_tag_data, self.get_tag_handles) - def get_person_handles(self, sort_handles=False): + def get_person_handles(self, sort_handles=False, locale=glocale): """ Return a list of database handles, one handle for each Person in the database. If sort_handles is True, the list is sorted by surnames """ if (self.db is not None) and self.db.is_open(): proxied = set(self.iter_person_handles()) - all = self.basedb.get_person_handles(sort_handles=sort_handles) + all = self.basedb.get_person_handles(sort_handles=sort_handles, + locale=locale) return [hdl for hdl in all if hdl in proxied] else: return [] - def get_family_handles(self, sort_handles=False): + def get_family_handles(self, sort_handles=False, locale=glocale): """ Return a list of database handles, one handle for each Family in the database. If sort_handles is True, the list is sorted by surnames """ if (self.db is not None) and self.db.is_open(): proxied = set(self.iter_family_handles()) - all = self.basedb.get_family_handles(sort_handles=sort_handles) + all = self.basedb.get_family_handles(sort_handles=sort_handles, + locale=locale) return [hdl for hdl in all if hdl in proxied] else: return [] @@ -233,7 +236,7 @@ class ProxyDbBase(DbReadBase): else: return [] - def get_source_handles(self, sort_handles=False): + def get_source_handles(self, sort_handles=False, locale=glocale): """ Return a list of database handles, one handle for each Source in the database. @@ -243,7 +246,7 @@ class ProxyDbBase(DbReadBase): else: return [] - def get_citation_handles(self, sort_handles=False): + def get_citation_handles(self, sort_handles=False, locale=glocale): """ Return a list of database handles, one handle for each Citation in the database. @@ -253,7 +256,7 @@ class ProxyDbBase(DbReadBase): else: return [] - def get_place_handles(self, sort_handles=False): + def get_place_handles(self, sort_handles=False, locale=glocale): """ Return a list of database handles, one handle for each Place in the database. @@ -263,7 +266,7 @@ class ProxyDbBase(DbReadBase): else: return [] - def get_media_handles(self, sort_handles=False): + def get_media_handles(self, sort_handles=False, locale=glocale): """ Return a list of database handles, one handle for each Media in the database. @@ -293,7 +296,7 @@ class ProxyDbBase(DbReadBase): else: return [] - def get_tag_handles(self, sort_handles=False): + def get_tag_handles(self, sort_handles=False, locale=glocale): """ Return a list of database handles, one handle for each Tag in the database. diff --git a/gramps/gen/utils/grampslocale.py b/gramps/gen/utils/grampslocale.py index ecc00f6a0..4741ef70d 100644 --- a/gramps/gen/utils/grampslocale.py +++ b/gramps/gen/utils/grampslocale.py @@ -935,6 +935,12 @@ class GrampsLocale: return string return key + def get_collation(self): + """ + Return the collation without any character encoding. + """ + return self.collation.split('.')[0] + def strcoll(self, string1, string2): """ Given two localized strings, compare them and return -1 if diff --git a/gramps/plugins/db/bsddb/read.py b/gramps/plugins/db/bsddb/read.py index 61d5b43f4..9e4beb255 100644 --- a/gramps/plugins/db/bsddb/read.py +++ b/gramps/plugins/db/bsddb/read.py @@ -36,6 +36,7 @@ import os from sys import maxsize from operator import itemgetter import ast +from functools import partial try: from bsddb3 import db @@ -1024,7 +1025,7 @@ class DbBsddbRead(DbReadBase, Callback): """ return [key.decode('utf-8') for key in table.keys(txn=self.txn)] - def get_person_handles(self, sort_handles=False): + def get_person_handles(self, sort_handles=False, locale=glocale): """ Return a list of database handles, one handle for each Person in the database. @@ -1034,11 +1035,12 @@ class DbBsddbRead(DbReadBase, Callback): if self.db_is_open: handle_list = self._all_handles(self.person_map) if sort_handles: - handle_list.sort(key=self.__sortbyperson_key) + handle_list.sort(key=partial(self.__sortbyperson_key, + locale=locale)) return handle_list return [] - def get_place_handles(self, sort_handles=False): + def get_place_handles(self, sort_handles=False, locale=glocale): """ Return a list of database handles, one handle for each Place in the database. @@ -1049,11 +1051,12 @@ class DbBsddbRead(DbReadBase, Callback): if self.db_is_open: handle_list = self._all_handles(self.place_map) if sort_handles: - handle_list.sort(key=self.__sortbyplace_key) + handle_list.sort(key=partial(self.__sortbyplace_key, + locale=locale)) return handle_list return [] - def get_source_handles(self, sort_handles=False): + def get_source_handles(self, sort_handles=False, locale=glocale): """ Return a list of database handles, one handle for each Source in the database. @@ -1063,11 +1066,12 @@ class DbBsddbRead(DbReadBase, Callback): if self.db_is_open: handle_list = self._all_handles(self.source_map) if sort_handles: - handle_list.sort(key=self.__sortbysource_key) + handle_list.sort(key=partial(self.__sortbysource_key, + locale=locale)) return handle_list return [] - def get_citation_handles(self, sort_handles=False): + def get_citation_handles(self, sort_handles=False, locale=glocale): """ Return a list of database handles, one handle for each Citation in the database. @@ -1077,11 +1081,12 @@ class DbBsddbRead(DbReadBase, Callback): if self.db_is_open: handle_list = self._all_handles(self.citation_map) if sort_handles: - handle_list.sort(key=self.__sortbycitation_key) + handle_list.sort(key=partial(self.__sortbycitation_key, + locale=locale)) return handle_list return [] - def get_media_handles(self, sort_handles=False): + def get_media_handles(self, sort_handles=False, locale=glocale): """ Return a list of database handles, one handle for each Media in the database. @@ -1091,7 +1096,8 @@ class DbBsddbRead(DbReadBase, Callback): if self.db_is_open: handle_list = self._all_handles(self.media_map) if sort_handles: - handle_list.sort(key=self.__sortbymedia_key) + handle_list.sort(key=partial(self.__sortbymedia_key, + locale=locale)) return handle_list return [] @@ -1104,7 +1110,7 @@ class DbBsddbRead(DbReadBase, Callback): return self._all_handles(self.event_map) return [] - def get_family_handles(self, sort_handles=False): + def get_family_handles(self, sort_handles=False, locale=glocale): """ Return a list of database handles, one handle for each Family in the database. @@ -1114,7 +1120,8 @@ class DbBsddbRead(DbReadBase, Callback): if self.db_is_open: handle_list = self._all_handles(self.family_map) if sort_handles: - handle_list.sort(key=self.__sortbyfamily_key) + handle_list.sort(key=partial(self.__sortbyfamily_key, + locale=locale)) return handle_list return [] @@ -1136,7 +1143,7 @@ class DbBsddbRead(DbReadBase, Callback): return self._all_handles(self.note_map) return [] - def get_tag_handles(self, sort_handles=False): + def get_tag_handles(self, sort_handles=False, locale=glocale): """ Return a list of database handles, one handle for each Tag in the database. @@ -1146,7 +1153,8 @@ class DbBsddbRead(DbReadBase, Callback): if self.db_is_open: handle_list = self._all_handles(self.tag_map) if sort_handles: - handle_list.sort(key=self.__sortbytag_key) + handle_list.sort(key=partial(self.__sortbytag_key, + locale=locale)) return handle_list return [] @@ -1774,24 +1782,24 @@ class DbBsddbRead(DbReadBase, Callback): """ return self.__has_gramps_id(self.cid_trans, gramps_id) - def __sortbyperson_key(self, handle): + def __sortbyperson_key(self, handle, locale=glocale): handle = handle.encode('utf-8') - return glocale.sort_key(find_fullname(handle, - self.person_map.get(handle))) + return locale.sort_key(find_fullname(handle, + self.person_map.get(handle))) - def __sortbyfamily_key(self, handle): + def __sortbyfamily_key(self, handle, locale=glocale): handle = handle.encode('utf-8') data = self.family_map.get(handle) data2 = data[2] data3 = data[3] if data2: # father handle data2 = data2.encode('utf-8') - return glocale.sort_key(find_fullname(data2, - self.person_map.get(data2))) + return locale.sort_key(find_fullname(data2, + self.person_map.get(data2))) elif data3: # mother handle data3 = data3.encode('utf-8') - return glocale.sort_key(find_fullname(data3, - self.person_map.get(data3))) + return locale.sort_key(find_fullname(data3, + self.person_map.get(data3))) return '' def __sortbyplace(self, first, second): @@ -1800,9 +1808,9 @@ class DbBsddbRead(DbReadBase, Callback): return glocale.strcoll(self.place_map.get(first)[2], self.place_map.get(second)[2]) - def __sortbyplace_key(self, place): + def __sortbyplace_key(self, place, locale=glocale): place = place.encode('utf-8') - return glocale.sort_key(self.place_map.get(place)[2]) + return locale.sort_key(self.place_map.get(place)[2]) def __sortbysource(self, first, second): first = first.encode('utf-8') @@ -1811,10 +1819,10 @@ class DbBsddbRead(DbReadBase, Callback): source2 = str(self.source_map[second][2]) return glocale.strcoll(source1, source2) - def __sortbysource_key(self, key): + def __sortbysource_key(self, key, locale=glocale): key = key.encode('utf-8') source = str(self.source_map[key][2]) - return glocale.sort_key(source) + return locale.sort_key(source) def __sortbycitation(self, first, second): first = first.encode('utf-8') @@ -1823,10 +1831,10 @@ class DbBsddbRead(DbReadBase, Callback): citation2 = str(self.citation_map[second][3]) return glocale.strcoll(citation1, citation2) - def __sortbycitation_key(self, key): + def __sortbycitation_key(self, key, locale=glocale): key = key.encode('utf-8') citation = str(self.citation_map[key][3]) - return glocale.sort_key(citation) + return locale.sort_key(citation) def __sortbymedia(self, first, second): first = first.encode('utf-8') @@ -1835,10 +1843,10 @@ class DbBsddbRead(DbReadBase, Callback): media2 = self.media_map[second][4] return glocale.strcoll(media1, media2) - def __sortbymedia_key(self, key): + def __sortbymedia_key(self, key, locale=glocale): key = key.encode('utf-8') media = self.media_map[key][4] - return glocale.sort_key(media) + return locale.sort_key(media) def __sortbytag(self, first, second): first = first.encode('utf-8') @@ -1847,10 +1855,10 @@ class DbBsddbRead(DbReadBase, Callback): tag2 = self.tag_map[second][1] return glocale.strcoll(tag1, tag2) - def __sortbytag_key(self, key): + def __sortbytag_key(self, key, locale=glocale): key = key.encode('utf-8') tag = self.tag_map[key][1] - return glocale.sort_key(tag) + return locale.sort_key(tag) def set_mediapath(self, path): """Set the default media path for database.""" diff --git a/gramps/plugins/db/dbapi/dbapi.py b/gramps/plugins/db/dbapi/dbapi.py index 56f9e9715..d7a0b0799 100644 --- a/gramps/plugins/db/dbapi/dbapi.py +++ b/gramps/plugins/db/dbapi/dbapi.py @@ -401,44 +401,57 @@ class DBAPI(DbGeneric): else: return key - def get_person_handles(self, sort_handles=False): + def get_person_handles(self, sort_handles=False, locale=glocale): """ Return a list of database handles, one handle for each Person in the database. - If sort_handles is True, the list is sorted by surnames. + :param sort_handles: If True, the list is sorted by surnames. + :type sort_handles: bool + :param locale: The locale to use for collation. + :type locale: A GrampsLocale object. """ if sort_handles: - self.dbapi.execute("SELECT handle FROM person " - "ORDER BY surname COLLATE glocale") + if locale != glocale: + self.dbapi.check_collation(locale) + + self.dbapi.execute('SELECT handle FROM person ' + 'ORDER BY surname ' + 'COLLATE "%s"' % locale.get_collation()) else: self.dbapi.execute("SELECT handle FROM person") rows = self.dbapi.fetchall() return [row[0] for row in rows] - def get_family_handles(self, sort_handles=False): + def get_family_handles(self, sort_handles=False, locale=glocale): """ Return a list of database handles, one handle for each Family in the database. - If sort_handles is True, the list is sorted by surnames. + :param sort_handles: If True, the list is sorted by surnames. + :type sort_handles: bool + :param locale: The locale to use for collation. + :type locale: A GrampsLocale object. """ if sort_handles: - sql = ("SELECT family.handle " + - "FROM family " + - "LEFT JOIN person AS father " + - "ON family.father_handle = father.handle " + - "LEFT JOIN person AS mother " + - "ON family.mother_handle = mother.handle " + - "ORDER BY (CASE WHEN father.handle IS NULL " + - "THEN mother.surname " + - "ELSE father.surname " + - "END), " + - "(CASE WHEN family.handle IS NULL " + - "THEN mother.given_name " + - "ELSE father.given_name " + - "END) " + - "COLLATE glocale") + if locale != glocale: + self.dbapi.check_collation(locale) + + sql = ('SELECT family.handle ' + + 'FROM family ' + + 'LEFT JOIN person AS father ' + + 'ON family.father_handle = father.handle ' + + 'LEFT JOIN person AS mother ' + + 'ON family.mother_handle = mother.handle ' + + 'ORDER BY (CASE WHEN father.handle IS NULL ' + + 'THEN mother.surname ' + + 'ELSE father.surname ' + + 'END), ' + + '(CASE WHEN family.handle IS NULL ' + + 'THEN mother.given_name ' + + 'ELSE father.given_name ' + + 'END) ' + + 'COLLATE "%s"' % locale.get_collation()) self.dbapi.execute(sql) else: self.dbapi.execute("SELECT handle FROM family") @@ -454,46 +467,67 @@ class DBAPI(DbGeneric): rows = self.dbapi.fetchall() return [row[0] for row in rows] - def get_citation_handles(self, sort_handles=False): + def get_citation_handles(self, sort_handles=False, locale=glocale): """ Return a list of database handles, one handle for each Citation in the database. - If sort_handles is True, the list is sorted by Citation title. + :param sort_handles: If True, the list is sorted by Citation title. + :type sort_handles: bool + :param locale: The locale to use for collation. + :type locale: A GrampsLocale object. """ if sort_handles: - self.dbapi.execute("SELECT handle FROM citation " - "ORDER BY page COLLATE glocale") + if locale != glocale: + self.dbapi.check_collation(locale) + + self.dbapi.execute('SELECT handle FROM citation ' + 'ORDER BY page ' + 'COLLATE "%s"' % locale.get_collation()) else: self.dbapi.execute("SELECT handle FROM citation") rows = self.dbapi.fetchall() return [row[0] for row in rows] - def get_source_handles(self, sort_handles=False): + def get_source_handles(self, sort_handles=False, locale=glocale): """ Return a list of database handles, one handle for each Source in the database. - If sort_handles is True, the list is sorted by Source title. + :param sort_handles: If True, the list is sorted by Source title. + :type sort_handles: bool + :param locale: The locale to use for collation. + :type locale: A GrampsLocale object. """ if sort_handles: - self.dbapi.execute("SELECT handle FROM source " - "ORDER BY title COLLATE glocale") + if locale != glocale: + self.dbapi.check_collation(locale) + + self.dbapi.execute('SELECT handle FROM source ' + 'ORDER BY title ' + 'COLLATE "%s"' % locale.get_collation()) else: self.dbapi.execute("SELECT handle from source") rows = self.dbapi.fetchall() return [row[0] for row in rows] - def get_place_handles(self, sort_handles=False): + def get_place_handles(self, sort_handles=False, locale=glocale): """ Return a list of database handles, one handle for each Place in the database. - If sort_handles is True, the list is sorted by Place title. + :param sort_handles: If True, the list is sorted by Place title. + :type sort_handles: bool + :param locale: The locale to use for collation. + :type locale: A GrampsLocale object. """ if sort_handles: - self.dbapi.execute("SELECT handle FROM place " - "ORDER BY title COLLATE glocale") + if locale != glocale: + self.dbapi.check_collation(locale) + + self.dbapi.execute('SELECT handle FROM place ' + 'ORDER BY title ' + 'COLLATE "%s"' % locale.get_collation()) else: self.dbapi.execute("SELECT handle FROM place") rows = self.dbapi.fetchall() @@ -508,16 +542,23 @@ class DBAPI(DbGeneric): rows = self.dbapi.fetchall() return [row[0] for row in rows] - def get_media_handles(self, sort_handles=False): + def get_media_handles(self, sort_handles=False, locale=glocale): """ Return a list of database handles, one handle for each Media in the database. - If sort_handles is True, the list is sorted by title. + :param sort_handles: If True, the list is sorted by title. + :type sort_handles: bool + :param locale: The locale to use for collation. + :type locale: A GrampsLocale object. """ if sort_handles: - self.dbapi.execute("SELECT handle FROM media " - "ORDER BY desc COLLATE glocale") + if locale != glocale: + self.dbapi.check_collation(locale) + + self.dbapi.execute('SELECT handle FROM media ' + 'ORDER BY desc ' + 'COLLATE "%s"' % locale.get_collation()) else: self.dbapi.execute("SELECT handle FROM media") rows = self.dbapi.fetchall() @@ -532,16 +573,23 @@ class DBAPI(DbGeneric): rows = self.dbapi.fetchall() return [row[0] for row in rows] - def get_tag_handles(self, sort_handles=False): + def get_tag_handles(self, sort_handles=False, locale=glocale): """ Return a list of database handles, one handle for each Tag in the database. - If sort_handles is True, the list is sorted by Tag name. + :param sort_handles: If True, the list is sorted by Tag name. + :type sort_handles: bool + :param locale: The locale to use for collation. + :type locale: A GrampsLocale object. """ if sort_handles: - self.dbapi.execute("SELECT handle FROM tag " - "ORDER BY name COLLATE glocale") + if locale != glocale: + self.dbapi.check_collation(locale) + + self.dbapi.execute('SELECT handle FROM tag ' + 'ORDER BY name ' + 'COLLATE "%s"' % locale.get_collation()) else: self.dbapi.execute("SELECT handle FROM tag") rows = self.dbapi.fetchall() diff --git a/gramps/plugins/db/dbapi/postgresql.py b/gramps/plugins/db/dbapi/postgresql.py index 95246e9eb..63019b13a 100644 --- a/gramps/plugins/db/dbapi/postgresql.py +++ b/gramps/plugins/db/dbapi/postgresql.py @@ -26,7 +26,6 @@ #------------------------------------------------------------------------- import psycopg2 import re -import os #------------------------------------------------------------------------- # @@ -34,6 +33,7 @@ import os # #------------------------------------------------------------------------- from gramps.gen.db.dbconst import ARRAYSIZE +from gramps.gen.const import GRAMPS_LOCALE as glocale psycopg2.paramstyle = 'format' @@ -57,9 +57,24 @@ class Postgresql: self.__connection = psycopg2.connect(*args, **kwargs) self.__connection.autocommit = True self.__cursor = self.__connection.cursor() - locale = os.environ.get('LANG', 'en_US.utf8') - self.execute("DROP COLLATION IF EXISTS glocale") - self.execute("CREATE COLLATION glocale (LOCALE = '%s')" % locale) + self.check_collation(glocale) + + def check_collation(self, locale): + """ + Checks that a collation exists and if not creates it. + + :param locale: Locale to be checked. + :param type: A GrampsLocale object. + """ + # Duplicating system collations works, but to delete them the schema + # must be specified, so get the current schema + self.execute('SELECT current_schema()') + current_schema, = self.fetchone() + collation = locale.get_collation() + self.execute('DROP COLLATION IF EXISTS "%s"."%s"' + % (current_schema, collation)) + self.execute('CREATE COLLATION "%s"' + "(LOCALE = '%s')" % (collation, locale.collation)) def _hack_query(self, query): query = query.replace("?", "%s") diff --git a/gramps/plugins/db/dbapi/sqlite.py b/gramps/plugins/db/dbapi/sqlite.py index 954301714..5a4a0e934 100644 --- a/gramps/plugins/db/dbapi/sqlite.py +++ b/gramps/plugins/db/dbapi/sqlite.py @@ -83,8 +83,20 @@ class Sqlite: self.log = logging.getLogger(".sqlite") self.__connection = sqlite3.connect(*args, **kwargs) self.__cursor = self.__connection.cursor() - self.__connection.create_collation("glocale", glocale.strcoll) self.__connection.create_function("regexp", 2, regexp) + self.__collations = [] + self.check_collation(glocale) + + def check_collation(self, locale): + """ + Checks that a collation exists and if not creates it. + + :param locale: Locale to be checked. + :param type: A GrampsLocale object. + """ + collation = locale.get_collation() + if collation not in self.__collations: + self.__connection.create_collation(collation, locale.strcoll) def execute(self, *args, **kwargs): """ diff --git a/gramps/plugins/textreport/familygroup.py b/gramps/plugins/textreport/familygroup.py index 50b7b3b1e..7455d0bf3 100644 --- a/gramps/plugins/textreport/familygroup.py +++ b/gramps/plugins/textreport/familygroup.py @@ -660,7 +660,8 @@ class FamilyGroup(Report): self.dump_family(child_family_handle, (generation+1)) def write_report(self): - flist = self.db.get_family_handles(sort_handles=True) + flist = self.db.get_family_handles(sort_handles=True, + locale=self._locale) if not self.filter: fam_list = flist else: diff --git a/gramps/plugins/textreport/indivcomplete.py b/gramps/plugins/textreport/indivcomplete.py index 5314142fd..c3b030f4f 100644 --- a/gramps/plugins/textreport/indivcomplete.py +++ b/gramps/plugins/textreport/indivcomplete.py @@ -815,7 +815,8 @@ class IndivCompleteReport(Report): def write_report(self): """ write the report """ - plist = self._db.get_person_handles(sort_handles=True) + plist = self._db.get_person_handles(sort_handles=True, + locale=self._locale) if self.filter: ind_list = self.filter.apply(self._db, plist, user=self._user) else: diff --git a/gramps/plugins/textreport/tagreport.py b/gramps/plugins/textreport/tagreport.py index 9fbc8972f..495467bf5 100644 --- a/gramps/plugins/textreport/tagreport.py +++ b/gramps/plugins/textreport/tagreport.py @@ -542,7 +542,8 @@ class TagReport(Report): def write_media(self): """ write the media associated with the tag """ - mlist = self.database.get_media_handles(sort_handles=True) + mlist = self.database.get_media_handles(sort_handles=True, + locale=self._locale) filter_class = GenericFilterFactory('Media') a_filter = filter_class() a_filter.add_rule(rules.media.HasTag([self.tag])) @@ -711,7 +712,8 @@ class TagReport(Report): def write_sources(self): """ write the sources associated with the tag """ - slist = self.database.get_source_handles(sort_handles=True) + slist = self.database.get_source_handles(sort_handles=True, + locale=self._locale) filter_class = GenericFilterFactory('Source') a_filter = filter_class() a_filter.add_rule(rules.source.HasTag([self.tag])) @@ -791,7 +793,8 @@ class TagReport(Report): def write_citations(self): """ write the citations associated with the tag """ - clist = self.database.get_citation_handles(sort_handles=True) + clist = self.database.get_citation_handles(sort_handles=True, + locale=self._locale) filter_class = GenericFilterFactory('Citation') a_filter = filter_class() a_filter.add_rule(rules.citation.HasTag([self.tag]))