Source code for

# Gramps - a GTK+/GNOME based genealogy program
# Copyright (C) 2007  Brian G. Matherly
# Copyright (C) 2010  Jakim Friant
# Copyright (C) 2011       Tim G L Lyons
# 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
# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

Contain and organize bibliographic information.
import string
import math
from ...lib.citation import Citation as lib_Citation

[docs]class Citation(object): """ Store information about a citation and all of its references. """ def __init__(self): """ Initialize members. """ self.__src_handle = None self.__ref_list = []
[docs] def get_source_handle(self): """ Provide the handle to the source that this citation is for. :return: Source Handle :rtype: handle """ return self.__src_handle
[docs] def set_source_handle(self, handle): """ Set the handle for the source that this citation is for. :param handle: Source Handle :type handle: handle """ self.__src_handle = handle
[docs] def get_ref_list(self): """ List all the references to this citation. :return: a list of references :rtype: list of :class:`~.citation.Citation` objects """ return self.__ref_list
[docs] def add_reference(self, source_ref): """ Add a reference to this citation. If a similar reference exists, don't add another one. :param source_ref: Source Reference :type source_ref: :class:`~.citation.Citation` :return: The key of the added reference among all the references. :rtype: char """ letter_count = len(string.ascii_lowercase) ref_count = len(self.__ref_list) x_ref_count = ref_count # Return "a" for ref_count = 0, otherwise log(0) does not work if ref_count == 0: self.__ref_list.append(("a", source_ref)) return "a" last_letter = string.ascii_lowercase[ ref_count % letter_count ] key = "" # Calculate prek number of digits. number_of_letters = int(math.log(float(ref_count), float(letter_count)))+1 # Exclude index for number_of_letters-1 for n in range(1, number_of_letters-1): ref_count -= pow(letter_count, n) # Adjust number_of_letters for new index number_of_letters = int(math.log(float(ref_count), float(letter_count))) +1 for n in range(1, number_of_letters): x_ref_count -= pow(letter_count, n) for letter in range(1, number_of_letters): index = x_ref_count / pow(letter_count, letter) % letter_count key += string.ascii_lowercase[ index ] key = key + last_letter self.__ref_list.append((key, source_ref)) return key
[docs]class Bibliography(object): """ Store and organize multiple citations into a bibliography. """ MODE_DATE = 2**0 MODE_PAGE = 2**1 MODE_CONF = 2**2 MODE_NOTE = 2**3 MODE_MEDIA = 2**4 MODE_ALL = MODE_DATE | MODE_PAGE | MODE_CONF | MODE_NOTE | MODE_MEDIA def __init__(self, mode=MODE_ALL): """ A bibliography will store citations (sources) and references to those citations (citations). Duplicate entries will not be added. To change what is considered duplicate, you can tell the bibliography what source ref information you are interested in by passing in the mode. Possible modes include: - MODE_DATE - MODE_PAGE - MODE_CONF - MODE_NOTE - MODE_MEDIA - MODE_ALL If you only care about pages, set "mode=MODE_PAGE". If you only care about dates and pages, set "mode=MODE_DATE|MODE_PAGE". If you care about everything, set "mode=MODE_ALL". """ self.__citation_list = [] self.mode = mode
[docs] def add_reference(self, lib_citation): """ Add a reference to a source to this bibliography. If the source already exists, don't add it again. If a similar reference exists, don't add another one. :param citation: Citation object :type citation: :class:`~.citation.Citation` :return: A tuple containing the index of the source among all the sources and the key of the reference among all the references. If there is no reference information, the second element will be None. :rtype: (int,char) or (int,None) .. note:: Within this file, the name 'citation' is used both for gen.lib.Citation, and for _bibliography.Citation. It is not clear how best to rename the concepts in this file to avoid the clash, so the names have been retained. In this function, lib_citation is used for gen.lib.Citation instances, and citation for _bibliography.Citation instances. Elsewhere in this file, source_ref is used for gen.lib.Citation instances. """ source_handle = lib_citation.get_reference_handle() cindex = 0 rkey = "" citation = None citation_found = False for citation in self.__citation_list: if citation.get_source_handle() == source_handle: citation_found = True break cindex += 1 if not citation_found: citation = Citation() citation.set_source_handle(source_handle) cindex = len(self.__citation_list) self.__citation_list.append(citation) if self.__sref_has_info(lib_citation): for key, ref in citation.get_ref_list(): if self.__srefs_are_equal(ref, lib_citation): # if a reference like this already exists, don't add # another one return (cindex, key) rkey = citation.add_reference(lib_citation) return (cindex, rkey)
[docs] def get_citation_count(self): """ Report the number of citations in this bibliography. :return: number of citations :rtype: int """ return len(self.__citation_list)
[docs] def get_citation_list(self): """ Return a list containing all the citations in this bibliography. :return: citation list :rtype: list of :class:`Citation` objects """ return self.__citation_list
def __sref_has_info(self, source_ref): """ Determine if this source_ref has any useful information based on the current mode. """ if ( self.mode & self.MODE_PAGE ) == self.MODE_PAGE: if source_ref.get_page() != "": return True if ( self.mode & self.MODE_DATE ) == self.MODE_DATE: date = source_ref.get_date_object() if date is not None and not date.is_empty(): return True if ( self.mode & self.MODE_CONF ) == self.MODE_CONF: confidence = source_ref.get_confidence_level() if confidence is not None and confidence != \ lib_Citation.CONF_NORMAL: return True if ( self.mode & self.MODE_NOTE ) == self.MODE_NOTE: if len(source_ref.get_note_list()) != 0: return True if ( self.mode & self.MODE_MEDIA ) == self.MODE_MEDIA: if len(source_ref.get_media_list()) != 0: return True # Can't find anything interesting. return False def __srefs_are_equal(self, source_ref1, source_ref2): """ Determine if two source references are equal based on the current mode. """ # The criterion for equality (in mode==MODE_ALL) is changed for # citations. Previously, it was based on is_equal from SecondaryObject, # which does a 'cmp' on the serialised data. (Note that this might not # have worked properly for Dates; see comments in Date.is_equal and # EditCitation.data_has_changed). The comparison is now made as to # whether the two gen.lib.Citations have the same handle (i.e. they are # actually the same database objects). It is felt that this better # reflects the intent of Citation objects, which can be merged if they # are intended to represent the same citation. if self.mode == self.MODE_ALL: return source_ref1.handle == source_ref2.handle if ( self.mode & self.MODE_PAGE ) == self.MODE_PAGE: if source_ref1.get_page() != source_ref2.get_page(): return False if ( self.mode & self.MODE_DATE ) == self.MODE_DATE: date1 = source_ref1.get_date_object() date2 = source_ref2.get_date_object() if not date1.is_equal(date2): return False if ( self.mode & self.MODE_CONF ) == self.MODE_CONF: conf1 = source_ref1.get_confidence_level() conf2 = source_ref2.get_confidence_level() if conf1 != conf2: return False if ( self.mode & self.MODE_NOTE ) == self.MODE_NOTE: nl1 = source_ref1.get_note_list() nl2 = source_ref2.get_note_list() if len(nl1) != len(nl2): return False for notehandle in nl1: if notehandle not in nl2: return False if ( self.mode & self.MODE_MEDIA ) == self.MODE_MEDIA: nl1 = source_ref1.get_media_list() nl2 = source_ref2.get_media_list() if len(nl1) != len(nl2): return False for mediahandle in nl1: if mediahandle not in nl2: return False # Can't find anything different. They must be equal. return True