{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# A simple example on how to use jQAssistant with Python Pandas\n", "I'm a huge fan of the software analysis framework jQAssistant (http://www.jqassistant.org). It's a great tool for scanning and validating various software artifacts (get a glimpse at https://buschmais.github.io/spring-petclinic/). But I also love Python Pandas (http://http://pandas.pydata.org) as a powerful tool in combination with Jupyter notebook (http://http://jupyter.org/) for reproducible Software Analytics (https://en.wikipedia.org/wiki/Software_analytics).\n", "\n", "Combining these tools is near at hand. So I've created a quick demonstration for \"first contact\" :-)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Step 0: Preliminary work\n", "For this quick example, I use the jQAssistant example project (https://www.github.com/buschmais/spring-petclinic/) based on the famous Spring PetClinic project. The authors of jQAssistant added a few validation rules and jQAssistant just works plain simple due to the clever Maven integration. If you want to do the same analysis, just clone the project and execute a mvn clean install. jQAssistant will then scan the software artifacts and store various data about their structure into the embedded graph database Neo4j (https://neo4j.com). After this command, start the neo4j database instance with mvn jqassistant:server. Optional: Check out http://localhost:7474 for directly accessing the Neo4j database." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Step 1: The imports\n", "Nothing spectacular here. We use the py2neo-Neo4j-connector (http://www.http://py2neo.org) for accessing the underlying Neo4j database instance that jQAssistant brings along. Just install the connector with a pip install py2neo. We also import Pandas with a nice short name." ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": true }, "outputs": [], "source": [ "import py2neo\n", "import pandas as pd" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Step 2: Connecting to jQAssistant's embedded neo4j database\n", "The embedded Neo4j installation comes with the standard configuration for port, username, password and an open HTTP port for accessing the database via web services. So there is no need to configure py2neo's connection at all. We just create a Graph object for later usage." ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": false }, "outputs": [], "source": [ "graph = py2neo.Graph()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Step 3: Executing Cypher queries\n", "For this demonstration, we simply list all the methods that are stored in our database (and marked by the label \"Method\"). As an example analysis, we would like to know if our application consists just of getters and setters or some real business methods, too. Our query is written in Neo4j's graph query language Cypher (https://neo4j.com/developer/cypher-query-language/) and returns some values (only the first three are displayed)." ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "[{'a': (d79c8d0:Constructor:Java:Member:Method {cyclomaticComplexity:1,name:\"\",signature:\"void ()\",visibility:\"public\"})},\n", " {'a': (aa25117:Constructor:Java:Member:Method {signature:\"void ()\"})},\n", " {'a': (df9b612:Java:Member:Method {cyclomaticComplexity:1,name:\"getId\",signature:\"java.lang.Integer getId()\",visibility:\"public\"})}]" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "query = \"MATCH (a:Method) RETURN a\"\n", "result = graph.data(query)\n", "result[0:3]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Step 4: Creating a Pandas DataFrame\n", "For the following analysis, we iterate through the dictionary that we'd received from the Neo4j database. We don't need the \"a\" keys that were returned, but only the corresponding values. This is accomplished via Python's list comprehension. We also avoid getting some Nan values in the 'name' column, so we simply drop all empty entries there. We end up with a nice, fully filled DataFrame (only the five rows are displayed)." ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/html": [ "
\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
abstractcyclomaticComplexitynamesignaturestaticsyntheticvisibility
0NaN1.0<init>void <init>()NaNNaNpublic
2NaN1.0getIdjava.lang.Integer getId()NaNNaNpublic
3NaN1.0setIdvoid setId(java.lang.Integer)NaNNaNpublic
4NaN3.0isNewboolean isNew()NaNNaNpublic
9NaN1.0addPetvoid addPet(org.springframework.samples.petcli...NaNNaNpublic
\n", "
" ], "text/plain": [ " abstract cyclomaticComplexity name \\\n", "0 NaN 1.0 \n", "2 NaN 1.0 getId \n", "3 NaN 1.0 setId \n", "4 NaN 3.0 isNew \n", "9 NaN 1.0 addPet \n", "\n", " signature static synthetic \\\n", "0 void () NaN NaN \n", "2 java.lang.Integer getId() NaN NaN \n", "3 void setId(java.lang.Integer) NaN NaN \n", "4 boolean isNew() NaN NaN \n", "9 void addPet(org.springframework.samples.petcli... NaN NaN \n", "\n", " visibility \n", "0 public \n", "2 public \n", "3 public \n", "4 public \n", "9 public " ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df = pd.DataFrame.from_dict([data['a'] for data in result]).dropna(subset=['name'])\n", "df.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Step 5: The analysis\n", "Next we simply work on the \"name\" column to retrieve some information we need for our analysis. In the code, we document our assumptions / heuristics for retrieving the getters and setters (just a subset is displayed for layout reasons)." ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/html": [ "
\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
namesignaturevisibilitymethod_type
47setDescriptionvoid setDescription(java.lang.String)publicSetter
48getPetorg.springframework.samples.petclinic.model.Pe...publicGetter
51onStartupvoid onStartup(javax.servlet.ServletContext)publicBusiness Methods
53registerDandelionServletvoid registerDandelionServlet(javax.servlet.Se...privateBusiness Methods
54createRootApplicationContextorg.springframework.web.context.WebApplication...protectedBusiness Methods
59createServletApplicationContextorg.springframework.web.context.WebApplication...protectedBusiness Methods
61getServletMappingsjava.lang.String[] getServletMappings()protectedGetter
62getServletFiltersjavax.servlet.Filter[] getServletFilters()protectedGetter
66registerServletFilterjavax.servlet.FilterRegistration$Dynamic regis...protectedBusiness Methods
75loadOwnersPetsAndVisitsvoid loadOwnersPetsAndVisits(java.util.List)privateBusiness Methods
\n", "
" ], "text/plain": [ " name \\\n", "47 setDescription \n", "48 getPet \n", "51 onStartup \n", "53 registerDandelionServlet \n", "54 createRootApplicationContext \n", "59 createServletApplicationContext \n", "61 getServletMappings \n", "62 getServletFilters \n", "66 registerServletFilter \n", "75 loadOwnersPetsAndVisits \n", "\n", " signature visibility \\\n", "47 void setDescription(java.lang.String) public \n", "48 org.springframework.samples.petclinic.model.Pe... public \n", "51 void onStartup(javax.servlet.ServletContext) public \n", "53 void registerDandelionServlet(javax.servlet.Se... private \n", "54 org.springframework.web.context.WebApplication... protected \n", "59 org.springframework.web.context.WebApplication... protected \n", "61 java.lang.String[] getServletMappings() protected \n", "62 javax.servlet.Filter[] getServletFilters() protected \n", "66 javax.servlet.FilterRegistration$Dynamic regis... protected \n", "75 void loadOwnersPetsAndVisits(java.util.List) private \n", "\n", " method_type \n", "47 Setter \n", "48 Getter \n", "51 Business Methods \n", "53 Business Methods \n", "54 Business Methods \n", "59 Business Methods \n", "61 Getter \n", "62 Getter \n", "66 Business Methods \n", "75 Business Methods " ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# filter out all the constructor \"methods\"\n", "df = df[df['name'] != \"\"]\n", "# assumption 1: getter start with \"get\"\n", "df.loc[df['name'].str.startswith(\"get\"), \"method_type\"] = \"Getter\"\n", "# assumption 2: \"is\" is just the same as a getter, just for boolean values\n", "df.loc[df['name'].str.startswith(\"is\"), \"method_type\"] = \"Getter\"\n", "# assumption 3: setter start with \"set\"\n", "df.loc[df['name'].str.startswith(\"set\"), \"method_type\"] = \"Setter\"\n", "# assumption 4: all other methods are \"Business Methods\"\n", "df['method_type'] = df['method_type'].fillna('Business Methods')\n", "df[['name', 'signature', 'visibility', 'method_type']][20:30]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Step 6: Preparing the output\n", "Now we group the data by their method type. We simply count the occurence of each entry and take only the 'name' column for further analysis." ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "method_type\n", "Business Methods 146\n", "Getter 33\n", "Setter 27\n", "Name: name, dtype: int64" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "grouped_data = df.groupby('method_type').count()['name']\n", "grouped_data" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Step 7: Visualization\n", "Until now, we could have done most of the work directly in the Neo4j database. But what we want is to create a nice little diagram to display our results. We use matplotlib (http://matplotlib.org) that is integrated with Pandas' DataFrame in a very good way." ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "collapsed": false }, "outputs": [], "source": [ "import matplotlib.pyplot as plt\n", "# some configuration for displaying nice diagrams directly in the notebook\n", "%matplotlib inline\n", "plt.style.use('fivethirtyeight')\n", "# apply additional style for getting a blank background\n", "plt.style.use('seaborn-white')" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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\noyQqeW+mQ1UAAAAASUVORK5CYII=\n", "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# plot a nice business people compatible pie chart\n", "ax = grouped_data.plot(kind='pie', figsize=(5,5), title=\"Business methods or just Getters or Setters?\")\n", "# get rid of the distracting label for the y-axis\n", "ax.set_ylabel(\"\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Summary\n", "This notebook showed a quick demonstration on how to use jQAssistant with Python Pandas within a Jupyter notebook. Granted, we could have done most of our analysis directly with Cypher, but that isn't my main point. The main advantage that comes from the approach here is, that we can do reproducible analysis of software structures based on the notebook approach. Additionally, with Pandas, it's possible to integrate different kind of data sources (relational databases, logfiles, XML files etc.).\n", "\n", "More to come in the future, stay tuned!" ] } ], "metadata": { "anaconda-cloud": {}, "kernelspec": { "display_name": "Python [Root]", "language": "python", "name": "Python [Root]" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.5.2" } }, "nbformat": 4, "nbformat_minor": 0 }