Пример #1
0
Файл: main.py Проект: cceh/ntg
def manuscript_full_json (passage_or_id, hs_hsnr_id):
    """Endpoint.  Serve information about a manuscript.

    :param string hs_hsnr_id: The hs, hsnr or id of the manuscript.

    """

    auth ()

    hs_hsnr_id = request.args.get ('ms_id') or hs_hsnr_id
    chapter    = request.args.get ('range') or 'All'

    with current_app.config.dba.engine.begin () as conn:
        passage   = Passage (conn, passage_or_id)
        ms        = Manuscript (conn, hs_hsnr_id)
        rg_id     = passage.range_id (chapter)

        json = ms.to_json ()
        json['length'] = ms.get_length (passage, chapter)

        # Get the attestation(s) of the manuscript (may be uncertain eg. a/b/c)
        res = execute (conn, """
        SELECT labez, clique, labez_clique
        FROM apparatus_view_agg
        WHERE ms_id = :ms_id AND pass_id = :pass_id
        """, dict (parameters, ms_id = ms.ms_id, pass_id = passage.pass_id))
        json['labez'], json['clique'], json['labez_clique'] = res.fetchone ()

        # Get the affinity of the manuscript to all manuscripts
        res = execute (conn, """
        SELECT avg (a.affinity) as aa,
        percentile_cont(0.5) WITHIN GROUP (ORDER BY a.affinity) as ma
        FROM affinity a
        WHERE a.ms_id1 = :ms_id1 AND a.rg_id = :rg_id
        """, dict (parameters, ms_id1 = ms.ms_id, rg_id = rg_id))
        json['aa'], json['ma'] = res.fetchone ()

        # Get the affinity of the manuscript to MT
        #
        # For a description of mt and mtp see the comment in
        # ActsMsListValPh3.pl and
        # http://intf.uni-muenster.de/cbgm/actsPh3/guide_en.html#Ancestors

        json['mt'], json['mtp'] = 0.0, 0.0
        res = execute (conn, """
        SELECT a.affinity as mt, a.equal::float / c.length as mtp
        FROM affinity a
        JOIN ms_ranges c
          ON (a.ms_id1, a.rg_id) = (c.ms_id, c.rg_id)
        WHERE a.ms_id1 = :ms_id1 AND a.ms_id2 = 2 AND a.rg_id = :rg_id
        """, dict (parameters, ms_id1 = ms.ms_id, rg_id = rg_id))
        if res.rowcount > 0:
            json['mt'], json['mtp'] = res.fetchone ()

        return make_json_response (json)
Пример #2
0
def cliques_json(passage_or_id):
    """ Endpoint.  Serve all cliques found in a passage.

    :param string passage_or_id: The passage id.

    """

    auth()

    with current_app.config.dba.engine.begin() as conn:
        passage = Passage(conn, passage_or_id)
        return make_json_response(passage.cliques())
Пример #3
0
Файл: main.py Проект: cceh/ntg
def cliques_json (passage_or_id):
    """ Endpoint.  Serve all cliques found in a passage.

    :param string passage_or_id: The passage id.

    """

    auth ()

    with current_app.config.dba.engine.begin () as conn:
        passage = Passage (conn, passage_or_id)
        return make_json_response (passage.cliques ())
Пример #4
0
def notes_json(range_id):
    """Endpoint.  Get a list of all editor notes."""

    private_auth()

    with current_app.config.dba.engine.begin() as conn:
        res = execute(
            conn, """
        SELECT pass_id, begadr, endadr, note
        FROM passages_view p
        JOIN ranges rg
          ON (rg.passage @> p.passage)
        JOIN notes
          USING (pass_id)
        WHERE rg.rg_id = :range_id
        ORDER BY pass_id
        """, dict(parameters, range_id=range_id))

        Notes = collections.namedtuple('Notes',
                                       'pass_id, begadr, endadr, note')
        notes = []
        for r in res:
            note = Notes._make(r)._asdict()
            note['hr'] = Passage.static_to_hr(note['begadr'], note['endadr'])
            notes.append(note)

        return make_json_response(notes)
Пример #5
0
def stemma(passage_or_id):
    """Serve a local stemma in dot format.

    A local stemma is a DAG (directed acyclic graph).  The layout of the DAG is
    precomputed on the server using GraphViz.  GraphViz adds a precomputed
    position to each node and a precomputed bezier path to each edge.

    N.B. I also considered client-side layout of DAGs, but found only 2 viable
    libraries:

    - dagre.  Javascript clone of GraphViz.  Unmaintained.  Buggy.  Does not
      work well with require.js.

    - viz.js.  GraphViz cross-compiled to Javascript with Emscripten.  Huge.
      Promising but still early days.

    Both libraries have their drawbacks so the easiest way out was to precompute
    the layout on the server.

    """

    width = float(request.args.get('width') or 0.0)
    fontsize = float(request.args.get('fontsize') or 10.0)

    with current_app.config.dba.engine.begin() as conn:
        passage = Passage(conn, passage_or_id)
        graph = db_tools.local_stemma_to_nx(conn, passage.pass_id,
                                            user_can_write(current_app))
        dot = helpers.nx_to_dot(graph, width, fontsize, nodesep=0.2)
        return dot
Пример #6
0
def leitzeile_json(passage_or_id):
    """Endpoint.  Serve the leitzeile for the verse containing passage_or_id. """

    auth()

    with current_app.config.dba.engine.begin() as conn:
        passage = Passage(conn, passage_or_id)
        verse_start = (passage.start // 1000) * 1000
        verse_end = verse_start + 999

        res = execute(
            conn, """
        SELECT l.begadr, l.endadr, l.lemma, ARRAY_AGG (p.pass_id)
        FROM nestle l
          LEFT JOIN passages p ON (p.passage @> l.passage)
        WHERE int4range (:start, :end + 1) @> l.passage
        GROUP BY l.begadr, l.endadr, l.lemma

        UNION -- get the insertions

        SELECT p.begadr, p.endadr, '', ARRAY_AGG (p.pass_id)
        FROM passages_view p
        WHERE int4range (:start, :end + 1) @> p.passage AND (begadr % 2) = 1
        GROUP BY p.begadr, p.endadr

        ORDER BY begadr, endadr DESC
        """, dict(parameters, start=verse_start, end=verse_end))

        Leitzeile = collections.namedtuple('Leitzeile',
                                           'begadr, endadr, lemma, pass_ids')
        leitzeile = [Leitzeile._make(r)._asdict() for r in res]

        return make_json_response(leitzeile)
Пример #7
0
def notes_txt(passage_or_id):
    """Read or write the editor notes for a passage

    """

    private_auth()

    with current_app.config.dba.engine.begin() as conn:
        passage = Passage(conn, passage_or_id)

        if request.method == 'PUT':

            edit_auth()
            json = request.get_json()

            res = execute(
                conn, """
            SET LOCAL ntg.user_id = :user_id;
            """, dict(parameters, user_id=flask_login.current_user.id))

            # check for edit conflicts
            res = execute(
                conn, """
            SELECT * FROM notes
            WHERE pass_id = :pass_id AND note != :old_note
            """,
                dict(parameters,
                     pass_id=passage.pass_id,
                     old_note=json['original']))
            for row in res:
                return make_json_response(
                    status=409,
                    message='Cannot save. The note was edited by another user.'
                )

            # save
            res = execute(
                conn, """
            INSERT INTO notes AS n (pass_id, note)
            VALUES (:pass_id, :note)
            ON CONFLICT (pass_id) DO
            UPDATE
            SET note = :note
            WHERE n.pass_id = EXCLUDED.pass_id
            """, dict(parameters, pass_id=passage.pass_id, note=json['note']))

            return make_json_response(message='Note saved.')

        res = execute(
            conn, """
        SELECT note
        FROM notes
        WHERE pass_id = :pass_id
        """, dict(parameters, pass_id=passage.pass_id))

        if res.rowcount > 0:
            return make_text_response(res.fetchone()[0])
        return make_text_response('')
Пример #8
0
def attesting_csv(passage_or_id, labez):
    """ Serve all relatives of all mss. attesting labez at passage. """

    auth()

    with current_app.config.dba.engine.begin() as conn:
        passage = Passage(conn, passage_or_id)

        res = execute(
            conn, """
        SELECT ms_id, hs, hsnr
        FROM apparatus_view
        WHERE pass_id = :pass_id AND labez = :labez
        ORDER BY hsnr
        """, dict(parameters, pass_id=passage.pass_id, labez=labez))

        Attesting = collections.namedtuple('Attesting', 'ms_id hs hsnr')

        return csvify(Attesting._fields, list(map(Attesting._make, res)))
Пример #9
0
def apparatus_json(passage_or_id):
    """ The contents of the apparatus table. """

    auth()

    with current_app.config.dba.engine.begin() as conn:
        passage = Passage(conn, passage_or_id)

        # list of labez => lesart
        res = execute(
            conn, """
        SELECT labez, reading (labez, lesart)
        FROM readings
        WHERE pass_id = :pass_id
        ORDER BY labez
        """, dict(parameters, pass_id=passage.pass_id))

        Readings = collections.namedtuple('Readings', 'labez lesart')
        readings = [Readings._make(r)._asdict() for r in res]

        # list of labez_clique => manuscripts
        res = execute(
            conn, """
        SELECT labez, clique, labez_clique, labezsuf, reading (labez, lesart), ms_id, hs, hsnr, certainty
        FROM apparatus_view_agg
        WHERE pass_id = :pass_id
        ORDER BY hsnr, labez, clique
        """, dict(parameters, pass_id=passage.pass_id))

        Manuscripts = collections.namedtuple(
            'Manuscripts',
            'labez clique labez_clique labezsuf lesart ms_id hs hsnr certainty'
        )
        manuscripts = [Manuscripts._make(r)._asdict() for r in res]

        return make_json_response({
            'readings': readings,
            'manuscripts': manuscripts,
        })

    return 'Error'
Пример #10
0
Файл: main.py Проект: cceh/ntg
def passage_json (passage_or_id = None):
    """Endpoint.  Serve information about a passage.

    Return information about a passage or navigate to it.

    :param string passage_or_id: The passage id.
    :param string siglum:        The siglum of the book to navigate to.
    :param string chapter:       The chapter to navigate to.
    :param string verse:         The verse to navigate to.
    :param string word:          The word (range) to navigate to.
    :param string button:        The button pressed.

    """

    auth ()

    passage_or_id = request.args.get ('pass_id') or passage_or_id or '0'

    siglum  = request.args.get ('siglum')
    chapter = request.args.get ('chapter')
    verse   = request.args.get ('verse')
    word    = request.args.get ('word')
    button  = request.args.get ('button')

    with current_app.config.dba.engine.begin () as conn:
        if siglum and chapter and verse and word and button == 'Go':
            parsed_passage = Passage.parse ("%s %s:%s/%s" % (siglum, chapter, verse, word))
            # log (logging.INFO, parsed_passage)
            passage = Passage (conn, parsed_passage)
            return make_json_response (passage.to_json ())

        if button in ('-1', '1'):
            passage = Passage (conn, passage_or_id)
            passage = Passage (conn, int (passage.pass_id) + int (button))
            return make_json_response (passage.to_json ())

        passage = Passage (conn, passage_or_id)
        return make_json_response (passage.to_json ())
Пример #11
0
Файл: editor.py Проект: cceh/ntg
def notes_json ():
    """Endpoint.  Get a list of all editor notes."""

    if not flask_login.current_user.has_role ('editor'):
        raise PrivilegeError ('You don\'t have editor privilege.')

    with current_app.config.dba.engine.begin () as conn:
        res = execute (conn, """
        SELECT pass_id, begadr, endadr, note
        FROM passages_view
        JOIN notes
          USING (pass_id)
        """, dict (parameters))

        Notes = collections.namedtuple ('Notes', 'pass_id, begadr, endadr, note')
        notes = []
        for r in res:
            note = Notes._make (r)._asdict ()
            note['hr'] = Passage.static_to_hr (note['begadr'], note['endadr'])
            notes.append (note)

        return make_json_response (notes)
Пример #12
0
def attestation_json(passage_or_id):
    """Answer with a list of the attestations of all manuscripts at one specified
    passage."""

    auth()

    with current_app.config.dba.engine.begin() as conn:
        passage = Passage(conn, passage_or_id)

        res = execute(
            conn, """
        SELECT ms_id, labez
        FROM apparatus
        WHERE pass_id = :pass_id
        ORDER BY ms_id
        """, dict(parameters, pass_id=passage.pass_id))

        attestations = {}
        for row in res:
            ms_id, labez = row
            attestations[str(ms_id)] = labez

        return make_json_response({'attestations': attestations})
Пример #13
0
def relatives_csv(passage_or_id, hs_hsnr_id):
    """Output a table of the nearest relatives of a manuscript.

    Output a table of the nearest relatives/ancestors/descendants of a
    manuscript and what they attest.

    """

    auth()

    type_ = request.args.get('type') or 'rel'
    limit = int(request.args.get('limit') or 0)
    labez = request.args.get('labez') or 'all'
    mode = request.args.get('mode') or 'sim'
    include = request.args.getlist('include[]') or []
    fragments = request.args.getlist('fragments[]') or []

    view = 'affinity_view' if mode == 'rec' else 'affinity_p_view'

    where = ''
    if type_ == 'anc':
        where = ' AND older < newer'
    if type_ == 'des':
        where = ' AND older >= newer'

    if labez == 'all':
        where += " AND labez !~ '^z'"
    elif labez == 'all+lac':
        pass
    else:
        where += " AND labez = '%s'" % labez

    if 'fragments' in fragments:
        frag_where = ''
    else:
        frag_where = 'AND aff.common > aff.ms1_length / 2'

    limit = '' if limit == 0 else ' LIMIT %d' % limit

    with current_app.config.dba.engine.begin() as conn:

        passage = Passage(conn, passage_or_id)
        ms = Manuscript(conn, hs_hsnr_id)
        rg_id = passage.request_rg_id(request)

        exclude = get_excluded_ms_ids(conn, include)

        # Get the X most similar manuscripts and their attestations
        res = execute(
            conn, """
        /* get the LIMIT closest ancestors for this node */
        WITH ranks AS (
          SELECT ms_id1, ms_id2,
            rank () OVER (ORDER BY affinity DESC, common, older, newer DESC, ms_id2) AS rank,
            affinity
          FROM {view} aff
          WHERE ms_id1 = :ms_id1 AND aff.rg_id = :rg_id AND ms_id2 NOT IN :exclude
            AND newer > older {frag_where}
          ORDER BY affinity DESC
        )

        SELECT r.rank,
               aff.ms_id2 as ms_id,
               ms.hs,
               ms.hsnr,
               aff.ms2_length,
               aff.common,
               aff.equal,
               aff.older,
               aff.newer,
               aff.unclear,
               aff.common - aff.equal - aff.older - aff.newer - aff.unclear as norel,
               CASE WHEN aff.newer < aff.older THEN ''
                    WHEN aff.newer = aff.older THEN '-'
                    ELSE '>'
               END as direction,
               aff.affinity,
               a.labez,
               a.certainty
        FROM
          {view} aff
        JOIN apparatus_view_agg a
          ON aff.ms_id2 = a.ms_id
        JOIN manuscripts ms
          ON aff.ms_id2 = ms.ms_id
        LEFT JOIN ranks r
          ON r.ms_id2 = aff.ms_id2
        WHERE aff.ms_id2 NOT IN :exclude AND aff.ms_id1 = :ms_id1
              AND aff.rg_id = :rg_id AND aff.common > 0
              AND a.pass_id = :pass_id {where} {frag_where}
        ORDER BY affinity DESC, r.rank, newer DESC, older DESC, hsnr
        {limit}
        """,
            dict(parameters,
                 where=where,
                 frag_where=frag_where,
                 ms_id1=ms.ms_id,
                 hsnr=ms.hsnr,
                 pass_id=passage.pass_id,
                 rg_id=rg_id,
                 limit=limit,
                 view=view,
                 exclude=exclude))

        Relatives = collections.namedtuple(
            'Relatives',
            'rank ms_id hs hsnr length common equal older newer unclear norel direction affinity labez certainty'
        )
        return csvify(Relatives._fields, list(map(Relatives._make, res)))
Пример #14
0
def manuscript_full_json(passage_or_id, hs_hsnr_id):
    """Endpoint.  Serve information about a manuscript.

    :param string hs_hsnr_id: The hs, hsnr or id of the manuscript.

    """

    auth()

    hs_hsnr_id = request.args.get('ms_id') or hs_hsnr_id

    with current_app.config.dba.engine.begin() as conn:
        passage = Passage(conn, passage_or_id)
        ms = Manuscript(conn, hs_hsnr_id)
        rg_id = passage.request_rg_id(request)

        if ms.ms_id is None:
            return cache(
                make_json_response(None, 400,
                                   'Bad request: No such manuscript.'))

        json = ms.to_json()
        json['length'] = ms.get_length(rg_id)

        # Get the attestation(s) of the manuscript (may be uncertain eg. a/b/c)
        res = execute(
            conn, """
        SELECT labez, clique, labez_clique, certainty
        FROM apparatus_view_agg
        WHERE ms_id = :ms_id AND pass_id = :pass_id
        """, dict(parameters, ms_id=ms.ms_id, pass_id=passage.pass_id))

        row = res.fetchone()
        if row is not None:
            json['labez'], json['clique'], json['labez_clique'], json[
                'certainty'] = row

        # Get the affinity of the manuscript to all manuscripts
        res = execute(
            conn, """
        SELECT avg (a.affinity) as aa,
        percentile_cont(0.5) WITHIN GROUP (ORDER BY a.affinity) as ma
        FROM affinity a
        WHERE a.ms_id1 = :ms_id1 AND a.rg_id = :rg_id
        """, dict(parameters, ms_id1=ms.ms_id, rg_id=rg_id))

        json['aa'], json['ma'] = 0.0, 0.0
        row = res.fetchone()
        if row is not None:
            json['aa'], json['ma'] = row

        # Get the affinity of the manuscript to MT
        #
        # For a description of mt and mtp see the comment in
        # ActsMsListValPh3.pl and
        # http://intf.uni-muenster.de/cbgm/actsPh3/guide_en.html#Ancestors

        res = execute(
            conn, """
        SELECT a.affinity as mt, a.equal::float / c.length as mtp
        FROM affinity a
        JOIN ms_ranges c
          ON (a.ms_id1, a.rg_id) = (c.ms_id, c.rg_id)
        WHERE a.ms_id1 = :ms_id1 AND a.ms_id2 = 2 AND a.rg_id = :rg_id
        """, dict(parameters, ms_id1=ms.ms_id, rg_id=rg_id))

        json['mt'], json['mtp'] = 0.0, 0.0
        row = res.fetchone()
        if row is not None:
            json['mt'], json['mtp'] = row

        return cache(make_json_response(json))
Пример #15
0
 def pass_hr (self):
     return Passage.static_to_hr (self.begadr, self.endadr)
Пример #16
0
def congruence_list(conn, passage, range_id):
    """Check the congruence.

    "Das Prüfprogramm soll eine Inkongruenz anzeigen, wenn der Zeuge einer Lesart
    x, die im lokalen Stemma von y abhängt UND (bei x keinen pV mit Conn <= 5
    hat ODER bei y keinen pV mit höherem Rang hat als ein weiterer pV bei einer
    anderen Variante), nicht mit x ODER x(n) der Quelle "?" zugeordnet wird."
    -- email K. Wachtel 16.01.2020

    Wenn Lesart x im lokalen Stemma von y != ? abhängt, muß jeder Zeuge der
    Lesart x:

    1. einen pV(conn=5) der Lesart x haben, oder

    2. der höchste pV(!= zz) die Lesart y haben.

    Wenn Lesart x im lokalen Stemma von ? abhängt, ist keine Aussage möglich.

    """

    res = execute(
        conn, """
    -- get the closest ancestors ms1 for every manuscript ms2
    WITH ranked AS (
      SELECT
        ms_id1,
        ms_id2,
        rank () OVER (PARTITION BY ms_id2 ORDER BY affinity DESC, common, older, newer DESC, ms_id1) AS rank,
        affinity
      FROM affinity_p_view aff
      WHERE ms_id1 NOT IN :exclude
        AND ms_id2 NOT IN :exclude
        AND aff.rg_id = :rg_id
        AND aff.newer < aff.older
        AND aff.common > aff.ms2_length / 2
      ORDER BY ms_id2, affinity DESC
    ),

    -- get readings
    readings AS (
      SELECT
        p.pass_id,
        p.begadr,
        p.endadr,
        r.ms_id1,
        r.ms_id2,
        r.rank,
        q1.labez AS labez1,
        q2.labez AS labez2,
        q1.clique AS clique1,
        q2.clique AS clique2,
        labez_clique (q1.labez, q1.clique) as lq1,
        labez_clique (q2.labez, q2.clique) as lq2,
        l.source_labez  as source_l2,
        l.source_clique as source_q2,
        labez_clique (l.source_labez, l.source_clique) as source_lq2,
        row_number () OVER (PARTITION BY p.pass_id, r.ms_id2 ORDER BY r.rank) as row_no,
        count (*) FILTER (WHERE q1.labez !~ '^z') OVER (
           PARTITION BY p.pass_id, r.ms_id2
           ORDER BY r.rank
           ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) as row_no_no_zz
      FROM passages p
        JOIN ranges rg ON (rg.rg_id = :range_id AND rg.passage @> p.passage)
      CROSS JOIN ranked r
        JOIN apparatus_cliques_view q1 ON q1.ms_id = r.ms_id1 AND q1.pass_id = p.pass_id
        JOIN apparatus_cliques_view q2 ON q2.ms_id = r.ms_id2 AND q2.pass_id = p.pass_id
        JOIN locstem l ON (l.pass_id, l.labez, l.clique) = (q2.pass_id, q2.labez, q2.clique)
      WHERE q1.certainty = 1.0
        AND q2.certainty = 1.0
        AND r.rank <= 2 * :connectivity -- speed things up
      ORDER BY pass_id, ms_id2, rank
    )

    -- output mss that fail both rules
    SELECT
      r.pass_id, r.begadr, r.endadr, ms1.hs AS hs1, ms2.hs AS hs2, r.ms_id1, r.ms_id2, r.lq1, r.lq2, r.rank
    FROM readings r
      JOIN manuscripts ms1 ON ms1.ms_id = r.ms_id1
      JOIN manuscripts ms2 ON ms2.ms_id = r.ms_id2
    WHERE r.lq2       != lq1
      AND r.labez1    !~ '^z'
      AND r.labez2    !~ '^z'
      AND r.source_l2 != '?'
      AND r.row_no    = 1
      -- ancestor ms1 reads different from descendant ms2
      AND -- ms2 fails rule 1 (muß einen pV(conn=5) der Lesart x haben)
        NOT EXISTS (
          SELECT 1 FROM readings c
          WHERE c.ms_id2  = r.ms_id2
            AND c.pass_id = r.pass_id
            AND c.lq1     = r.lq2
            AND c.row_no <= :connectivity
        )
      AND -- ms2 fails rule 2 (der höchste pV(!= zz) muß die Lesart y haben)
        NOT EXISTS (
          SELECT 1 FROM readings c
          WHERE c.ms_id2  = r.ms_id2
            AND c.pass_id = r.pass_id
            AND c.lq1     = r.source_lq2
            AND c.row_no_no_zz = 1
        )

    ORDER BY pass_id, ms1.hsnr
    """,
        dict(
            rg_id=passage.range_id('All'),
            range_id=range_id,
            connectivity=5,
            exclude=(1, 2),
        ))

    Ranks = collections.namedtuple(
        'Ranks',
        'pass_id begadr endadr ms1 ms2 ms_id1 ms_id2 labez1 labez2 rank')
    ranks = []
    for r in res:
        rank = Ranks._make(r)._asdict()
        rank['hr'] = Passage.static_to_hr(rank['begadr'], rank['endadr'])
        ranks.append(rank)

    return ranks
Пример #17
0
def textflow(passage_or_id):
    """ Output a stemma of manuscripts. """

    labez = request.args.get('labez') or ''
    hyp_a = request.args.get('hyp_a') or 'A'
    connectivity = int(request.args.get('connectivity') or 10)
    width = float(request.args.get('width') or 0.0)
    fontsize = float(request.args.get('fontsize') or 10.0)
    mode = request.args.get('mode') or 'sim'

    include = request.args.getlist('include[]') or []
    fragments = request.args.getlist('fragments[]') or []
    checks = request.args.getlist('checks[]') or []
    var_only = request.args.getlist('var_only[]') or []
    cliques = request.args.getlist('cliques[]') or []

    fragments = 'fragments' in fragments
    checks = 'checks' in checks
    var_only = 'var_only' in var_only  # Panel: Coherence at Variant Passages (GraphViz)
    cliques = 'cliques' in cliques  # consider or ignore cliques
    leaf_z = 'Z' in include  # show leaf z nodes in global textflow?

    view = 'affinity_view' if mode == 'rec' else 'affinity_p_view'

    global_textflow = not ((labez != '') or var_only)
    rank_z = False  # include z nodes in ranking?

    if global_textflow:
        connectivity = 1
        rank_z = True
    if connectivity == 21:
        connectivity = 9999

    labez_where = ''
    frag_where = ''
    z_where = ''

    if labez != '':
        labez_where = 'AND app.cbgm AND app.labez = :labez'
        if hyp_a != 'A':
            labez_where = 'AND app.cbgm AND (app.labez = :labez OR (app.ms_id = 1 AND :hyp_a = :labez))'

    if not fragments:
        frag_where = 'AND a.common > a.ms1_length / 2'

    if not rank_z:
        z_where = "AND app.labez !~ '^z' AND app.certainty = 1.0"

    group_field = 'labez_clique' if cliques else 'labez'

    with current_app.config.dba.engine.begin() as conn:
        passage = Passage(conn, passage_or_id)
        rg_id = passage.request_rg_id(request)

        exclude = get_excluded_ms_ids(conn, include)

        # nodes query
        #
        # get all nodes or all nodes (hypothetically) attesting labez

        res = execute(
            conn, """
        SELECT ms_id
        FROM apparatus app
        WHERE pass_id = :pass_id AND ms_id NOT IN :exclude {labez_where} {z_where}
        """,
            dict(parameters,
                 exclude=exclude,
                 pass_id=passage.pass_id,
                 labez=labez,
                 hyp_a=hyp_a,
                 labez_where=labez_where,
                 z_where=z_where))

        nodes = {row[0] for row in res}
        if not nodes:
            nodes = {-1}  # avoid SQL syntax error

        # rank query
        #
        # query to get the closest ancestors for every node with rank <= connectivity

        query = """
        SELECT ms_id1, ms_id2, rank
        FROM (
          SELECT ms_id1, ms_id2, rank () OVER (PARTITION BY ms_id1
             ORDER BY affinity DESC, common, older, newer DESC, ms_id2) AS rank
          FROM {view} a
          WHERE ms_id1 IN :nodes AND a.rg_id = :rg_id AND ms_id2 NOT IN :exclude
            AND newer > older {frag_where}
        ) AS r
        WHERE rank <= :connectivity
        ORDER BY rank
        """

        res = execute(
            conn, query,
            dict(parameters,
                 nodes=tuple(nodes),
                 exclude=exclude,
                 rg_id=rg_id,
                 pass_id=passage.pass_id,
                 view=view,
                 labez=labez,
                 connectivity=connectivity,
                 frag_where=frag_where,
                 hyp_a=hyp_a))

        Ranks = collections.namedtuple('Ranks', 'ms_id1 ms_id2 rank')
        ranks = list(map(Ranks._make, res))

        # Initially build an unconnected graph with one node for each
        # manuscript.  We will connect the nodes later.  Finally we will remove
        # unconnected nodes.

        graph = nx.DiGraph()

        dest_nodes = {r.ms_id1 for r in ranks}
        src_nodes = {r.ms_id2 for r in ranks}

        res = execute(
            conn, """
        SELECT ms.ms_id, ms.hs, ms.hsnr, a.labez, a.clique, a.labez_clique, a.certainty
        FROM apparatus_view_agg a
        JOIN manuscripts ms USING (ms_id)
        WHERE pass_id = :pass_id AND ms_id IN :ms_ids
        """,
            dict(parameters,
                 ms_ids=tuple(src_nodes | dest_nodes | nodes),
                 pass_id=passage.pass_id))

        Mss = collections.namedtuple(
            'Mss', 'ms_id hs hsnr labez clique labez_clique certainty')
        mss = list(map(Mss._make, res))

        for ms in mss:
            attrs = {}
            attrs['hs'] = ms.hs
            attrs['hsnr'] = ms.hsnr
            attrs[
                'labez'] = ms.labez if ms.certainty == 1.0 else 'zw ' + ms.labez
            attrs['clique'] = ms.clique
            attrs[
                'labez_clique'] = ms.labez_clique if ms.certainty == 1.0 else 'zw ' + ms.labez_clique
            attrs['ms_id'] = ms.ms_id
            attrs['label'] = ms.hs
            attrs['certainty'] = ms.certainty
            attrs['clickable'] = '1'
            if ms.ms_id == 1 and hyp_a != 'A':
                attrs['labez'] = hyp_a[0]
                attrs['clique'] = ''
                attrs['labez_clique'] = hyp_a[0]
            # FIXME: attrs['shape'] = SHAPES.get (attrs['labez'], SHAPES['a'])
            graph.add_node(ms.ms_id, **attrs)

        # Connect the nodes
        #
        # Step 1: If the node has internal parents, keep only the top-ranked
        # internal parent.
        #
        # Step 2: If the node has no internal parents, keep the top-ranked
        # parents for each external attestation.
        #
        # Assumption: ranks are sorted top-ranked first

        def is_z_node(n):
            labez = n['labez']
            cert = n['certainty']
            return (labez[0] == 'z') or (cert < 1.0)

        tags = set()
        for step in (1, 2):
            for r in ranks:
                a1 = graph.nodes[r.ms_id1]
                if not r.ms_id2 in graph.nodes:
                    continue
                a2 = graph.nodes[r.ms_id2]
                if not (global_textflow) and is_z_node(a2):
                    # disregard zz / zw
                    continue
                if step == 1 and a1[group_field] != a2[group_field]:
                    # differing attestations are handled in step 2
                    continue
                if r.ms_id1 in tags:
                    # an ancestor of this node that lays within the node's
                    # attestation was already seen.  we need not look into other
                    # attestations
                    continue
                if str(r.ms_id1) + a2[group_field] in tags:
                    # an ancestor of this node that lays within this attestation
                    # was already seen.  we need not look into further nodes
                    continue
                # add a new parent
                if r.rank > 1:
                    graph.add_edge(r.ms_id2,
                                   r.ms_id1,
                                   rank=r.rank,
                                   headlabel=r.rank)
                else:
                    graph.add_edge(r.ms_id2, r.ms_id1)

                if a1[group_field] == a2[group_field]:
                    # tag: has ancestor node within the same attestation
                    tags.add(r.ms_id1)
                else:
                    # tag: has ancestor node with this other attestation
                    tags.add(str(r.ms_id1) + a2[group_field])

        if not leaf_z:
            remove_z_leaves(graph)

        # the if clause fixes #83
        graph.remove_nodes_from([
            n for n in nx.isolates(graph) if graph.nodes[n]['labez'] != labez
        ])

        if var_only:
            # Panel: Coherence at Variant Passages (GraphViz)
            #
            # if one predecessor is within the same attestation then remove all
            # other predecessors that are not within the same attestation
            for n in graph:
                within = False
                attestation_n = graph.nodes[n][group_field]
                for p in graph.predecessors(n):
                    if graph.nodes[p][group_field] == attestation_n:
                        within = True
                        break
                if within:
                    for p in graph.predecessors(n):
                        if graph.nodes[p][group_field] != attestation_n:
                            graph.remove_edge(p, n)

            # remove edges between nodes within the same attestation
            for u, v in list(graph.edges()):
                if graph.nodes[u][group_field] == graph.nodes[v][group_field]:
                    graph.remove_edge(u, v)

            # remove now isolated nodes
            graph.remove_nodes_from(list(nx.isolates(graph)))

            # unconstrain backward edges (yields a better GraphViz layout)
            for u, v in graph.edges():
                if graph.nodes[u][group_field] > graph.nodes[v][group_field]:
                    graph.adj[u][v]['constraint'] = 'false'

        else:
            for n in graph:
                # Use a different label if the parent's labez_clique differs from this
                # node's labez_clique.
                pred = list(graph.predecessors(n))
                attrs = graph.nodes[n]
                if not pred:
                    attrs['label'] = "%s: %s" % (attrs['labez_clique'],
                                                 attrs['hs'])
                for p in pred:
                    if attrs['labez_clique'] != graph.nodes[p]['labez_clique']:
                        attrs['label'] = "%s: %s" % (attrs['labez_clique'],
                                                     attrs['hs'])
                        graph.adj[p][n]['style'] = 'dashed'

        if checks:
            for rank in congruence(conn, passage):
                try:
                    graph.adj[rank.ms_id1][rank.ms_id2]['style'] = 'bold'
                except KeyError:
                    pass

    if var_only:
        dot = helpers.nx_to_dot_subgraphs(graph, group_field, width, fontsize)
    else:
        dot = helpers.nx_to_dot(graph, width, fontsize)
    return dot
Пример #18
0
Файл: editor.py Проект: cceh/ntg
def stemma_edit (passage_or_id):
    """Edit a local stemma.

    Called from local-stemma.js (split, merge, move) and textflow.js (move-manuscripts).

    """

    if not flask_login.current_user.has_role ('editor'):
        raise PrivilegeError ('You don\'t have editor privilege.')

    args = request.get_json ()

    action = args.get ('action')

    if action not in ('split', 'merge', 'move', 'move-manuscripts'):
        raise EditError ('Bad request')

    params = { 'original_new' : args.get ('labez_new') == '*' }
    for n in 'labez_old labez_new'.split ():
        params[n] = args.get (n)
        if not RE_VALID_LABEZ.match (params[n]):
            raise EditError ('Bad request')
        if params[n] in ('*', '?'):
            params[n] = None
    for n in 'clique_old clique_new'.split ():
        params[n] = args.get (n)
        if not RE_VALID_CLIQUE.match (params[n]):
            raise EditError ('Bad request')
        if params[n] == '0':
            params[n] = None

    with current_app.config.dba.engine.begin () as conn:
        passage = Passage (conn, passage_or_id)
        params['pass_id'] = passage.pass_id
        params['user_id'] = flask_login.current_user.id

        res = execute (conn, """
        SET LOCAL ntg.user_id = :user_id;
        """, dict (parameters, **params))

        if action == 'move':
            try:
                res = execute (conn, """
                UPDATE locstem
                SET source_labez = :labez_new, source_clique = :clique_new, original = :original_new
                WHERE pass_id = :pass_id AND labez = :labez_old AND clique = :clique_old
                """, dict (parameters, **params))
            except sqlalchemy.exc.IntegrityError as e:
                if 'unique constraint' in str (e):
                    raise EditError (
                        '''Only one original reading allowed. If you want to change the original
                        reading, first remove the old original reading.<br/><br/>''' + str (e)
                    )
                raise EditError (str (e))
            except sqlalchemy.exc.DatabaseError as e:
                raise EditError (str (e))

            # test the still uncommited changes

            graph = db_tools.local_stemma_to_nx (conn, passage.pass_id)

            # test: not a DAG
            if not nx.is_directed_acyclic_graph (graph):
                raise EditError ('The graph is not a DAG anymore.')
            # test: not connected
            graph.add_edge ('*', '?')
            if not nx.is_weakly_connected (graph):
                raise EditError ('The graph is not connected anymore.')
            # test: x derived from x
            for e in graph.edges:
                m0 = RE_EXTRACT_LABEZ.match (e[0])
                m1 = RE_EXTRACT_LABEZ.match (e[1])
                if m0 and m1 and m0.group (1) == m1.group (1):
                    raise EditError (
                        '''A reading cannot be derived from the same reading.
                        If you want to <b>merge</b> instead, use shift + drag.'''
                    )
        elif action == 'split':
            # get the next free clique
            res = execute (conn, """
            SELECT max (clique)
            FROM  cliques
            WHERE pass_id = :pass_id AND labez = :labez_old
            """, dict (parameters, **params))
            params['clique_next'] = str (int (res.fetchone ()[0]) + 1)

            # insert into cliques table
            res = execute (conn, """
            INSERT INTO cliques (pass_id, labez, clique)
            VALUES (:pass_id, :labez_old, :clique_next)
            """, dict (parameters, **params))

            # insert into locstem table with source = '?'
            res = execute (conn, """
            INSERT INTO locstem (pass_id, labez, clique, source_labez, source_clique, original)
            VALUES (:pass_id, :labez_old, :clique_next, NULL, NULL, false)
            """, dict (parameters, **params))

        elif action == 'merge':
            # reassign manuscripts to merged clique
            res = execute (conn, """
            UPDATE ms_cliques
            SET clique = :clique_new
            WHERE (pass_id, labez, clique) = (:pass_id, :labez_old, :clique_old)
            """, dict (parameters, **params))

            # reassign sources to merged clique
            res = execute (conn, """
            UPDATE locstem
            SET source_clique = :clique_new
            WHERE (pass_id, source_labez, source_clique) = (:pass_id, :labez_old, :clique_old)
            """, dict (parameters, **params))

            # remove clique from locstem
            res = execute (conn, """
            DELETE FROM locstem
            WHERE (pass_id, labez, clique) = (:pass_id, :labez_old, :clique_old)
            """, dict (parameters, **params))

            # remove clique from cliques
            res = execute (conn, """
            DELETE FROM cliques
            WHERE (pass_id, labez, clique) = (:pass_id, :labez_old, :clique_old)
            """, dict (parameters, **params))

        elif action == 'move-manuscripts':
            ms_ids = set (args.get ('ms_ids') or [])

            # reassign manuscripts to new clique
            res = execute (conn, """
            UPDATE apparatus_cliques_view
            SET clique = :clique_new
            WHERE (pass_id, labez, clique) = (:pass_id, :labez_old, :clique_old)
              AND ms_id IN :ms_ids
            """, dict (parameters, ms_ids = tuple (ms_ids), **params))

            tools.log (logging.INFO, 'Moved ms_ids: ' + str (ms_ids))

        # return the changed passage
        passage = Passage (conn, passage_or_id)
        return make_json_response (passage.to_json ())

    raise EditError ('Could not edit local stemma.')
Пример #19
0
 def pass_hr (self):
     return Passage.static_to_hr (self.begadr, self.endadr)
Пример #20
0
def stemma_edit(passage_or_id):
    """Edit a local stemma.

    Called from local-stemma.js (split, merge, move) and textflow.js (move-manuscripts).

    """

    edit_auth()

    args = request.get_json()

    action = args.get('action')

    if action not in ('add', 'del', 'split', 'merge', 'move',
                      'move-manuscripts'):
        raise EditError('Bad request')

    params = {}
    for n in 'labez_old labez_new source_labez'.split():
        if n in args:
            params[n] = args.get(n)
            if not RE_VALID_LABEZ.match(params[n]):
                raise EditError('Bad request')
    for n in 'clique_old clique_new source_clique'.split():
        if n in args:
            params[n] = args.get(n)
            if not RE_VALID_CLIQUE.match(params[n]):
                raise EditError('Bad request')

    def integrity_error(e):
        if 'ix_locstem_unique_original' in str(e):
            raise EditError(
                '''Only one original reading allowed. If you want to change the original
                reading, first remove the old original reading.<br/><br/>''' +
                str(e))
        if 'locstem_pkey' in str(e):
            raise EditError(
                '''This readings already dependes on that reading.<br/><br/>'''
                + str(e))
        if 'same_source' in str(e):
            raise EditError(
                '''A reading cannot be derived from the same reading.
                If you want to <b>merge two readings</b>, use shift + drag.''')
        raise EditError(str(e))

    with current_app.config.dba.engine.begin() as conn:
        passage = Passage(conn, passage_or_id)
        params['pass_id'] = passage.pass_id
        params['user_id'] = flask_login.current_user.id

        res = execute(
            conn, """
        SET LOCAL ntg.user_id = :user_id;
        """, dict(parameters, **params))

        if action == 'move':
            # reassign a source reading
            # there may be multiple existent assignments, there'll be only one left
            try:
                res = execute(
                    conn, """
                DELETE FROM locstem
                WHERE (pass_id, labez, clique) = (:pass_id, :labez_old, :clique_old);
                INSERT INTO locstem (pass_id, labez, clique, source_labez, source_clique)
                VALUES (:pass_id, :labez_old, :clique_old, :labez_new, :clique_new)
                """, dict(parameters, **params))
            except sqlalchemy.exc.IntegrityError as e:
                integrity_error(e)
            except sqlalchemy.exc.DatabaseError as e:
                raise EditError(str(e))

        if action == 'del':
            # remove a source reading
            try:
                # check if we are asked to remove the only link,
                # in that case reassign to 'unknown'
                res = execute(
                    conn, """
                SELECT pass_id
                FROM locstem
                WHERE (pass_id, labez, clique) = (:pass_id, :labez_old, :clique_old);
                """, dict(parameters, **params))

                tools.log(logging.INFO, 'Deleting: ' + str(params))

                if res.rowcount > 1:
                    res = execute(
                        conn, """
                    DELETE FROM locstem
                    WHERE (pass_id, labez, clique) = (:pass_id, :labez_old, :clique_old)
                      AND (source_labez, source_clique) = (:source_labez, :source_clique)
                    """, dict(parameters, **params))
                else:
                    res = execute(
                        conn, """
                    UPDATE locstem
                    SET (source_labez, source_clique) = ('?', '1')
                    WHERE (pass_id, labez, clique) = (:pass_id, :labez_old, :clique_old);
                    """, dict(parameters, **params))
            except sqlalchemy.exc.IntegrityError as e:
                integrity_error(e)
            except sqlalchemy.exc.DatabaseError as e:
                raise EditError(str(e))

        if action == 'add':
            # add a source reading
            try:
                res = execute(
                    conn, """
                INSERT INTO locstem (pass_id, labez, clique, source_labez, source_clique)
                VALUES (:pass_id, :labez_old, :clique_old, :labez_new, :clique_new)
                """, dict(parameters, **params))
            except sqlalchemy.exc.IntegrityError as e:
                integrity_error(e)
            except sqlalchemy.exc.DatabaseError as e:
                raise EditError(str(e))

        if action in ('add', 'del', 'move'):
            # test the still uncommitted changes

            graph = db_tools.local_stemma_to_nx(conn, passage.pass_id)

            # test: not a DAG
            if not nx.is_directed_acyclic_graph(graph):
                raise EditError('The new graph contains cycles.')
            # test: not connected
            graph.add_edge('*', '?')
            if not nx.is_weakly_connected(graph):
                raise EditError('The new graph is not connected.')

        elif action == 'split':
            # Get the lowest free integer for the new clique. See: #122
            res = execute(
                conn, """
            SELECT clique
            FROM  cliques
            WHERE pass_id = :pass_id AND labez = :labez_old
            """, dict(parameters, **params))

            taken = set([int(r[0]) for r in res])
            n = 1
            while n in taken:
                n += 1
            params['clique_next'] = str(n)

            # insert into cliques table
            res = execute(
                conn, """
            INSERT INTO cliques (pass_id, labez, clique)
            VALUES (:pass_id, :labez_old, :clique_next)
            """, dict(parameters, **params))

            # insert into locstem table with source = '?'
            res = execute(
                conn, """
            INSERT INTO locstem (pass_id, labez, clique, source_labez, source_clique)
            VALUES (:pass_id, :labez_old, :clique_next, '?', '1')
            """, dict(parameters, **params))

        elif action == 'merge':
            # merge two cliques (eg. b1, b2) into one clique (eg. b1)
            #
            # reassign manuscripts to merged clique
            res = execute(
                conn, """
            UPDATE ms_cliques
            SET clique = :clique_new
            WHERE (pass_id, labez, clique) = (:pass_id, :labez_old, :clique_old)
            """, dict(parameters, **params))

            # reassign sources to merged clique
            res = execute(
                conn, """
            UPDATE locstem
            SET source_clique = :clique_new
            WHERE (pass_id, source_labez, source_clique) = (:pass_id, :labez_old, :clique_old)
            """, dict(parameters, **params))

            # remove clique from locstem
            res = execute(
                conn, """
            DELETE FROM locstem
            WHERE (pass_id, labez, clique) = (:pass_id, :labez_old, :clique_old)
            """, dict(parameters, **params))

            # remove clique from cliques
            res = execute(
                conn, """
            DELETE FROM cliques
            WHERE (pass_id, labez, clique) = (:pass_id, :labez_old, :clique_old)
            """, dict(parameters, **params))

        elif action == 'move-manuscripts':
            # reassign a set of manuscripts to a new clique
            ms_ids = set(args.get('ms_ids') or [])

            res = execute(
                conn, """
            UPDATE apparatus_cliques_view
            SET clique = :clique_new
            WHERE (pass_id, labez, clique) = (:pass_id, :labez_old, :clique_old)
              AND ms_id IN :ms_ids
            """, dict(parameters, ms_ids=tuple(ms_ids), **params))

            tools.log(logging.INFO, 'Moved ms_ids: ' + str(ms_ids))

        # return the changed passage
        passage = Passage(conn, passage_or_id)
        return make_json_response(passage.to_json())

    raise EditError('Could not edit local stemma.')
Пример #21
0
def congruence_list_json(range_id):
    """ Endpoint: check the congruence """

    with current_app.config.dba.engine.begin() as conn:
        passage = Passage(conn, 1)
        return make_json_response(congruence_list(conn, passage, range_id))
Пример #22
0
Файл: main.py Проект: cceh/ntg
def relatives_csv (passage_or_id, hs_hsnr_id):
    """Output a table of the nearest relatives of a manuscript.

    Output a table of the nearest relatives/ancestors/descendants of a
    manuscript and what they attest.

    """

    auth ()

    type_     = request.args.get ('type') or 'rel'
    chapter   = request.args.get ('range') or 'All'
    limit     = int (request.args.get ('limit') or 0)
    labez     = request.args.get ('labez') or 'all'
    mode      = request.args.get ('mode') or 'sim'
    include   = request.args.getlist ('include[]') or []
    fragments = request.args.getlist ('fragments[]') or []

    view = 'affinity_view' if mode == 'rec' else 'affinity_p_view'

    where = ''
    if type_ == 'anc':
        where =  ' AND older < newer'
    if type_ == 'des':
        where =  ' AND older >= newer'

    if labez == 'all':
        where += " AND labez !~ '^z'"
    elif labez == 'all+lac':
        pass
    else:
        where += " AND labez = '%s'" % labez

    if 'fragments' in fragments:
        frag_where = ''
    else:
        frag_where = 'AND aff.common > aff.ms1_length / 2'

    limit = '' if limit == 0 else ' LIMIT %d' % limit

    with current_app.config.dba.engine.begin () as conn:

        passage   = Passage (conn, passage_or_id)
        ms        = Manuscript (conn, hs_hsnr_id)
        rg_id     = passage.range_id (chapter)

        exclude = get_excluded_ms_ids (conn, include)

        # Get the X most similar manuscripts and their attestations
        res = execute (conn, """
        /* get the LIMIT closest ancestors for this node */
        WITH ranks AS (
          SELECT ms_id1, ms_id2,
            rank () OVER (ORDER BY affinity DESC, common, older, newer DESC, ms_id2) AS rank,
            affinity
          FROM {view} aff
          WHERE ms_id1 = :ms_id1 AND aff.rg_id = :rg_id AND ms_id2 NOT IN :exclude
            AND newer > older {frag_where}
          ORDER BY affinity DESC
        )

        SELECT r.rank,
               aff.ms_id2 as ms_id,
               ms.hs,
               ms.hsnr,
               aff.ms2_length,
               aff.common,
               aff.equal,
               aff.older,
               aff.newer,
               aff.unclear,
               aff.common - aff.equal - aff.older - aff.newer - aff.unclear as norel,
               CASE WHEN aff.newer < aff.older THEN ''
                    WHEN aff.newer = aff.older THEN '-'
                    ELSE '>'
               END as direction,
               aff.affinity,
               a.labez
        FROM
          {view} aff
        JOIN apparatus_view_agg a
          ON aff.ms_id2 = a.ms_id
        JOIN manuscripts ms
          ON aff.ms_id2 = ms.ms_id
        LEFT JOIN ranks r
          ON r.ms_id2 = aff.ms_id2
        WHERE aff.ms_id2 NOT IN :exclude AND aff.ms_id1 = :ms_id1
              AND aff.rg_id = :rg_id AND aff.common > 0
              AND a.pass_id = :pass_id {where} {frag_where}
        ORDER BY affinity DESC, r.rank, newer DESC, older DESC, hsnr
        {limit}
        """, dict (parameters, where = where, frag_where = frag_where,
                   ms_id1 = ms.ms_id, hsnr = ms.hsnr,
                   pass_id = passage.pass_id, rg_id = rg_id, limit = limit,
                   view = view, exclude = exclude))

        Relatives = collections.namedtuple (
            'Relatives',
            'rank ms_id hs hsnr length common equal older newer unclear norel direction affinity labez'
        )
        return csvify (Relatives._fields, list (map (Relatives._make, res)))
Пример #23
0
def passage_json(passage_or_id=None):
    """Endpoint.  Serve information about a passage.

    Return information about a passage or navigate to it.

    :param string passage_or_id: The passage id.
    :param string siglum:        The siglum of the book to navigate to.
    :param string chapter:       The chapter to navigate to.
    :param string verse:         The verse to navigate to.
    :param string word:          The word (range) to navigate to.
    :param string button:        The button pressed.

    """

    auth()

    passage_or_id = request.args.get('pass_id') or passage_or_id or '0'

    siglum = request.args.get('siglum')
    chapter = request.args.get('chapter')
    verse = request.args.get('verse')
    word = request.args.get('word')
    button = request.args.get('button')

    with current_app.config.dba.engine.begin() as conn:
        if siglum and chapter and verse and word and button == 'Go':
            parsed_passage = Passage.parse("%s %s:%s/%s" %
                                           (siglum, chapter, verse, word))
            passage = Passage(conn, parsed_passage)
            return make_json_response(passage.to_json())

        if button in ('-1', '1'):
            passage = Passage(conn, passage_or_id)
            passage = Passage(conn, int(passage.pass_id) + int(button))
            return make_json_response(passage.to_json())

        passage = Passage(conn, passage_or_id)
        return cache(make_json_response(passage.to_json()))
Пример #24
0
 def pass_hr (self):
     """ Add a field with a human-readable passage id. """
     return Passage.static_to_hr (self.begadr, self.endadr)
Пример #25
0
 def pass_hr(self):
     """ Add a field with a human-readable passage id. """
     return Passage.static_to_hr(self.begadr, self.endadr)