Commit 73d6849d authored by Pierre-Yves Gibello's avatar Pierre-Yves Gibello
Browse files

Merge branch 'master' of https://gitlab.ow2.org/ow2/mrl

parents 7da4521d 1f1dc420
Pipeline #10529 passed with stages
in 1 minute and 12 seconds
......@@ -66,10 +66,6 @@
<build>
<finalName>mrl-tree-webapp</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
......
package org.ow2.treewebapp.database;
import org.ow2.treewebapp.rest.attribute.MKTAttribute;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.*;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
public class DatasetFactory {
private static final Logger LOGGER = LoggerFactory.getLogger(DatasetFactory.class);
// Database credentials
static String database = "OW2_MRL", user = "root", password = "password";
public static final String DATABASE_CONFIGURATION_FILENAME = "mysql.properties";
static {
try {
Class.forName("org.mariadb.jdbc.Driver");
Properties props = new Properties();
try {
LOGGER.debug("Loading database connection configuration from: {}", DATABASE_CONFIGURATION_FILENAME);
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
InputStream databaseConfigurationFile = classLoader.getResourceAsStream(DATABASE_CONFIGURATION_FILENAME);
props.load(databaseConfigurationFile);
LOGGER.debug("Properties load from file: {}", props.toString());
String credential = props.getProperty("database");
if(credential != null) DatasetFactory.database = credential.trim();
credential = props.getProperty("user");
if(credential != null) DatasetFactory.user = credential.trim();
credential = props.getProperty("password");
if(credential != null) DatasetFactory.password = credential.trim();
} catch(IOException e) {
LOGGER.error("Not able to load database connection configuration from: {}.", DATABASE_CONFIGURATION_FILENAME, e);
}
} catch (Exception e) {
LOGGER.error("Error in MariaDB configuration reading", e);
}
}
String project;
final HashMap<String, Metric> metrics = new HashMap<>();
public static final String DATABASE_CONFIGURATION_FILENAME = "mysql.properties";
private static final Logger LOGGER = LoggerFactory.getLogger(DatasetFactory.class);
// Database credentials
static String database = "OW2_MRL", user = "root", password = "password";
public DatasetFactory(String project) {
this.project = project;
}
static {
try {
Class.forName("org.mariadb.jdbc.Driver");
Properties props = new Properties();
try {
LOGGER.debug("Loading database connection configuration from: {}", DATABASE_CONFIGURATION_FILENAME);
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
InputStream databaseConfigurationFile = classLoader.getResourceAsStream(DATABASE_CONFIGURATION_FILENAME);
props.load(databaseConfigurationFile);
LOGGER.debug("Properties load from file: {}", props.toString());
String credential = props.getProperty("database");
if (credential != null) DatasetFactory.database = credential.trim();
credential = props.getProperty("user");
if (credential != null) DatasetFactory.user = credential.trim();
credential = props.getProperty("password");
if (credential != null) DatasetFactory.password = credential.trim();
} catch (IOException e) {
LOGGER.error("Not able to load database connection configuration from: {}.", DATABASE_CONFIGURATION_FILENAME, e);
}
} catch (Exception e) {
LOGGER.error("Error in MariaDB configuration reading", e);
}
}
/**
* List projects with data in database, for latest collection date
* @param minData Minimum data count for project
* @param emphasize If null, remove data under count, else emphasize it
* @return list of projects or null if error occurs
*/
public static List<String> listProjects(int minData, String emphasize) {
LinkedList<String> projects = new LinkedList<>();
Connection conn = null;
try {
conn = DatasetFactory.connectDB();
// Get project list
ResultSet rs = conn.createStatement().executeQuery(
"select Project, count(Project) as cnt from RawData where Date="
+ "(select max(Date) from RawData)"
+ " group by Project order by cnt desc, Project");
while(rs.next()) {
int ndata = rs.getInt("cnt");
if(ndata < minData) {
if(emphasize != null) projects.add(rs.getString("Project") + " (" + emphasize + ")");
} else {
projects.add(rs.getString("Project"));
}
}
rs.close();
} catch (SQLException ex) {
// handle any errors
LOGGER.error("Error while listing the projects names. SQLException: {} SQLState: {} VendorError: {}", ex.getMessage(), ex.getSQLState(), ex.getErrorCode());
return null;
} finally {
if(conn != null) try { conn.close(); } catch(Exception ignore) { }
}
return projects;
}
final HashMap<String, Metric> metrics = new HashMap<>();
final String project;
/**
* Overall score is a score out of 10, calculated as follows:
* Round (2 x (mean of all MRL attributes + MKT capabilities))
* For the record, MRL / MKT scores are scores out of 5...
* @return The overall score (out of 10, rounded)
*/
public long getOverallScore() {
int count = 0;
double total = 0D;
// Extract data from MRL tree
// This must be done FIRST because it fills this.metrics table !
TreeNode mrl = getMRLTree();
if(mrl.getChildren() != null) {
for(TreeNode node : mrl.getChildren()) {
total += node.computeValue();
count++;
}
}
String[] mktList = new String[] { "MKT_Community", "MKT_Customers", "MKT_Finances", "MKT_Marketing", "MKT_Product", "MKT_Sales", "MKT_Support" };
for (String s : mktList) {
Metric mkt = this.metrics.get(s);
if (mkt != null) {
total += mkt.getSensorValue();
count++;
}
}
if(total <= 0D) total = 0D;
// Multiply by 2, as average is out of 5, and score out of 10
return Math.max(1L, Math.min(10L, Math.round(2D * total/count)));
}
public DatasetFactory(String project) {
this.project = project;
}
/**
* Retrieves MRL tree
* @return The MRL tree
*/
public TreeNode getMRLTree() {
Connection conn = null;
try {
conn = DatasetFactory.connectDB();
// Get all metric data for project
ResultSet rs = conn.createStatement().executeQuery(
"select m.MetricName as MetricName, Value, Type, Filter, Description, Threshold1, Threshold2, Threshold3, Threshold4, Threshold5, Reverse"
+ " from RawData d, Metric m"
+ " where m.MetricName = d.MetricName and Project='" + this.project + "'"
+ " order by date desc");
metrics.clear();
while(rs.next()) {
Metric m = new Metric(this.project, rs);
//TODO Only latest metric value taken into account: ok for last, what about average is any ?
metrics.putIfAbsent(m.getName(), m);
}
rs.close();
return buildTree(this.project);
} catch (SQLException ex) {
// handle any errors
LOGGER.error("Error while getting MRL tree for project: {}. SQLException: {} SQLState: {} VendorError: {}", this.project, ex.getMessage(), ex.getSQLState(), ex.getErrorCode());
return null;
} finally {
if(conn != null) try { conn.close(); } catch(Exception ignore) { }
}
}
/**
* Retrieves a node given its name
* @param root (sub) tree to search
* @param name Node name
* @return The requested node, null if not found
*/
public TreeNode findNodeByName(TreeNode root, String name) {
if(root.getName().contentEquals(name)) return root;
else {
List<TreeNode> children = root.getChildren();
if(children != null) {
for(TreeNode node : children) {
TreeNode result = findNodeByName(node, name);
if(result != null) return result;
}
}
}
return null;
}
private TreeNode buildTree(String project) {
TreeNode tree = new TreeNode(project);
Connection conn = null;
try {
conn = DatasetFactory.connectDB();
// Get all metric data for project
ResultSet rs = conn.createStatement().executeQuery(
"select AttributeName, a.AggregateName as AggregateName, MetricName"
+ " from Aggregate a, AggregateMetric m"
+ " where m.aggregatename = a.aggregatename"
+ " order by attributename, a.aggregatename");
while(rs.next()) {
tree.addNodes(rs.getString("AttributeName"), rs.getString("AggregateName"), this.metrics.get(rs.getString("MetricName")));
}
rs.close();
} catch (SQLException ex) {
// handle any errors
LOGGER.error("Error while building the tree for project: {}. SQLException: {} SQLState: {} VendorError: {}", project, ex.getMessage(), ex.getSQLState(), ex.getErrorCode());
return null;
} finally {
if(conn != null) try { conn.close(); } catch(Exception ignore) { }
}
return tree;
}
private static Connection connectDB() throws SQLException {
return DriverManager.getConnection("jdbc:mariadb://localhost/"
+ DatasetFactory.database
+ "?user=" + DatasetFactory.user
/**
* List projects with data in database, for latest collection date
*
* @param minData Minimum data count for project
* @param emphasize If null, remove data under count, else emphasize it
* @return list of projects or null if error occurs
*/
public static List<String> listProjects(int minData, String emphasize) {
LinkedList<String> projects = new LinkedList<>();
Connection conn = null;
try {
conn = DatasetFactory.connectDB();
// Get project list
ResultSet rs = conn.createStatement().executeQuery(
"select Project, count(Project) as cnt from RawData where Date="
+ "(select max(Date) from RawData)"
+ " group by Project order by cnt desc, Project");
while (rs.next()) {
int ndata = rs.getInt("cnt");
if (ndata < minData) {
if (emphasize != null) projects.add(rs.getString("Project") + " (" + emphasize + ")");
} else {
projects.add(rs.getString("Project"));
}
}
rs.close();
} catch (SQLException ex) {
// handle any errors
LOGGER.error("Error while listing the projects names. SQLException: {} SQLState: {} VendorError: {}", ex.getMessage(), ex.getSQLState(), ex.getErrorCode());
return null;
} finally {
if (conn != null) try {
conn.close();
} catch (Exception ignore) {
}
}
return projects;
}
private static Connection connectDB() throws SQLException {
return DriverManager.getConnection("jdbc:mariadb://localhost/"
+ DatasetFactory.database
+ "?user=" + DatasetFactory.user
+ "&password=" + DatasetFactory.password);
}
}
/**
* Overall score is a score out of 10, calculated as follows:
* Round (2 x (mean of all MRL attributes + MKT capabilities))
* For the record, MRL / MKT scores are scores out of 5...
*
* @return The overall score (out of 10, rounded)
*/
public long getOverallScore() {
// Multiply by 2, as average is out of 5, and score out of 10
return Math.max(1L, Math.min(10L, Math.round(2D * getNotRoundedOverallScore())));
}
public double getNotRoundedOverallScore() {
int count = 0;
double total = 0D;
// Extract data from MRL tree
// This must be done FIRST because it fills this.metrics table !
TreeNode mrl = getMRLTree();
if (mrl.getChildren() != null) {
for (TreeNode node : mrl.getChildren()) {
total += node.computeValue();
count++;
}
}
for (MKTAttribute attribute : MKTAttribute.values()) {
Metric mkt = this.metrics.get(attribute.getDatabaseMetricName());
if (mkt != null) {
total += mkt.getSensorValue();
count++;
}
}
if (total <= 0D) total = 0D;
return total / count;
}
/**
* Retrieves MRL tree
*
* @return The MRL tree
*/
public TreeNode getMRLTree() {
Connection conn = null;
try {
conn = DatasetFactory.connectDB();
// Get all metric data for project
ResultSet rs = conn.createStatement().executeQuery(
"select m.MetricName as MetricName, Value, Type, Filter, Description, Threshold1, Threshold2, Threshold3, Threshold4, Threshold5, Reverse"
+ " from RawData d, Metric m"
+ " where m.MetricName = d.MetricName and Project='" + this.project + "'"
+ " order by date desc");
metrics.clear();
while (rs.next()) {
Metric m = new Metric(this.project, rs);
//TODO Only latest metric value taken into account: ok for last, what about average is any ?
metrics.putIfAbsent(m.getName(), m);
}
rs.close();
return buildTree(this.project);
} catch (SQLException ex) {
// handle any errors
LOGGER.error("Error while getting MRL tree for project: {}. SQLException: {} SQLState: {} VendorError: {}", this.project, ex.getMessage(), ex.getSQLState(), ex.getErrorCode());
return null;
} finally {
if (conn != null) try {
conn.close();
} catch (Exception ignore) {
}
}
}
public void fetchMKTMetrics() {
try {
Connection connection = DatasetFactory.connectDB();
try (Statement statement = connection.createStatement()) {
ResultSet resultSet = statement.executeQuery("SELECT * FROM (SELECT Project, Date, MetricName, Value, row_number() over(partition by MetricName order by Date desc) AS rn " +
"FROM RawData WHERE Project = 'asm' AND MetricName LIKE 'MKT_%') t WHERE t.rn = 1;");
while (resultSet.next()) {
Metric m = new Metric(project, resultSet.getString("MetricName"), resultSet.getDouble("Value"));
metrics.putIfAbsent(m.getName(), m);
}
} catch (SQLException e) {
LOGGER.error("Error while trying to get MKT metrics", e);
}
} catch (SQLException e) {
LOGGER.error("Error when connecting to the database", e);
}
}
/**
* Retrieves a node given its name
*
* @param root (sub) tree to search
* @param name Node name
* @return The requested node, null if not found
*/
public TreeNode findNodeByName(TreeNode root, String name) {
if (root.getName().contentEquals(name)) return root;
else {
List<TreeNode> children = root.getChildren();
if (children != null) {
for (TreeNode node : children) {
TreeNode result = findNodeByName(node, name);
if (result != null) return result;
}
}
}
return null;
}
private TreeNode buildTree(String project) {
TreeNode tree = new TreeNode(project);
Connection conn = null;
try {
conn = DatasetFactory.connectDB();
// Get all metric data for project
ResultSet rs = conn.createStatement().executeQuery(
"select AttributeName, a.AggregateName as AggregateName, MetricName"
+ " from Aggregate a, AggregateMetric m"
+ " where m.aggregatename = a.aggregatename"
+ " order by attributename, a.aggregatename");
while (rs.next()) {
tree.addNodes(rs.getString("AttributeName"), rs.getString("AggregateName"), this.metrics.get(rs.getString("MetricName")));
}
rs.close();
} catch (SQLException ex) {
// handle any errors
LOGGER.error("Error while building the tree for project: {}. SQLException: {} SQLState: {} VendorError: {}", project, ex.getMessage(), ex.getSQLState(), ex.getErrorCode());
return null;
} finally {
if (conn != null) try {
conn.close();
} catch (Exception ignore) {
}
}
return tree;
}
/*
public static void main(String args[]) throws Exception {
......@@ -227,7 +257,11 @@ public class DatasetFactory {
}
*/
public String getProject() {
return project;
}
public String getProject() {
return project;
}
public HashMap<String, Metric> getMetrics() {
return metrics;
}
}
......@@ -46,6 +46,16 @@ public class Metric extends TreeNode {
else setValue(reverse ? 0 : 5);
}
public Metric(String project, String metricName, double value) {
super(metricName);
this.project = project;
this.project = "";
this.type = "last";
this.reverse = false;
setSensorValue(value);
}
public String getProject() {
return project;
}
......
package org.ow2.treewebapp.rest;
import org.ow2.treewebapp.rest.pojo.MRLNode;
import org.ow2.treewebapp.services.DendrogramService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@RestController
public class DendrogramController {
@Autowired
private DendrogramService dendrogramService;
@GetMapping("dendrogram/{name}")
@CrossOrigin("*")
List<MRLNode> nodes(@PathVariable String name) {
return Collections.singletonList(dendrogramService.getDendrogram(name));
}
}
......@@ -3,6 +3,7 @@ package org.ow2.treewebapp.rest;
import org.ow2.treewebapp.rest.pojo.GraphData;
import org.ow2.treewebapp.services.GraphDataService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
......@@ -14,6 +15,7 @@ public class GraphDataController {
private GraphDataService graphDataService;
@GetMapping("graph-data/{name}")
@CrossOrigin("*")
GraphData one(@PathVariable String name) {
return graphDataService.getGraphData(name);
}
......
package org.ow2.treewebapp.rest.attribute;
import java.util.Arrays;
public enum MKTAttribute {
COMMUNITY("MKT_Community", "Developers and Community", "MKT-2", "Developers and Community"),
CUSTOMERS("MKT_Customers", "User and Customer Base", "MKT-4", "User and Customer Base"),
FINANCES("MKT_Finances", "Financial Stability", "MKT-6", "FInancial Stability"),
MARKETING("MKT_Marketing", "Market Recognition", "MKT-7", "Market Recognition"),
PRODUCT("MKT_Product", "Product Establishment", "MKT-1", "Product Establishment"),
SALES("MKT_Sales", "Sales Activity", "MKT-5", "Sales Activity"),
SUPPORT("MKT_Support", "Professional Support", "MKT-3", "Professional Support");
/**
* Name as used in the database
*/
private final String databaseMetricName;
/**
* Name use when displaying the dendrogram (will be used when generating the JSON that is part of dendrogram API response)
*/
private final String displayName;
/**
* Name as defined in https://projects.ow2.org/edit/ow2/MKTFormClass
*/
private final String name;
/**
* Pretty name as defined in https://projects.ow2.org/edit/ow2/MKTFormClass
*/