Пример #1
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 ())
Пример #2
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()))
Пример #3
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.')
Пример #4
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.')