예제 #1
0
    def __init__(self, sourceFolder, givenName, encoding='utf-8') -> None:
        """
        Constructor: just sets up the XML Bible file converter object.
        """
        # Setup and initialise the base class first
        dPrint(
            'Quiet', debuggingThisModule,
            "OpenSongXMLBible( {}, {}, {} )".format(sourceFolder, givenName,
                                                    encoding))
        Bible.__init__(self)
        self.objectNameString = 'OpenSong XML Bible object'
        self.objectTypeString = 'OpenSong'

        # Now we can set our object variables
        self.sourceFolder, self.givenName, self.encoding = sourceFolder, givenName, encoding
        self.sourceFilepath = os.path.join(self.sourceFolder, self.givenName)

        self.XMLTree = None  # Will hold the XML data

        # Get the data tables that we need for proper checking
        #self.ISOLanguages = ISO_639_3_Languages().loadData()
        self.genericBOS = BibleOrganisationalSystem('GENERIC-KJV-66-ENG')

        # Do a preliminary check on the readability of our file
        if not os.access(self.sourceFilepath, os.R_OK):
            vPrint(
                'Quiet', debuggingThisModule,
                "OpenSongXMLBible: File {!r} is unreadable".format(
                    self.sourceFilepath))

        self.name = self.givenName
예제 #2
0
    def fetchAllVersions(self):
        """
        Download the version lists from FCBH.

        This can be quite slow.

        Populates self.versionList (323 entries as of 2014-10)

        Each list entry is a dictionary, e.g.
            {'version_name': '', 'version_code': 'ABB', 'english_name': ''}
            {'version_name': '', 'version_code': 'ABM', 'english_name': ''}
            {'version_name': '', 'version_code': 'ABS', 'english_name': ''}
            …
            {'version_name': 'Biblia de América', 'version_code': 'BDA', 'english_name': ' Biblia de America'}
            {'version_name': 'Hermanos Libres del Ecuador', 'version_code': 'HLE', 'english_name': ' Hermanos Libres del Ecuador'}
            {'version_name': '1545 Luther Bibel', 'version_code': 'L45', 'english_name': '1545 Luther Bibel'}
            …
            {'version_name': 'Yessan-Mayo Yamano Version', 'version_code': 'YYV', 'english_name': 'Yessan-Mayo Yamano Version'}
            {'version_name': 'Yessan-Mayo Yawu', 'version_code': 'YWV', 'english_name': 'Yessan-Mayo Yawu'}
            {'version_name': 'Ze Zoo Zersion', 'version_code': 'ZZQ', 'english_name': 'Ze Zoo Zersion'}
        """
        fnPrint(debuggingThisModule, "DBPBibles.fetchAllVersions()")

        vPrint('Info', debuggingThisModule,
               _("Downloading list of available versions from FCBH…"))

        if self.onlineVersion:  # Get a list of available data sets
            self.versionList = self.getOnlineData(
                'library/version'
            )  # Get an alphabetically ordered list of dictionaries -- one for each version
            dPrint('Quiet', debuggingThisModule, "  versionList",
                   len(self.versionList))  #, self.versionList )
        return self.versionList
예제 #3
0
    def fetchAllLanguages(self):
        """
        Download the language lists from FCBH.

        This can be quite slow.

        Populates self.languageList (1733 entries as of 2014-10)

        Each list entry is a dictionary, e.g.
            {'language_family_code': 'CTD', 'language_name': 'Zokam', 'language_iso_name': 'Tedim Chin',
                'english_name': 'Zokam', 'language_code': 'CTD', 'language_iso_2B': '', 'language_iso': 'ctd',
                'language_iso_1': '', 'language_iso_2T': ''}
            {'language_family_code': 'ZOS', 'language_name': 'Zoque de Francisco León',
                'language_iso_name': 'Francisco León Zoque', 'english_name': 'Zoque de Francisco Leon',
                'language_code': 'ZOS', 'language_iso_2B': '', 'language_iso': 'zos', 'language_iso_1': '',
                'language_iso_2T': ''}
            {'language_family_code': 'GND', 'language_name': 'Zulgo', 'language_iso_name': 'Zulgo-Gemzek',
                'english_name': 'Zulgo', 'language_code': 'GND', 'language_iso_2B': '', 'language_iso': 'gnd',
                'language_iso_1': '', 'language_iso_2T': ''}
            {'language_family_code': 'ZUN', 'language_name': 'Zuni', 'language_iso_name': 'Zuni',
                'english_name': 'Zuni', 'language_code': 'ZUN', 'language_iso_2B': 'zun', 'language_iso': 'zun',
                'language_iso_1': '', 'language_iso_2T': 'zun'}
        """
        fnPrint(debuggingThisModule, "DBPBibles.fetchAllLanguages()")

        vPrint('Info', debuggingThisModule,
               _("Downloading list of available languages from FCBH…"))

        if self.onlineVersion:  # Get a list of available data sets
            self.languageList = self.getOnlineData(
                "library/language"
            )  # Get an alphabetically ordered list of dictionaries -- one for each language
            dPrint('Quiet', debuggingThisModule, "  languageList",
                   len(self.languageList))  #, self.languageList )
        return self.languageList
예제 #4
0
    def validateWithLint( self ):
        """
        On a Linux system, runs the xmllint program to validate the XML file.
        """
        checkProgramOutputString = checkProgramErrorOutputString = None

        parameters = [ '/usr/bin/xmllint', '--noout', self.sourceFilepath ]
        if self.schemaFilepath:
            parameters = [ '/usr/bin/xmllint', '--noout', '--schema', self.schemaFilepath, self.sourceFilepath ]
        checkProcess = subprocess.Popen( parameters, stdout=subprocess.PIPE, stderr=subprocess.PIPE )
        checkProgramOutputBytes, checkProgramErrorOutputBytes = checkProcess.communicate()

        if checkProgramOutputBytes:
            checkProgramOutputString = checkProgramOutputBytes.decode( encoding='utf-8', errors='replace' )
        if checkProgramErrorOutputBytes:
            checkProgramErrorOutputString = checkProgramErrorOutputBytes.decode( encoding='utf-8', errors='replace' )

        if checkProcess.returncode != 0:
            vPrint( 'Normal', debuggingThisModule, "  WARNING: xmllint gave an error on the {} XML file: {} = {}" \
                            .format( self.sourceFilepath, checkProcess.returncode, xmllintError[checkProcess.returncode] ) )
            self.validatedWithLint = False
        else:
            vPrint( 'Info', debuggingThisModule, "  xmllint validated the xml file {}.".format( self.sourceFilepath ) )
            self.validatedWithLint = True

        dPrint( 'Quiet', debuggingThisModule, "cPOS  = {!r}".format( checkProgramOutputString ) )
        dPrint( 'Quiet', debuggingThisModule, "cPEOS = {!r}".format( checkProgramErrorOutputString ) )
        return self.validatedWithLint, checkProgramOutputString, checkProgramErrorOutputString
예제 #5
0
def main() -> None:
    """
    Reorder songs by title (\s line in song record -- assumed to always be the second line in the record).
    """
    BibleOrgSysGlobals.introduceProgram(__name__, programNameVersion,
                                        LAST_MODIFIED_DATE)

    # Read our sample data
    songsInputFilepath = os.path.join(
        testFolder, testFile)  # Relative to module call, not cwd
    vPrint('Quiet', debuggingThisModule,
           _("Loading songs from {}…").format(songsInputFilepath))
    songs = SFMFile.SFMRecords()
    # Left the four default parameters at the end of the next line so you can see what's available
    songs.read(songsInputFilepath,
               key='c',
               ignoreSFMs=None,
               ignoreEntries=None,
               changePairs=None,
               encoding='utf-8')
    vPrint('Normal', debuggingThisModule,
           _("  {} songs loaded").format(len(songs.records)))
    vPrint('Never', debuggingThisModule, songs)

    # Extract the information out of the file that we want to use for sorting
    #   (We get the \s field, plus keep track of the index of each record)
    keyPairs = []
    for j, songRecord in enumerate(songs.records):
        dPrint('Never', debuggingThisModule, "songRecord", songRecord)
        sFieldData = songRecord[
            1]  # Get the second line of the song record (assumed to be the \s or title line )
        assert sFieldData[
            0] == 's'  # This is a 2-tuple of marker (without backslash) and marker contents
        keyPairs.append(
            (sFieldData[1], j)
        )  # Store the contents of the \s field, along with the index of this record
    dPrint('Never', debuggingThisModule, "keyPairs", keyPairs)

    # Now we sort the records by the \s field and write them out to a new file in the new, sorted order
    songsOutputFilepath = os.path.join(
        outputFolder, testFile)  # Relative to module call, not cwd
    vPrint('Quiet', debuggingThisModule,
           "Writing reordered songs to {}…".format(songsOutputFilepath))
    with open(songsOutputFilepath, 'wt') as outputFile:
        for k, keyPair in enumerate(sorted(keyPairs)):
            vPrint('Never', debuggingThisModule, "keyPair", keyPair)
            outputFile.write(
                '\n\\c {}\n'.format(k + 1)
            )  # Output our new (numbered) c line at the start of the record
            songRecord = songs.records[
                keyPair[1]]  # Get the record (song) that we need
            for s, songLine in enumerate(songRecord):
                vPrint('Never', debuggingThisModule, "songLine", s, songLine)
                if s == 0: continue  # skip old c line
                outputFile.write('\\{} {}\n'.format(*songLine))
    vPrint('Normal', debuggingThisModule, "  {} songs written".format(k + 1))

    vPrint('Quiet', debuggingThisModule,
           "{} finished.".format(programNameVersion))
예제 #6
0
    def fetchAllEnglishTextVolumes(self):
        """
        Download the volume lists from FCBH if necessary. (This can be quite slow.)

        Populates self.EnglishVolumeNameDict (847 entries as of 2014-10)

        Dictionary keys are version names, entries are a list of indexes to self.volumeList, e.g.
            'Popoloca San Juan Atzingo 1982 Edition' [1143]
            'Zokam 1994 Zokam International Version' [334]
            'ಕನ್ನಡ Easy-to-Read Version' [413]
            …
            'English 2001 English Standard' [393, 395]
            'English English Version for the Deaf' [396, 397]
            'English King James' [399, 401, 403, 405]
            'English 1995 Edition' [406, 407]
            'English 1986 New Life Version (Easy to Read)' [408]
            'English World English Bible' [410, 411]
            …
            'Español La Biblia de las Americas' [1302, 1303]
            'Mam, Northern 1993 Edition' [825, 826]
            'Русский 1876 Synodal Bible' [1246, 1247]
        """
        fnPrint(debuggingThisModule, "DBPBibles.fetchAllEnglishTextVolumes()")

        vPrint('Info', debuggingThisModule,
               _("Creating list of available English text volumes from FCBH…"))

        if self.volumeList is None:
            self.fetchAllVolumes()

        self.EnglishVolumeNameDict = {}
        if self.volumeList:  # Create a list of resource types
            for j, volume in enumerate(self.volumeList):
                assert volume['language_family_code']
                if volume['language_family_code'] == 'ENG':
                    assert volume['volume_name']
                    ourName = '{}: {}'.format(volume['version_code'],
                                              volume['volume_name'])
                    #ourName = volume['volume_name']
                    assert volume['media'] and volume['delivery'] and volume[
                        'collection_code']
                    if volume['media'] == 'text':
                        if 'web' in volume['delivery']:
                            self.EnglishVolumeNameDict[ourName] = volume[
                                'dam_id'][:
                                          6]  # Just remember the 6-character damRoot
                            #if ourName in self.EnglishVolumeNameDict: self.EnglishVolumeNameDict[ourName].append( j )
                            #else: self.EnglishVolumeNameDict[ourName] = [j]
                        else:
                            vPrint('Info',
                                   debuggingThisModule, "No web delivery in",
                                   repr(ourName), "only", volume['delivery'])
                    elif volume['media'] not in ('audio', 'video'):
                        vPrint('Quiet', debuggingThisModule, "No text in",
                               ourName, volume['media'])
        dPrint(
            'Quiet', debuggingThisModule, "EnglishVolumeNameDict",
            len(self.EnglishVolumeNameDict))  #, self.EnglishVolumeNameDict )
        return self.EnglishVolumeNameDict
예제 #7
0
    def fetchAllBibles(self):
        """
        Download the Bible lists from DCS.

        This can be quite slow.

        """
        fnPrint(debuggingThisModule, "DCSBibles.fetchAllBibles()")

        limit = 500  # Documentation says 50, but larger numbers seem to work ok
        vPrint(
            'Normal', debuggingThisModule,
            f"Downloading list of available Bibles from DCS ({limit} at a time)…"
        )

        if self.onlineVersion:  # Get a list of available data sets
            self.BibleList = []
            # Does a case-insensitive search
            for searchText in ('ULT', 'UST', 'Bible', 'ULB',
                               'UDB'):  # 7,227 if these are all included!!!
                pageNumber = 1
                while True:
                    if BibleOrgSysGlobals.verbosityLevel > 0:
                        vPrint('Quiet', debuggingThisModule,
                               f"  Getting '{searchText}' page {pageNumber}…")
                    resultDict = self.getOnlineData(
                        f'repos/search?q={searchText}&page={pageNumber}&limit={limit}'
                    )
                    #dPrint( 'Quiet', debuggingThisModule, f"  Result {type(resultDict)} {len(resultDict)} = {resultDict}" )
                    # resultDict should be a dict of length 2 with keys 'ok'(=True) and 'data'
                    assert resultDict and isinstance( resultDict, dict) and  len(resultDict) == 2 \
                                    and resultDict['ok']==True
                    if not resultDict['data']: break  # no more data
                    if BibleOrgSysGlobals.verbosityLevel > 1:
                        vPrint('Quiet', debuggingThisModule,
                               f"    Got {len(resultDict['data'])} entries")
                    self.BibleList.extend(resultDict['data'])
                    if pageNumber > 1 \
                    and len(resultDict['data']) < limit: # must be finished
                        break
                    pageNumber += 1
            dPrint('Quiet', debuggingThisModule, "  BibleList",
                   len(self.BibleList), self.BibleList)
        return self.BibleList
    def getOrganisationalSystemValue(self, valueName):
        """ Gets a value for the system. """
        #dPrint( 'Quiet', debuggingThisModule, "getOrganisationalSystemValue( {} )".format( repr(valueName) ) )
        assert self.__dataDict
        assert valueName and isinstance(valueName, str)

        if valueName in self.__dataDict: return self.__dataDict[valueName]
        # else maybe we can find the value in a derived text
        #dPrint( 'Quiet', debuggingThisModule, "q0", self.getOrganisationalSystemName() )
        for tryType in self.getMoreBasicTypes():
            if 'usesText' in self.__dataDict:
                for trySystemName in self.__dataDict['usesText']:
                    if isinstance(trySystemName, str):
                        dPrint(
                            'Quiet', debuggingThisModule,
                            "trySystemName for 'usesText' is a string: {!r}".
                            format(trySystemName))
                    elif isinstance(trySystemName, list):
                        #dPrint( 'Quiet', debuggingThisModule, "trySystemName for 'usesText' is a list: {!r}".format( trySystemName ) )
                        trySystemName = trySystemName[
                            0]  # Take the first string from the list
                    #dPrint( 'Quiet', debuggingThisModule, "q1", "{} is trying usesText of {}".format(self.__systemName,trySystemName) )
                    result = self.__boss.getOrganisationalSystemValue(
                        trySystemName, valueName)
                    if result is not None: return result
            if 'derivedFrom' in self.__dataDict:
                trySystemName = self.__dataDict['derivedFrom']
                if isinstance(trySystemName, str):
                    dPrint(
                        'Quiet', debuggingThisModule,
                        "trySystemName for 'derivedFrom' is a string: {!r}".
                        format(trySystemName))
                elif isinstance(trySystemName, list):
                    #dPrint( 'Quiet', debuggingThisModule, "trySystemName for 'derivedFrom' is a list: {!r}".format( trySystemName ) )
                    trySystemName = trySystemName[
                        0]  # Take the first string from the list
                #dPrint( 'Quiet', debuggingThisModule, "q2", "{} is trying derivedFrom of {}".format(self.__systemName,trySystemName) )
                result = self.__boss.getOrganisationalSystemValue(
                    trySystemName, valueName)
                if result is not None: return result
        # else we couldn't find it anywhere
        logging.error(
            _("{} Bible Organisational System has no {} specified (c)").format(
                self.getOrganisationalSystemName(), valueName))
예제 #9
0
 def validate(self):
     """
     """
     from BibleOrgSys.Internals.InternalBibleInternals import BOS_ALL_ADDED_MARKERS
     for USFMMarker, styleData in self.dataDict.items():
         dPrint('Verbose', debuggingThisModule,
                f"validate {USFMMarker} {styleData}")
         if USFMMarker.startswith('Heb') or USFMMarker.startswith(
                 'WordRef'):
             continue
         if USFMMarker in ('###', ): continue  # ignore
         if USFMMarker[0] == '*':
             USFMMarker = USFMMarker[
                 1:]  # Remove any leading asterisk for the check
         if USFMMarker[-1] == '#':
             USFMMarker = USFMMarker[:
                                     -1]  # Remove any trailing hash for the check
         #dPrint( 'Quiet', debuggingThisModule, USFMMarker )
         assert USFMMarker in BibleOrgSysGlobals.loadedUSFMMarkers or USFMMarker in BOS_ALL_ADDED_MARKERS
        def getOrganisationalSystemValue(valueName):
            """ Gets a value for the system. """
            def getMoreBasicTypes():
                """ Returns a list of more basic (original) types. """
                ix = BibleOrgSysGlobals.ALLOWED_ORGANISATIONAL_TYPES.index(
                    self.__dataDict["type"])
                return BibleOrgSysGlobals.ALLOWED_ORGANISATIONAL_TYPES[ix + 1:]

            # end of getMoreBasicTypes

            #dPrint( 'Quiet', debuggingThisModule, "q0", valueName )
            if valueName in self.__dataDict: return self.__dataDict[valueName]
            # else maybe we can find the value in a derived text
            #dPrint( 'Quiet', debuggingThisModule, "q1", self.getOrganisationalSystemName() )
            for tryType in getMoreBasicTypes():
                if 'usesText' in self.__dataDict:
                    for trySystemName in self.__dataDict['usesText']:
                        #dPrint( 'Quiet', debuggingThisModule, "q2", "{} is trying usesText of {}".format(self.__systemName,trySystemName) )
                        result = self.__boss.getOrganisationalSystemValue(
                            trySystemName, valueName)
                        #dPrint( 'Quiet', debuggingThisModule, "  result is", result )
                        if result is not None: return result
                if 'derivedFrom' in self.__dataDict:
                    trySystemName = self.__dataDict['derivedFrom']
                    if isinstance(trySystemName, str):
                        dPrint(
                            'Quiet', debuggingThisModule,
                            "trySystemName for 'derivedFrom' is a string: {!r}"
                            .format(trySystemName))
                    elif isinstance(trySystemName, list):
                        #dPrint( 'Quiet', debuggingThisModule, "trySystemName for 'derivedFrom' is a list: {!r}".format( trySystemName ) )
                        trySystemName = trySystemName[
                            0]  # Take the first string from the list
                    #dPrint( 'Quiet', debuggingThisModule, "q3", "{} is trying derivedFrom of {}".format(self.__systemName,trySystemName) )
                    result = self.__boss.getOrganisationalSystemValue(
                        trySystemName, valueName)
                    #dPrint( 'Quiet', debuggingThisModule, "  result is", result )
                    if result is not None: return result
            # else we couldn't find it anywhere
            logging.error(
                _("{} Bible Organisational System has no {} specified (b)").
                format(self.__systemName, valueName))
    def __init__(self, systemName) -> None:
        """
        Constructor:
        """
        def getOrganisationalSystemValue(valueName):
            """ Gets a value for the system. """
            def getMoreBasicTypes():
                """ Returns a list of more basic (original) types. """
                ix = BibleOrgSysGlobals.ALLOWED_ORGANISATIONAL_TYPES.index(
                    self.__dataDict["type"])
                return BibleOrgSysGlobals.ALLOWED_ORGANISATIONAL_TYPES[ix + 1:]

            # end of getMoreBasicTypes

            #dPrint( 'Quiet', debuggingThisModule, "q0", valueName )
            if valueName in self.__dataDict: return self.__dataDict[valueName]
            # else maybe we can find the value in a derived text
            #dPrint( 'Quiet', debuggingThisModule, "q1", self.getOrganisationalSystemName() )
            for tryType in getMoreBasicTypes():
                if 'usesText' in self.__dataDict:
                    for trySystemName in self.__dataDict['usesText']:
                        #dPrint( 'Quiet', debuggingThisModule, "q2", "{} is trying usesText of {}".format(self.__systemName,trySystemName) )
                        result = self.__boss.getOrganisationalSystemValue(
                            trySystemName, valueName)
                        #dPrint( 'Quiet', debuggingThisModule, "  result is", result )
                        if result is not None: return result
                if 'derivedFrom' in self.__dataDict:
                    trySystemName = self.__dataDict['derivedFrom']
                    if isinstance(trySystemName, str):
                        dPrint(
                            'Quiet', debuggingThisModule,
                            "trySystemName for 'derivedFrom' is a string: {!r}"
                            .format(trySystemName))
                    elif isinstance(trySystemName, list):
                        #dPrint( 'Quiet', debuggingThisModule, "trySystemName for 'derivedFrom' is a list: {!r}".format( trySystemName ) )
                        trySystemName = trySystemName[
                            0]  # Take the first string from the list
                    #dPrint( 'Quiet', debuggingThisModule, "q3", "{} is trying derivedFrom of {}".format(self.__systemName,trySystemName) )
                    result = self.__boss.getOrganisationalSystemValue(
                        trySystemName, valueName)
                    #dPrint( 'Quiet', debuggingThisModule, "  result is", result )
                    if result is not None: return result
            # else we couldn't find it anywhere
            logging.error(
                _("{} Bible Organisational System has no {} specified (b)").
                format(self.__systemName, valueName))

        # end of getOrganisationalSystemValue

        vPrint('Info', debuggingThisModule,
               _("Loading {!r} system").format(systemName))
        assert systemName and isinstance(systemName, str)
        self.__boss = BibleOrganisationalSystems().loadData(
        )  # Doesn't reload the XML unnecessarily :)
        result = self.__boss.getOrganisationalSystem(systemName)
        if result is None:
            logging.critical(
                _("No {!r} system in Bible Organisational Systems").format(
                    systemName))
            self.__dataDict = self.__systemName = None
            del self
            return

        # else:
        self.__dataDict = result
        self.__systemName = systemName
        #dPrint( 'Quiet', debuggingThisModule, self.__dataDict )

        # Now initialize the inherited classes
        bookOrderSystemName = self.getOrganisationalSystemValue(
            'bookOrderSystem')
        versificationSystemName = self.getOrganisationalSystemValue(
            'versificationSystem')
        punctuationSystemName = self.getOrganisationalSystemValue(
            'punctuationSystem')
        booksNamesSystemName = self.getOrganisationalSystemValue(
            'booksNamesSystem')
        dPrint(
            'Never', debuggingThisModule,
            "Got organisation bits: BOS={}, VS={}, PS={}, BNS={}".format(
                bookOrderSystemName, versificationSystemName,
                punctuationSystemName, booksNamesSystemName))
        if bookOrderSystemName and bookOrderSystemName != 'None' and bookOrderSystemName != 'Unknown':
            vPrint('Info', debuggingThisModule,
                   "Uses {!r} book order system".format(bookOrderSystemName))
            BibleBookOrderSystem.__init__(self, bookOrderSystemName)
        if versificationSystemName and versificationSystemName != 'None' and versificationSystemName != 'Unknown':
            vPrint(
                'Info', debuggingThisModule,
                "Uses {!r} versification system".format(
                    versificationSystemName))
            BibleVersificationSystem.__init__(self, versificationSystemName)
        if punctuationSystemName and punctuationSystemName != 'None' and punctuationSystemName != 'Unknown':
            vPrint(
                'Info', debuggingThisModule,
                "Uses {!r} punctuation system".format(punctuationSystemName))
            BiblePunctuationSystem.__init__(self, punctuationSystemName)
        if booksNamesSystemName and booksNamesSystemName != 'None' and booksNamesSystemName != 'Unknown':
            vPrint('Info', debuggingThisModule,
                   "Uses {!r} books name system".format(booksNamesSystemName))
            BibleBooksNamesSystem.__init__(
                self, booksNamesSystemName,
                getOrganisationalSystemValue('includesBooks')
            )  # Does one extra step To create the input abbreviations

        # Do some cross-checking
        myBooks = getOrganisationalSystemValue('includesBooks')
        if myBooks is not None:
            for BBB in myBooks:
                if not BibleBookOrderSystem.containsBook(self, BBB):
                    logging.error(
                        _("Book {!r} is included in {} system but missing from {} book order system"
                          ).format(
                              BBB, self.__systemName,
                              BibleBookOrderSystem.getBookOrderSystemName(
                                  self)))
예제 #12
0
def testSwB(SwFolderpath, SwModuleName=None):
    """
    Crudely demonstrate and test the Sword Bible class
    """
    from BibleOrgSys.Reference import VerseReferences

    vPrint('Normal', debuggingThisModule,
           _("Demonstrating the Sword Bible class…"))
    vPrint('Quiet', debuggingThisModule,
           "  Test folder is {!r} {!r}".format(SwFolderpath, SwModuleName))
    SwBible = SwordBible(SwFolderpath, SwModuleName)
    SwBible.loadBooks()  # Load and process the file
    vPrint('Normal', debuggingThisModule, SwBible)  # Just print a summary
    if BibleOrgSysGlobals.strictCheckingFlag:
        SwBible.check()
        #dPrint( 'Quiet', debuggingThisModule, UsfmB.books['GEN']._processedLines[0:40] )
        SwBErrors = SwBible.getCheckResults()
        #dPrint( 'Quiet', debuggingThisModule, SwBErrors )
    if BibleOrgSysGlobals.commandLineArguments.export:
        ##SwBible.toDrupalBible()
        SwBible.doAllExports(wantPhotoBible=False,
                             wantODFs=False,
                             wantPDFs=False)
    for reference in (
        ('OT', 'GEN', '1', '1'),
        ('OT', 'GEN', '1', '3'),
        ('OT', 'PSA', '3', '0'),
        ('OT', 'PSA', '3', '1'),
        ('OT', 'DAN', '1', '21'),
        ('NT', 'MAT', '1', '1'),
        ('NT', 'MAT', '3', '5'),
        ('NT', 'MAT', '3', '8'),
        ('NT', 'JDE', '1', '4'),
        ('NT', 'REV', '22', '21'),
        ('DC', 'BAR', '1', '1'),
        ('DC', 'MA1', '1', '1'),
        (
            'DC',
            'MA2',
            '1',
            '1',
        ),
    ):
        (T, BBB, C, V) = reference
        if T == 'OT' and len(SwBible) == 27:
            continue  # Don't bother with OT references if it's only a NT
        if T == 'NT' and len(SwBible) == 39:
            continue  # Don't bother with NT references if it's only a OT
        if T == 'DC' and len(SwBible) <= 66:
            continue  # Don't bother with DC references if it's too small
        svk = VerseReferences.SimpleVerseKey(BBB, C, V)
        #dPrint( 'Quiet', debuggingThisModule, svk, SwBible.getVerseDataList( reference ) )
        shortText = svk.getShortText()
        try:
            verseText = SwBible.getVerseText(svk)
            #dPrint( 'Quiet', debuggingThisModule, "verseText", verseText )
            fullVerseText = SwBible.getVerseText(svk, fullTextFlag=True)
        except KeyError:
            verseText = fullVerseText = "Verse not available!"
        if BibleOrgSysGlobals.verbosityLevel > 1:
            dPrint('Quiet', debuggingThisModule, '')
            vPrint('Quiet', debuggingThisModule, reference, shortText,
                   verseText)
            dPrint('Quiet', debuggingThisModule, '  {}'.format(fullVerseText))
    return SwBible
예제 #13
0
    def checkBookOrderSystem(self, thisSystemName, bookOrderSchemeToCheck):
        """
        Check the given book order scheme against all the loaded systems.
        Create a new book order file if it doesn't match any.
        Returns the number of matched systems (which can also be used as a True/False "matched" flag).
        """
        assert thisSystemName
        assert bookOrderSchemeToCheck
        assert self.__DataLists
        #dPrint( 'Quiet', debuggingThisModule, thisSystemName, bookOrderSchemeToCheck )
        for BBB in bookOrderSchemeToCheck:
            if not BibleOrgSysGlobals.loadedBibleBooksCodes.isValidBBB(BBB):
                logging.error(f"Invalid '{BBB}' book code")

        matchedBookOrderSystemCodes = []
        exactMatchCount, subsetMatchCount, systemMismatchCount, allErrors, errorSummary = 0, 0, 0, '', ''
        for bookOrderSystemCode in self.__DataLists:  # Step through the various reference schemes
            if self.__DataLists[bookOrderSystemCode] == bookOrderSchemeToCheck:
                #dPrint( 'Quiet', debuggingThisModule, "  {} exactly matches {!r} book order system".format( thisSystemName, bookOrderSystemCode ) )
                exactMatchCount += 1
                matchedBookOrderSystemCodes.append(bookOrderSystemCode)
            else:  # it's not an exact match
                if len(self.__DataLists[bookOrderSystemCode]) == len(
                        bookOrderSchemeToCheck
                ):  # They're both contain the same NUMBER of books
                    for BBB1, BBB2 in zip(
                            self.__DataLists[bookOrderSystemCode],
                            bookOrderSchemeToCheck):
                        if BBB1 != BBB2: break
                    thisError = "    " + _(
                        "Doesn't match '{0}' system (Both have {1} books, but {0} has {2} where {3} has {4})"
                    ).format(bookOrderSystemCode, len(bookOrderSchemeToCheck),
                             BBB1, thisSystemName, BBB2)
                    errorSummary += ("\n" if errorSummary else "") + thisError
                else:
                    thisError = "    " + _(
                        "Doesn't exactly match '{0}' system ({0} has {1} books whereas {2} has {3})"
                    ).format(bookOrderSystemCode,
                             len(self.__DataLists[bookOrderSystemCode]),
                             thisSystemName, len(bookOrderSchemeToCheck))
                    allErrors += ("\n" if allErrors else "") + thisError
                    #systemMismatchCount += 1
                # look for a subset
                lastIndex, isSubset = -1, True
                for BBB in bookOrderSchemeToCheck:
                    if not BBB in self.__DataLists[
                            bookOrderSystemCode]:  # This book isn't even in the other system
                        thisError = "    " + _(
                            "Can't match '{0}' system ({0} doesn't even have {1})"
                        ).format(bookOrderSystemCode, BBB)
                        allErrors += ("\n" if allErrors else "") + thisError
                        isSubset = False
                        break
                    index = self.__DataLists[bookOrderSystemCode].index(BBB)
                    #dPrint( 'Quiet', debuggingThisModule, BBB, index, lastIndex )
                    if index < lastIndex:  # they must be in a different order
                        thisError = "    " + _(
                            "Can't match '{0}' system ({0} has {1} in a different place)"
                        ).format(bookOrderSystemCode, BBB)
                        allErrors += ("\n" if allErrors else "") + thisError
                        isSubset = False
                        break
                    lastIndex = index
                if isSubset:
                    #dPrint( 'Quiet', debuggingThisModule, "  {} is a subset of {!r} book order system".format( thisSystemName, bookOrderSystemCode ) )
                    subsetMatchCount += 1
                    matchedBookOrderSystemCodes.append(bookOrderSystemCode)

        systemMatchCount = exactMatchCount + subsetMatchCount  # seems like we could improve this whole section of code
        systemMismatchCount = len(self.__DataLists) - systemMatchCount
        if systemMatchCount == 1:  # What we hope for
            vPrint(
                'Quiet', debuggingThisModule, "  " +
                _("{} matched {} book order (with these {} books)").format(
                    thisSystemName, matchedBookOrderSystemCodes[0],
                    len(bookOrderSchemeToCheck)))
            dPrint('Quiet', debuggingThisModule, errorSummary)
        elif systemMatchCount == 0:  # No matches
            vPrint(
                'Quiet', debuggingThisModule, "  " +
                _("{} mismatched {} book order systems (with these {} books)"
                  ).format(thisSystemName, systemMismatchCount,
                           len(bookOrderSchemeToCheck)))
            vPrint(
                'Quiet', debuggingThisModule,
                allErrors if BibleOrgSysGlobals.debugFlag
                or BibleOrgSysGlobals.verbosityLevel > 2 else errorSummary)
        else:  # Multiple matches
            vPrint(
                'Quiet', debuggingThisModule, "  " +
                _("{} matched {} book order system(s): {} (with these {} books)"
                  ).format(thisSystemName, systemMatchCount,
                           matchedBookOrderSystemCodes,
                           len(bookOrderSchemeToCheck)))
        dPrint('Quiet', debuggingThisModule, errorSummary)

        if BibleOrgSysGlobals.commandLineArguments.export and not systemMatchCount:  # Write a new file
            outputFilepath = os.path.join(
                os.path.dirname(__file__), 'DataFiles/', 'ScrapedFiles/',
                "BibleBookOrder_" + thisSystemName + '.xml')
            vPrint(
                'Quiet', debuggingThisModule,
                _("Writing {} {} books to {}…").format(
                    len(bookOrderSchemeToCheck), thisSystemName,
                    outputFilepath))
            with open(outputFilepath, 'wt', encoding='utf-8') as myFile:
                for n, BBB in enumerate(bookOrderSchemeToCheck):
                    myFile.write('  <book id="{}">{}</book>\n'.format(
                        n + 1, BBB))
                myFile.write("</BibleBookOrderSystem>")

        return systemMatchCount
예제 #14
0
    def fetchAllTextVolumes(self):
        """
        Download the volume lists from FCBH if necessary. (This can be quite slow.)

        Populates self.volumeNameDict (847 entries as of 2014-10)

        Dictionary keys are version names, entries are a list of indexes to self.volumeList, e.g.
            'Popoloca San Juan Atzingo 1982 Edition' [1143]
            'Zokam 1994 Zokam International Version' [334]
            'ಕನ್ನಡ Easy-to-Read Version' [413]
            …
            'English 2001 English Standard' [393, 395]
            'English English Version for the Deaf' [396, 397]
            'English King James' [399, 401, 403, 405]
            'English 1995 Edition' [406, 407]
            'English 1986 New Life Version (Easy to Read)' [408]
            'English World English Bible' [410, 411]
            …
            'Español La Biblia de las Americas' [1302, 1303]
            'Mam, Northern 1993 Edition' [825, 826]
            'Русский 1876 Synodal Bible' [1246, 1247]
        """
        fnPrint(debuggingThisModule, "DBPBibles.fetchAllTextVolumes()")

        vPrint('Info', debuggingThisModule,
               _("Creating list of available text volumes from FCBH…"))

        if self.volumeList is None:
            self.fetchAllVolumes()

        self.volumeNameDict = {}
        if self.volumeList:  # Create a list of resource types
            for j, volume in enumerate(self.volumeList):
                assert volume['language_name'] and volume['volume_name']
                ourName = '{} {}'.format(volume['language_name'],
                                         volume['volume_name'])
                assert volume['media'] and volume['delivery'] and volume[
                    'collection_code']
                if volume['media'] == 'text':
                    if 'web' in volume['delivery']:
                        #ourName= '{} {}'.format( volume['language_name'], volume['volume_name'] )
                        if ourName in self.volumeNameDict:
                            #dPrint( 'Quiet', debuggingThisModule, "\nAlready have", ourName )
                            ##dPrint( 'Quiet', debuggingThisModule, "New", j, volume )
                            #ix = self.volumeNameDict[ourName]
                            #oldVolume = self.volumeList[ix]
                            ##dPrint( 'Quiet', debuggingThisModule, "Old", ix, oldVolume )
                            #assert len(volume) == len(oldVolume)
                            #for someKey in volume:
                            #if volume[someKey] != oldVolume[someKey]:
                            #if someKey not in ('dam_id','fcbh_id','sku','updated_on','collection_name',):
                            #dPrint( 'Quiet', debuggingThisModule, "  ", someKey, volume[someKey], oldVolume[someKey] )
                            self.volumeNameDict[ourName].append(j)
                        else:
                            self.volumeNameDict[ourName] = [j]
                    #else: vPrint( 'Quiet', debuggingThisModule, j, repr(volume['language_name']), repr(volume['volume_name']) )
                    else:
                        vPrint('Info',
                               debuggingThisModule, "No web delivery in",
                               repr(ourName), "only", volume['delivery'])
                elif volume['media'] not in ('audio', 'video'):
                    vPrint('Quiet', debuggingThisModule, "No text in", ourName,
                           volume['media'])
        dPrint('Quiet', debuggingThisModule, "  volumeNameDict",
               len(self.volumeNameDict))  #, self.volumeNameDict )
        return self.volumeNameDict
예제 #15
0
    def fetchAllVolumes(self):
        """
        Download the volume lists from FCBH.

        This can be quite slow.

        Populates self.volumeList (1637 entries as of 2014-10)

        Each list entry is a dictionary, e.g.
            {'language_iso_1': '', 'collection_name': 'New Testament', 'language_family_iso_2B': '',
                'created_on': '2011-00-00 00:00:00', 'sku': 'N1ZTQTBL', 'language_code': 'ZTQ', 'media_type': 'Non-Drama',
                'delivery': ['mobile', 'web', 'subsplash'], 'num_sample_audio': '0', 'language_family_iso_2T': '',
                'version_name': 'The Bible League', 'media': 'text', 'expiration': '0000-00-00',
                'language_family_english': 'Zapoteco de Quioquitani', 'version_english': 'The Bible League',
                'updated_on': '2014-09-12 22:28:08', 'language_family_name': 'Zapoteco de Quioquitani',
                'language_iso_2T': '', 'language_family_code': 'ZTQ', 'volume_name': '2000 Edition',
                'dbp_agreement': 'true', 'language_english': 'Zapoteco de Quioquitani', 'language_family_iso': 'ztq',
                'dam_id': 'ZTQTBLN1ET', 'font': {'platforms': {'android': True, 'ios': True, 'web': True}, 'id': '12',
                'files': {'zip': 'all.zip', 'ttf': 'font.ttf'}, 'name': 'Charis SIL',
                'base_url': 'http://cloud.faithcomesbyhearing.com/fonts/Charis_SIL'}, 'resolution': [],
                'audio_zip_path': 'ZTQTBLN1ET/ZTQTBLN1ET.zip', 'language_iso_2B': '', 'fcbh_id': 'ZTQTBLN1ET',
                'status': 'live', 'language_family_iso_1': '', 'language_iso_name': 'Quioquitani-Quierí Zapotec',
                'language_iso': 'ztq', 'language_name': 'Zapoteco de Quioquitani', 'right_to_left': 'false',
                'num_art': '0', 'version_code': 'TBL', 'collection_code': 'NT'}
            {'language_iso_1': '', 'collection_name': 'New Testament', 'language_family_iso_2B': '',
                'created_on': '2010-09-14 17:01:29', 'sku': 'N1ZTYTBL', 'language_code': 'ZTY', 'media_type': 'Non-Drama',
                'delivery': ['mobile', 'web', 'web_streaming', 'streaming_url', 'mp3_cd', 'digital_download', 'bible_stick', 'subsplash'],
                'num_sample_audio': '1', 'language_family_iso_2T': '', 'version_name': 'The Bible League', 'media': 'audio',
                'expiration': '0000-00-00', 'language_family_english': 'Zapoteco de Yatee',
                'version_english': 'The Bible League', 'updated_on': '2014-01-03 21:36:32',
                'language_family_name': 'Zapoteco de Yatee', 'language_iso_2T': '', 'language_family_code': 'ZTY',
                'volume_name': '2002 Edition', 'dbp_agreement': 'true', 'language_english': 'Zapoteco de Yatee',
                'language_family_iso': 'zty', 'dam_id': 'ZTYTBLN1DA', 'font': None, 'resolution': [],
                'audio_zip_path': 'ZTYTBLN1DA/ZTYTBLN1DA.zip', 'language_iso_2B': '', 'fcbh_id': 'ZTYTBLN1DA',
                'status': 'live', 'language_family_iso_1': '', 'language_iso_name': 'Yatee Zapotec', 'language_iso': 'zty',
                'language_name': 'Zapoteco de Yatee', 'right_to_left': 'false', 'num_art': '3', 'version_code': 'TBL',
                'collection_code': 'NT'}
            {'language_iso_1': '', 'collection_name': 'New Testament', 'language_family_iso_2B': '',
                'created_on': '2011-00-00 00:00:00', 'sku': 'N1ZTYTBL', 'language_code': 'ZTY', 'media_type': 'Non-Drama',
                'delivery': ['mobile', 'web', 'subsplash'], 'num_sample_audio': '0', 'language_family_iso_2T': '',
                'version_name': 'The Bible League', 'media': 'text', 'expiration': '0000-00-00',
                'language_family_english': 'Zapoteco de Yatee', 'version_english': 'The Bible League',
                'updated_on': '2014-09-12 22:28:08', 'language_family_name': 'Zapoteco de Yatee', 'language_iso_2T': '',
                'language_family_code': 'ZTY', 'volume_name': '2002 Edition', 'dbp_agreement': 'true',
                'language_english': 'Zapoteco de Yatee', 'language_family_iso': 'zty', 'dam_id': 'ZTYTBLN1ET',
                'font': {'platforms': {'android': True, 'ios': True, 'web': True}, 'id': '12',
                'files': {'zip': 'all.zip', 'ttf': 'font.ttf'}, 'name': 'Charis SIL',
                'base_url': 'http://cloud.faithcomesbyhearing.com/fonts/Charis_SIL'}, 'resolution': [],
                'audio_zip_path': 'ZTYTBLN1ET/ZTYTBLN1ET.zip', 'language_iso_2B': '', 'fcbh_id': 'ZTYTBLN1ET',
                'status': 'live', 'language_family_iso_1': '', 'language_iso_name': 'Yatee Zapotec', 'language_iso': 'zty',
                'language_name': 'Zapoteco de Yatee', 'right_to_left': 'false', 'num_art': '0', 'version_code': 'TBL',
                'collection_code': 'NT'}
        """
        fnPrint(debuggingThisModule, "DBPBibles.fetchAllVolumes()")

        vPrint('Info', debuggingThisModule,
               _("Downloading list of available volumes from FCBH…"))

        if self.onlineVersion:  # Get a list of available data sets
            self.volumeList = self.getOnlineData(
                'library/volume'
            )  # Get an alphabetically ordered list of dictionaries -- one for each volume
            dPrint('Quiet', debuggingThisModule, "  volumeList",
                   len(self.volumeList))  #, self.volumeList )
        return self.volumeList
예제 #16
0
    def loadBook(self, BBB: str):
        """
        Load the requested book out of the SQLite3 database.
        """
        fnPrint(debuggingThisModule, "loadBook( {} )".format(BBB))
        assert self.preloadDone

        if BBB in self.books:
            dPrint('Quiet', debuggingThisModule,
                   "  {} is already loaded -- returning".format(BBB))
            return  # Already loaded
        if BBB in self.triedLoadingBook:
            logging.warning(
                "We had already tried loading MySwordBible {} for {}".format(
                    BBB, self.name))
            return  # We've already attempted to load this book
        self.triedLoadingBook[BBB] = True
        self.bookNeedsReloading[BBB] = False
        if BibleOrgSysGlobals.verbosityLevel > 2 or BibleOrgSysGlobals.debugFlag:
            vPrint(
                'Quiet', debuggingThisModule,
                _("MySwordBible: Loading {} from {}…").format(
                    BBB, self.sourceFilepath))

        #if self.suppliedMetadata['MySword']['OT'] and self.suppliedMetadata['MySword']['NT']:
        #testament, BBB = 'BOTH', 'GEN'
        #booksExpected, textLineCountExpected = 1, 31102
        #elif self.suppliedMetadata['MySword']['OT']:
        #testament, BBB = 'OT', 'GEN'
        #booksExpected, textLineCountExpected = 1, 23145
        #elif self.suppliedMetadata['MySword']['NT']:
        #testament, BBB = 'NT', 'MAT'
        #booksExpected, textLineCountExpected = 1, 7957

        # Create the first book
        thisBook = BibleBook(self, BBB)
        thisBook.objectNameString = 'MySword Bible Book object'
        thisBook.objectTypeString = 'MySword'

        verseList = self.BibleOrganisationalSystem.getNumVersesList(BBB)
        numC, numV = len(verseList), verseList[0]
        nBBB = BibleOrgSysGlobals.loadedBibleBooksCodes.getReferenceNumber(BBB)
        C = V = 1

        #bookCount = 0
        ourGlobals = {}
        continued = ourGlobals['haveParagraph'] = False
        haveLines = False
        while True:
            self.cursor.execute(
                'select Scripture from Bible where Book=? and Chapter=? and Verse=?',
                (nBBB, C, V))
            try:
                row = self.cursor.fetchone()
                line = row[0]
            except TypeError:  # This reference is missing (row is None)
                #dPrint( 'Quiet', debuggingThisModule, "something wrong at", BBB, C, V )
                #if BibleOrgSysGlobals.debugFlag: halt
                #dPrint( 'Quiet', debuggingThisModule, row )
                line = None
            #dPrint( 'Quiet', debuggingThisModule, nBBB, BBB, C, V, 'MySw file line is "' + line + '"' )
            if line is None:
                logging.warning(
                    "MySwordBible.load: Have missing verse line at {} {}:{}".
                    format(BBB, C, V))
            else:  # line is not None
                if not isinstance(line, str):
                    if 'encryption' in self.suppliedMetadata['MySword']:
                        logging.critical(
                            "MySwordBible.load: Unable to decrypt verse line at {} {}:{} {!r}"
                            .format(BBB, C, V, line))
                        break
                    else:
                        logging.critical(
                            "MySwordBible.load: Unable to decode verse line at {} {}:{} {!r} {}"
                            .format(BBB, C, V, line,
                                    self.suppliedMetadata['MySword']))
                elif not line:
                    logging.warning(
                        "MySwordBible.load: Found blank verse line at {} {}:{}"
                        .format(BBB, C, V))
                else:
                    haveLines = True

                    # Some modules end lines with \r\n or have it in the middle!
                    #   (We just ignore these for now)
                    while line and line[-1] in '\r\n':
                        line = line[:-1]
                    if '\r' in line or '\n' in line:  # (in the middle)
                        logging.warning(
                            "MySwordBible.load: Found CR or LF characters in verse line at {} {}:{}"
                            .format(BBB, C, V))
                    line = line.replace('\r\n',
                                        ' ').replace('\r',
                                                     ' ').replace('\n', ' ')

            #dPrint( 'Quiet', debuggingThisModule, "MySword.load", BBB, C, V, repr(line) )
            handleRTFLine(self.name, BBB, C, V, line, thisBook, ourGlobals)
            V += 1
            if V > numV:
                C += 1
                if C <= numC:  # next chapter only
                    #thisBook.addLine( 'c', str(C) )
                    numV = verseList[C - 1]
                    V = 1
                else:  # Save this book now
                    if haveLines:
                        vPrint('Info', debuggingThisModule, "  MySword saving",
                               BBB)
                        self.stashBook(thisBook)
                    #else: vPrint( 'Quiet', debuggingThisModule, "Not saving", BBB )
                    break

            if ourGlobals['haveParagraph']:
                thisBook.addLine('p', '')
                ourGlobals['haveParagraph'] = False
예제 #17
0
def USFMBookCompare(filepath1,
                    filepath2,
                    file1Name='file1',
                    file2Name='file2'):
    """
    """
    vPrint('Info', debuggingThisModule,
           "\nUSFMBookCompare() for USFM Bible books")
    vPrint('Verbose', debuggingThisModule, "  comparing {}".format(filepath1))
    vPrint('Verbose', debuggingThisModule, "        and {}".format(filepath2))

    # Set up empty results dictionaries
    resultDict = {}
    resultDict['File1'], resultDict['File2'] = {}, {}
    resultDict['Same'], resultDict['Different'], resultDict[
        'Summary'] = {}, {}, {}

    # Note paths and folders
    resultDict['File1']['Filepath'], resultDict['File2'][
        'Filepath'] = filepath1, filepath2
    resultDict['File1']['Folder'], resultDict['File1'][
        'Filename'] = os.path.split(filepath1)
    resultDict['File2']['Folder'], resultDict['File2'][
        'Filename'] = os.path.split(filepath2)
    if resultDict['File2']['Filename'] == resultDict['File1']['Filename']:
        resultDict['Same']['Filename'] = resultDict['File1']['Filename']
    if resultDict['File2']['Folder'] == resultDict['File1']['Folder']:
        resultDict['Same']['Folder'] = resultDict['File1']['Folder']

    # Note file dates and sizes
    s1, s2 = os.stat(filepath1), os.stat(filepath2)
    resultDict['File1']['Filesize'], resultDict['File2'][
        'Filesize'] = s1.st_size, s2.st_size
    if resultDict['File1']['Filesize'] == resultDict['File2']['Filesize']:
        resultDict['Same']['Filesize'] = resultDict['File1']['Filesize']
    else:
        resultDict['Different']['Filesize'] = (resultDict['File1']['Filesize'],
                                               resultDict['File2']['Filesize'])
        if s1.st_size > s2.st_size:
            resultDict['Summary'][
                'Filesize'] = "{} is bigger (by {:,} bytes)".format(
                    file1Name, s1.st_size - s2.st_size)
        elif s1.st_size < s2.st_size:
            resultDict['Summary'][
                'Filesize'] = "{} is bigger (by {:,} bytes)".format(
                    file2Name, s2.st_size - s1.st_size)
    resultDict['File1']['ModifiedTimeStamp'], resultDict['File2'][
        'ModifiedTimeStamp'] = s1.st_mtime, s2.st_mtime
    if s1.st_mtime - s2.st_mtime > 1.0:
        resultDict['Summary']['ModifiedTime'] = "{} is newer".format(file1Name)
    elif s2.st_mtime - s1.st_mtime > 1.0:
        resultDict['Summary']['ModifiedTime'] = "{} is newer".format(file2Name)
    t1, t2 = datetime.fromtimestamp(s1.st_mtime), datetime.fromtimestamp(
        s2.st_mtime)
    resultDict['File1']['ModifiedDate'], resultDict['File2'][
        'ModifiedDate'] = t1.strftime('%Y-%m-%d'), t2.strftime('%Y-%m-%d')
    if resultDict['File1']['ModifiedDate'] == resultDict['File2'][
            'ModifiedDate']:
        resultDict['Same']['ModifiedDate'] = resultDict['File1'][
            'ModifiedDate']
    else:
        resultDict['Different']['ModifiedDate'] = (
            resultDict['File1']['ModifiedDate'],
            resultDict['File2']['ModifiedDate'])
    resultDict['File1']['ModifiedTime'], resultDict['File2'][
        'ModifiedTime'] = t1.strftime('%H:%M:%S'), t2.strftime('%H:%M:%S')
    if resultDict['File1']['ModifiedTime'] == resultDict['File2'][
            'ModifiedTime']:
        resultDict['Same']['ModifiedTime'] = resultDict['File1'][
            'ModifiedTime']
    else:
        resultDict['Different']['ModifiedTime'] = (
            resultDict['File1']['ModifiedTime'],
            resultDict['File2']['ModifiedTime'])

    # Read the files
    uf1, uf2 = USFMFile(), USFMFile()
    uf1.read(filepath1)
    uf2.read(filepath2)
    #dPrint( 'Quiet', debuggingThisModule, 'f1', uf1.lines )
    #dPrint( 'Quiet', debuggingThisModule, 'f2', uf2.lines )

    # Note line counts
    resultDict['File1']['LineCount'], resultDict['File2']['LineCount'] = len(
        uf1.lines), len(uf2.lines)
    if resultDict['File1']['LineCount'] == resultDict['File2']['LineCount']:
        resultDict['Same']['LineCount'] = resultDict['File1']['LineCount']
    else:
        resultDict['Different']['LineCount'] = (
            resultDict['File1']['LineCount'], resultDict['File2']['LineCount'])

    # Work through each file counting chapters and verses, etc.
    resultDict['File1']['IntroLineCount'] = resultDict['File2'][
        'IntroLineCount'] = 0
    resultDict['File1']['ChapterMarkerCount'] = resultDict['File2'][
        'ChapterMarkerCount'] = 0
    resultDict['File1']['VerseMarkerCount'] = resultDict['File2'][
        'VerseMarkerCount'] = 0
    resultDict['File1']['HasContentCount'] = resultDict['File2'][
        'HasContentCount'] = 0
    startedCVs = False
    lastC = lastV = 0
    C, V = '-1', '-1'  # So first/id line starts at -1:0
    for marker, line in uf1.lines:
        #dPrint( 'Quiet', debuggingThisModule, '1', C, V, lastC, lastV, marker, line )
        if marker == 'c':
            resultDict['File1']['ChapterMarkerCount'] += 1
            C, V, lastV = line.strip(), '0', 0
            try:
                intC = int(C)
            except ValueError:
                intC = -2  # invalid value
            startedCVs = True
            if intC != lastC + 1:
                if 'File1Chapters' not in resultDict[
                        'Summary']:  # only record the first one
                    resultDict['Summary'][
                        'File1Chapters'] = "{} has chapters out of order ({} after {})".format(
                            file1Name, C, lastC)
            lastC = intC
        elif marker == 'v':
            resultDict['File1']['VerseMarkerCount'] += 1
            V = line.strip().split()[0]
            if '-' in V:  # it's a verse bridge
                V, V2 = V.split('-', 1)
            else:
                V2 = None
            try:
                intV = int(V)
            except ValueError:
                intV = -1
            startedCVs = True  # Some one chapter books don't include a chapter marker
            if intV != lastV + 1:
                if 'File1Verses' not in resultDict[
                        'Summary']:  # only record the first one
                    resultDict['Summary'][
                        'File1Verses'] = "{} has verses out of order ({}:{} after {}:{})".format(
                            file1Name, C, V, C, lastV)
            if V2: lastV = int(V2)
            else: lastV = intV
        if not startedCVs: resultDict['File1']['IntroLineCount'] += 1
        if line.strip(): resultDict['File1']['HasContentCount'] += 1
        if '<<<<' in line or '====' in line or '>>>>' in line:
            if 'File1Conflicts' not in resultDict[
                    'Summary']:  # only record the first one
                resultDict['Summary'][
                    'File1Conflicts'] = "{} may have a merge conflict around {}:{}".format(
                        file1Name, C, V)

    startedCVs = False
    lastC = lastV = 0
    C, V = '-1', '-1'  # So first/id line starts at -1:0
    for marker, line in uf2.lines:
        #dPrint( 'Quiet', debuggingThisModule, '1', C, V, lastC, lastV, marker, line )
        if marker == 'c':
            resultDict['File2']['ChapterMarkerCount'] += 1
            C, V, lastV = line.strip(), '0', 0
            try:
                intC = int(C)
            except ValueError:
                intC = -2  # invalid value
            startedCVs = True
            if intC != lastC + 1:
                if 'File2Chapters' not in resultDict[
                        'Summary']:  # only record the first one
                    resultDict['Summary'][
                        'File2Chapters'] = "{} has chapters out of order ({} after {})".format(
                            file2Name, C, lastC)
            lastC = intC
        elif marker == 'v':
            resultDict['File2']['VerseMarkerCount'] += 1
            V = line.strip().split()[0]
            if '-' in V:  # it's a verse bridge
                V, V2 = V.split('-', 1)
            else:
                V2 = None
            try:
                intV = int(V)
            except ValueError:
                intV = -1
            startedCVs = True  # Some one chapter books don't include a chapter marker
            if intV != lastV + 1:
                if 'File2Verses' not in resultDict[
                        'Summary']:  # only record the first one
                    resultDict['Summary'][
                        'File2Verses'] = "{} has verses out of order ({}:{} after {}:{})".format(
                            file2Name, C, V, C, lastV)
            if V2: lastV = int(V2)
            else: lastV = intV
        if not startedCVs: resultDict['File2']['IntroLineCount'] += 1
        if line.strip(): resultDict['File2']['HasContentCount'] += 1
        if '<<<<' in line or '====' in line or '>>>>' in line:
            if 'File2Conflicts' not in resultDict[
                    'Summary']:  # only record the first one
                resultDict['Summary'][
                    'File2Conflicts'] = "{} may have a merge conflict around {}:{}".format(
                        file2Name, C, V)

    if resultDict['File1']['IntroLineCount'] == resultDict['File2'][
            'IntroLineCount']:
        resultDict['Same']['IntroLineCount'] = resultDict['File1'][
            'IntroLineCount']
    else:
        resultDict['Different']['IntroLineCount'] = (
            resultDict['File1']['IntroLineCount'],
            resultDict['File2']['IntroLineCount'])
        if resultDict['File1']['IntroLineCount'] > resultDict['File2'][
                'IntroLineCount']:
            difference = resultDict['File1']['IntroLineCount'] - resultDict[
                'File2']['IntroLineCount']
            resultDict['Summary'][
                'IntroLineCount'] = "{} has {} more intro marker{}".format(
                    file1Name, difference, '' if difference == 1 else 's')
        elif resultDict['File1']['IntroLineCount'] < resultDict['File2'][
                'IntroLineCount']:
            difference = resultDict['File2']['IntroLineCount'] - resultDict[
                'File1']['IntroLineCount']
            resultDict['Summary'][
                'IntroLineCount'] = "{} has {} more intro marker{}".format(
                    file2Name, difference, '' if difference == 1 else 's')
    if resultDict['File1']['ChapterMarkerCount'] == resultDict['File2'][
            'ChapterMarkerCount']:
        resultDict['Same']['ChapterMarkerCount'] = resultDict['File1'][
            'ChapterMarkerCount']
    else:
        resultDict['Different']['ChapterMarkerCount'] = (
            resultDict['File1']['ChapterMarkerCount'],
            resultDict['File2']['ChapterMarkerCount'])
        if resultDict['File1']['ChapterMarkerCount'] > resultDict['File2'][
                'ChapterMarkerCount']:
            difference = resultDict['File1']['ChapterMarkerCount'] - resultDict[
                'File2']['ChapterMarkerCount']
            resultDict['Summary'][
                'ChapterMarkerCount'] = "{} has {} more chapter marker{}".format(
                    file1Name, )
        elif resultDict['File1']['ChapterMarkerCount'] < resultDict['File2'][
                'ChapterMarkerCount']:
            difference = resultDict['File2']['ChapterMarkerCount'] - resultDict[
                'File1']['ChapterMarkerCount']
            resultDict['Summary'][
                'ChapterMarkerCount'] = "{} has {} more chapter marker{}".format(
                    file2Name, difference, '' if difference == 1 else 's')
    if resultDict['File1']['VerseMarkerCount'] == resultDict['File2'][
            'VerseMarkerCount']:
        resultDict['Same']['VerseMarkerCount'] = resultDict['File1'][
            'VerseMarkerCount']
    else:
        resultDict['Different']['VerseMarkerCount'] = (
            resultDict['File1']['VerseMarkerCount'],
            resultDict['File2']['VerseMarkerCount'])
        if resultDict['File1']['VerseMarkerCount'] > resultDict['File2'][
                'VerseMarkerCount']:
            difference = resultDict['File1']['VerseMarkerCount'] - resultDict[
                'File2']['VerseMarkerCount']
            resultDict['Summary'][
                'VerseMarkerCount'] = "{} has {} more verse marker{}".format(
                    file1Name, difference, '' if difference == 1 else 's')
        elif resultDict['File1']['VerseMarkerCount'] < resultDict['File2'][
                'VerseMarkerCount']:
            difference = resultDict['File2']['VerseMarkerCount'] - resultDict[
                'File1']['VerseMarkerCount']
            resultDict['Summary'][
                'VerseMarkerCount'] = "{} has {} more verse marker{}".format(
                    file2Name, difference, '' if difference == 1 else 's')
    if resultDict['File1']['HasContentCount'] == resultDict['File2'][
            'HasContentCount']:
        resultDict['Same']['HasContentCount'] = resultDict['File1'][
            'HasContentCount']
    else:
        resultDict['Different']['HasContentCount'] = (
            resultDict['File1']['HasContentCount'],
            resultDict['File2']['HasContentCount'])
        if resultDict['File1']['HasContentCount'] > resultDict['File2'][
                'HasContentCount']:
            difference = resultDict['File1']['HasContentCount'] - resultDict[
                'File2']['HasContentCount']
            resultDict['Summary'][
                'HasContentCount'] = "{} has {} more content line{}".format(
                    file1Name, difference, '' if difference == 1 else 's')
        elif resultDict['File1']['HasContentCount'] < resultDict['File2'][
                'HasContentCount']:
            difference = resultDict['File2']['HasContentCount'] - resultDict[
                'File1']['HasContentCount']
            resultDict['Summary'][
                'HasContentCount'] = "{} has {} more content line{}".format(
                    file2Name, difference, '' if difference == 1 else 's')

    # Work through the files again comparing lines
    #   Trying to resync if there's a different number of lines…NOT FINISHED YET XXXXXXXXXXXXXXX
    resultDict['Same']['SameMarkerCount'] = resultDict['Different'][
        'DifferentMarkerCount'] = 0
    resultDict['Same']['SameLineCount'] = resultDict['Different'][
        'DifferentLineCount'] = 0
    lineIndex = lineOffset = 0
    startedCVs1 = startedCVs2 = False
    while True:
        if lineIndex >= resultDict['File1']['LineCount']:
            vPrint('Never', debuggingThisModule, "File1 done")
            break
        if lineIndex >= resultDict['File2']['LineCount']:
            vPrint('Never', debuggingThisModule, "File2 done")
            break
        (m1,
         l1), (m2,
               l2) = uf1.lines[lineIndex], uf2.lines[lineIndex + lineOffset]
        #dPrint( 'Quiet', debuggingThisModule, lineIndex, lineOffset, m1, m2 )
        if m1 == m2: resultDict['Same']['SameMarkerCount'] += 1
        else:
            dPrint('Quiet', debuggingThisModule, "Diff", m1, m2, l1, l2)
            resultDict['Different']['DifferentMarkerCount'] += 1
        if m1 == m2 and l1 == l2: resultDict['Same']['SameLineCount'] += 1
        else:
            dPrint('Quiet', debuggingThisModule, "Diff", m1, m2, l1, l2)
            resultDict['Different']['DifferentLineCount'] += 1
        lineIndex += 1

    # Clean up and return
    for something, value in list(resultDict['Different'].items()):
        if not value: del resultDict['Different'][something]
    return resultDict