Source

noor / noor / quran.py

import os.path
import sys
import xml.sax.handler

import noor.utils


class Quran(object):

    def __init__(self, text, replacements=[]):
        self._init_data()
        self.text = text
        self.replacements = replacements

    def _init_data(self):
        datafile = os.path.join(get_path('data', 'noor/data'),
                                'quran-data.xml')
        handler = _DataHandler()
        xml.sax.parse(datafile, handler)
        self.data = handler.result

    @property
    @noor.utils.cacheit
    def suras(self):
        result = []
        for index, sura in sorted(self.data['sura'].items()):
            result.append(Sura(self, sura))
        return result

    def _sura_ayas(self, sura):
        lineno = int(sura.data['start'])
        ayas = int(sura.data['ayas'])
        start = self._find_line_start(lineno)
        end = self._find_line_start(ayas, initial=start) - 1
        text = self.text[start:end]
        for pattern, goal in self.replacements:
            text = text.replace(pattern, goal)
        sura_ayas = text.splitlines()
        if sura.number != 1:
            self._fix_extra_in_the_name(sura_ayas)
        return sura_ayas

    def _fix_extra_in_the_name(self, sura_ayas):
        if not self.suras:
            return
        in_the_name = self.get_in_the_name()
        if sura_ayas[0].startswith(in_the_name):
            sura_ayas[0] = sura_ayas[0][len(in_the_name):]

    def _find_line_start(self, lineno, initial=0):
        index = initial
        for i in range(lineno):
            try:
                index = self.text.index('\n', index) + 1
            except ValueError:
                return len(self.text)
        return index

    def get_in_the_name(self):
        result = self.suras[0].ayas[0]
        if ord(result[0]) == 65279:
            return result[1:]
        return result

    def _sura_juz(self, sura):
        number = sura.number
        result = []
        for index, start in enumerate(self.juz_starts):
            if start[0] < number:
                continue
            if start[0] == number:
                if not result and start[1] > 1:
                    result.append(index)
                result.append(index + 1)
            if start[0] > number:
                if not result:
                    result.append(index)
                break
        if not result:
            result.append(30)
        return result

    @property
    @noor.utils.cacheit
    def juz_starts(self):
        result = []
        for index, juz in sorted(self.data['juz'].items()):
            result.append((int(juz['sura']), int(juz['aya'])))
        return result

    @property
    @noor.utils.cacheit
    def sajdas(self):
        result = {}
        for index, sajda in sorted(self.data['sajda'].items()):
            kind = 'major'
            if sajda['type'] == 'recommended':
                kind = 'minor'
            result[(int(sajda['sura']), int(sajda['aya']))] = kind
        return result


class Sura(object):

    def __init__(self, quran, data):
        self.quran = quran
        self.data = data

    @property
    @noor.utils.cacheit
    def juz(self):
        return self.quran._sura_juz(self)

    @property
    @noor.utils.cacheit
    def ayas(self):
        return self.quran._sura_ayas(self)

    @property
    def number(self):
        return int(self.data['index'])

    @property
    def name(self):
        return self.data['name']

    @property
    def english_name(self):
        return self.data['tname']


def get_path(local_path, global_path):
    noordir = os.path.dirname(sys.modules['noor'].__file__)
    path = os.path.join(noordir, local_path)
    if os.path.exists(path):
        return path
    for prefix in ['/usr', '/usr/local']:
        path = os.path.join(prefix, 'share', global_path)
        if os.path.exists(path):
            return path
    raise RuntimeError('can\'t find data dir: ' + local_path)

def quran_from_path(path=None, **kwds):
    if path is None:
        path = os.path.join(get_path('', 'noor'), 'quran-text.txt')
    quran_file = open(path, 'rb')
    data = quran_file.read()
    quran_file.close()
    return Quran(data.decode('utf-8'), **kwds)

def get_aya(quran, sura, aya):
    return quran.suras[sura - 1].ayas[aya - 1]

class _DataHandler(xml.sax.handler.ContentHandler):

    def __init__(self):
        self.result = {}

    def startElement(self, name, attrs):
        attrs = dict(attrs)
        if 'index' in attrs:
            values = self.result.setdefault(name, {})
            values[int(attrs['index'])] = attrs
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.