IDempiere Assessment Demo

The notebook demonstrates graph based technical debt assessment technique. The code relies on SAApy. The assessment implementation is split into 3 steps:

  1. Assessment environment preparation
  2. Assessment Graph construction using multiple available project iDempiere artifacts
  3. Analysis of the project technical debt based on the constructed graph

Assessment Environment Preparation


In [2]:
import logging
logging.captureWarnings(True) # mostly warnings caused by self-signed certs

# next two liner switchs matplotlib to non-interactive mode
# to stop osx python rocket launcher from jumping in the dock
# ref http://leancrew.com/all-this/2014/01/stopping-the-python-rocketship-icon/
import matplotlib
matplotlib.use("Agg")

import matplotlib.pyplot as plt
# Enable inline plotting
%matplotlib inline

import pandas as pd

from saapy import Workspace, Environment

def setup_env():
    ws = Workspace.from_home("iDempiere", rel_root_dir="Projects", work_dir="iDempiere")
    env = Environment(ws)
    env.setup()
    return env

env = setup_env()

Assessment Graph Construction


In [ ]:
def build_graph(env):
    export_jira_issues(env)
    export_commit_tree(env)
    commits = lookup_refs_from_commits(env)

Technical Debt Analysis


In [10]:
def find_reporting_debt():
    query = """
    MATCH 
        (file:JavaFile)-[:DEFINES]->
        (zk_coupled_class:JavaClass)
        -[zk_couple:COUPLES]->
        (zk_class:JavaClass) 
    WHERE 
        zk_class.name =~ {zk_class_name_exp} AND 
        zk_coupled_class.name =~ {class_name_exp}
    WITH file, zk_coupled_class, zk_couple
    MATCH 
        (file:JavaFile)-[:DEFINES]->
        (zk_coupled_class:JavaClass)
        -[db_couple:COUPLES]->
        (db_class:JavaClass) 
    WHERE db_class.name =~ {db_class_name_exp} 
    RETURN DISTINCT file, zk_couple, db_couple
    """
    zk_class_name_exp = "(?i)^org\.zkoss.*"
    class_name_exp = "(?i).*report.*"
    db_class_name_exp = "(?i)^java\.sql.*"
    yield query, dict(zk_class_name_exp=zk_class_name_exp, 
                      db_class_name_exp=db_class_name_exp, 
                      class_name_exp=class_name_exp)
    
def find_class_names():
    query = """
    MATCH (class:JavaClass)
    RETURN class.name as class_name
    """
    yield query, {}

In [14]:
def analyze():
    env = setup_env()
    reporting_debt = env.neo4j.run_in_tx(find_reporting_debt)

In [ ]: