예제 #1
0
def get_diagrams(project_id, token):
    """
    Synopsis:
        Collects and returns the diagram IDs contained in a project.
    Status Codes:
        200: successful
        400: password or token count not found in request
        403: password invalid
        404: project not found in database
        410: project is empty
    :param project_id: unique project ID
    :param token: user authentification token
    :returns response: JSON-object containing
        1. list of (name, ID) tuples
    """
    if not is_int_castable(project_id):
        abort(400)  # Bad request

    if not token_is_valid(project_id, token):
        abort(403)  # Forbidden

    if not db.check_project_exists(project_id=project_id):
        abort(404)  # Not found

    mdjs = db.get_project_models(project_id=project_id)
    if len(mdjs) < 1:
        abort(410)  # Gone

    parsed = parser.parse_models(mdj_array=mdjs)

    return jsonify({
        'dia_name_id_pairs':
        list(set(anal.get_diagram_names_and_ids(parsed)))
    }), 200  # OK
예제 #2
0
def get_recent_changes(project_id, from_rev, to_rev, token, diagram_id):
    """
    Synopsis:
        Returns a textual diff-log of the given project, restricted to objects in the given diagram.
        This function claims the *db.lock* system lock.
    Status Codes:
        400: invalid project ID
        403: password invalid
        404: project not found in database OR diagram ID not found in project
        410: project is empty
    :param project_id: unique project ID
    :param from_rev: revision index to start changelog
    :param to_rev: revision index to end changelog
    :param token: user authentification token
    :param diagram_id: ID of diagram restricting which objects are to be rendered
    :returns log: string
    """
    if not is_int_castable(project_id):
        abort(400)  # Bad request

    if not token_is_valid(project_id, token):
        abort(403)  # Forbidden

    if not db.check_project_exists(project_id=project_id):
        abort(404)  # Not found

    mdjs = db.get_project_models(project_id=project_id)
    if not mdjs:
        abort(410)  # Gone

    parsed = parser.parse_models(mdj_array=mdjs)
    if not parsed:
        abort(500)  # Internal Server Error

    diagram_name_id_pairs = list(set(anal.get_diagram_names_and_ids(parsed)))
    if diagram_id not in map(lambda x: x[1], diagram_name_id_pairs):
        abort(404)  # Not found

    timeslice_array, error_flag = anal.get_timeslices(parsed=parsed,
                                                      diagram_id=diagram_id)

    if error_flag:
        abort(500)  # Internal Server Error

    log = gen.generate_log(timeslice_array=timeslice_array,
                           from_version=from_rev,
                           to_version=to_rev)

    return log
예제 #3
0
def _collect_quantity_stats(mdjs, stats):
    """
    Synopsis:
        Collects data about model component quantity.
    :param mdjs: list of mdj file paths
    :param stats: dictionary of stats where the results will be added
    :returns parsed: list of object trees
    :returns timeslice_array: list of timeslices
    """
    parsed = list()
    timeslice_array = list()
    for n in range(1, len(mdjs) + 1):
        if n != 1:  # keep the last .dot file
            os.remove('temp.dot')

        parsed = parser.parse_models(mdj_array=mdjs[:n])
        timeslice_array, error_flag = anal.get_timeslices(parsed=parsed,
                                                          diagram_id='all')
        graphs = anim.render_timeslices(timeslice_array=timeslice_array,
                                        disable_rendering=True)

        with open('temp.dot', 'w') as temp_dot_file:
            for line in graphs[-1].split('\n'):
                if 'style=invis' not in line:
                    temp_dot_file.write(line + '\n')

        cmd = ['gc', '-n', 'temp.dot']
        p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
        nodes = int('0' + p.stdout.read().replace(' ', '').split('G')[0])
        stats['nodes_over_time'].append(nodes)

        cmd = ['gc', '-e', 'temp.dot']
        p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
        edges = int('0' + p.stdout.read().replace(' ', '').split('G')[0])
        stats['edges_over_time'].append(edges)

        stats['model_complexity_over_time'].append(
            min((float(edges) / nodes) /
                (nodes * (nodes - 1) / 2) if nodes > 1 else 0, 1))
    return parsed, timeslice_array
예제 #4
0
def get_history(project_id, frame, token, diagram_id):
    """
    Synopsis:
        Returns a frame of the visualized diagram history of the given project.
        The frame is taken from the project cache. If no cache exists for the given diagram,
        all frames of the history are rendered and composed into a new cache.
        This function claims the *db.lock* and *cache.lock* system locks.
    Status Codes:
        400: invalid project ID
        403: password invalid
        404: project not found in database OR diagram ID not found in project
        410: project is empty
        416: frame index out of bounds
    :param project_id: unique project ID
    :param frame: index of frame to be returned. range: [0 - #revisions]
    :param token: user authentification token
    :param diagram_id: ID of diagram restricting which objects are to be rendered
    :returns file: file, mimetype = image/png
    """
    if not is_int_castable(project_id):
        abort(400)  # Bad request

    if not token_is_valid(project_id, token):
        abort(403)  # Forbidden

    if not db.check_project_exists(project_id=project_id):
        abort(404)  # Not found

    with open('cache.lock', 'a', 0) as cache_lock:
        plocker.lock(cache_lock, plocker.LOCK_EX)
        frame_path = db.get_cache_filepath(frame, project_id, diagram_id)
        if frame_path:
            return send_file(frame_path, mimetype='image/png')

        mdjs = db.get_project_models(project_id=project_id)
        if len(mdjs) == 0:
            abort(410)  # Gone
        if len(mdjs) < frame or frame < 0:
            abort(416)  # Range not acceptable

        parsed = parser.parse_models(mdj_array=mdjs)
        if not parsed:
            abort(500)  # Internal Server Error

        diagram_name_id_pairs = list(
            set(anal.get_diagram_names_and_ids(parsed)))
        if diagram_id not in map(lambda x: x[1], diagram_name_id_pairs):
            abort(404)  # Not found

        timeslice_array, error_flag = anal.get_timeslices(
            parsed=parsed, diagram_id=diagram_id)
        if error_flag:
            abort(500)  # Internal Server Error

        anim.render_timeslices(timeslice_array=timeslice_array)

        db.build_cache(project_id, diagram_id)

        frame_path = db.get_cache_filepath(frame, project_id, diagram_id)
        if frame_path:
            return send_file(frame_path, mimetype='image/png')
        else:
            abort(500)  # Internal Server Error
예제 #5
0
def create_new_revision(project_id, token):
    """
    Synopsis:
        Checks in a new model revision into a given project.
        The model is saved as new file in the database.
        The project statistics are updated.
        This operation invalidates the project cache.
        This function claims the *db.lock* and *cache.lock* system locks.
    Status Codes:
        201: successful
        400: password or token count not found in request
        403: password invalid
        413: provided model file too large
        415: provided file has unsupported type
        422: model does not contain any parsable information
    :param project_id: unique project ID
    :param token: user authentification token
    :returns response: JSON-object containing
        1. new revision ID
        2. project ID
        3. return message
    """
    if request.content_length > MDJ_MAX_SIZE:
        abort(413)  # Payload Too Large

    if not is_int_castable(project_id):
        abort(400)  # Bad request

    if not token_is_valid(project_id, token):
        abort(403)  # Forbidden

    model_file = None
    unicode_filename = ''

    if request.files and request.files['file'] and request.form['filename']:
        model_file = list()
        for line in request.files['file'].stream:
            model_file.append(line)
        escaped_filename = request.form['filename'].encode('utf-8')
        unescaped_filename = escaped_filename.decode('string-escape')
        unicode_filename = unescaped_filename.decode('utf-8')
    else:
        abort(400)  # Bad request

    if model_file is None or not allowed_file(unicode_filename):
        abort(415)  # Unsupported Media Type

    db.invalidate_cache(project_id)

    db.add_revision(project_id='temp',
                    model_file=model_file,
                    filename=unicode_filename)
    new_version_number = -1
    mdjs = db.get_project_models(project_id='temp')

    parsed = parser.parse_models(mdj_array=mdjs)
    db.delete_project('temp')
    if len(anal.get_diagram_names_and_ids(parsed)) == 0:
        abort(422)  # Unprocessable Entity

    else:
        new_version_number = db.add_revision(project_id=project_id,
                                             model_file=model_file,
                                             filename=unicode_filename)

    generate_statistics(project_id)

    return jsonify({
        'new_revision_id':
        new_version_number,
        'project_id':
        project_id,
        'return_msg':
        'The new revision numbered %i has successfully been added to the project '
        'with the ID %s' % (new_version_number, project_id)
    }), 201  # Created