PHP – Child Development Tracking

A very small peice of a web app for tracking child development screenings (ASQ). Live demo here

<?php 
session_start();
$SID = $_SESSION['myid'];
if(!isset($SID) || $SID != session_id()){
	echo 'dill';
	die('');
}
require_once('db.php');
$user_id = $_SESSION['user_id'];
$single_agency_filter = "";
if(isset($_SESSION['agency_id']))
	$single_agency_filter = "and uaa.agency_id = ".$_SESSION['agency_id'];
$wh = "where p.person_type = 'CHI' and p.status <> 'DEL' and exists (select pa.agency_id, uaa.user_id from person_agency_assoc pa join user_agency_assoc uaa on uaa.user_id = $user_id and uaa.agency_id = pa.agency_id $single_agency_filter where pa.person_id = p.person_id) ";
$searchOn = $_REQUEST['_search'];
if($searchOn=='true') {
	require_once('search_op.php');
	//{"groupOp":"AND","rules":[{"field":"app_version","op":"eq","data":"1.6.0.18"},{"field":"update_version","op":"eq","data":"1.6.0.18"}]}
	$sarr = json_decode($_REQUEST['filters']);
	//error_log("Filters: $sarr");
	$grOp = $sarr->{'groupOp'};
	foreach( $sarr->{'rules'} as $filter) {
		$k = $filter->{'field'};
		$v = $filter->{'data'};
		$op = $filter->{'op'};
		//error_log("Filter: $op : $k -> $v ");
		switch ($k) {
			//person_id, first_name, last_name, person_type, person_status, address, city, state, zip_code, language, email, phone_primary, phone_secondary, contact_time, contact_type, child_id, person_id, dob, gender, weeks_premature, presenting_issue, notes, child_person_assoc_id, family_id, person_id, assoc_type, relation
			default:
				$wh .= " $grOp ".insertOp($op,$k,$v);
				
		}
	}
}
$child_multi_ids = $_POST['child_multi_ids'];
if(isset($child_multi_ids)){
	$wh .= " and c.child_id in ( $child_multi_ids ) "; 
}
$assoc_id = $_POST['assoc_id'];
$assoc_type = $_POST['assoc_type'];
$family_id = $_POST['family_id'];
$page = $_POST['page']; // get the requested page
$limit = $_POST['rows']; // get how many rows we want to have into the grid
$sidx = $_POST['sidx']; // get index row - i.e. user click to sort
$sord = $_POST['sord']; // get the direction
if(!$page) $page=1;
if(!$sidx) $sidx =1;
if(!$limit) $limit =100;// detect if here we post the data from allready loaded tree
// we can make here other checks
$assoc_filter = '';
$gb = '';
if(isset($assoc_id) && isset($assoc_type)){
	if($assoc_type == 'FAM'){
		$assoc_filter = " join child_person_assoc cpa on (cpa.child_id = c.child_id or cpa.person_id = c.person_id) and cpa.family_id = $assoc_id ";
		$gb = " group by c.child_id ";
	}
	else if($assoc_type == 'PERSON'){
		$assoc_filter = " join child_person_assoc cpa on cpa.child_id = c.child_id and cpa.person_id = $assoc_id ";
		$gb = " group by c.child_id ";
	}
}
$result = mysql_query("SELECT COUNT(*) AS count FROM person p join children c on c.person_id = p.person_id $assoc_filter $wh $gb");
$row = mysql_fetch_array($result,MYSQL_ASSOC);
$count = $row['count'];
//echo 'count: ' . $count;
if( $count >0 ) {
    $total_pages = ceil($count/$limit);
} else {
    $total_pages = 0;
}
if ($page > $total_pages) $page=$total_pages;
$start = $limit*$page - $limit; // do not put $limit*($page - 1)
if ($start<0) $start = 0;

//person_id, first_name, last_name, person_type, person_status, address, city, state, zip_code, language, email, phone_primary, phone_secondary, contact_time, contact_type, child_id, person_id, dob, gender, weeks_premature, presenting_issue, notes, child_person_assoc_id, family_id, person_id, assoc_type, relation
$audit_subsql = "(SELECT CONCAT(a.time_stamp, '^', a.audit_source, '^', a.audit_id, '^', COALESCE(a.audit_group_id,'')) FROM audit_log a where ((a.audit_table_name = 'person' and a.row_id = p.person_id) or (a.audit_table_name = 'children' and a.row_id = c.child_id)) and a.audit_source <> 'U' and not exists (select a2.audit_log_id from audit_log a2 where a2.undone_id = a.audit_log_id ) order by a.time_stamp desc limit 1) AS audit_fields";
$SQL = "SELECT c.*, p.*, $audit_subsql FROM person p join children c on c.person_id = p.person_id $assoc_filter $wh $ob $gb LIMIT $limit OFFSET $start";
//error_log($SQL);
$result = mysql_query( $SQL ) or die("Couldn't execute query.".mysql_error() . '<br>'.$SQL);
$responce = (object) array('page' => $page, 'total' => $total_pages, 'records' =>$count, 'rows' => "");
$responce->page = $page;
$responce->total = $total_pages;
$responce->records = $count;
$i=0;

while($row = mysql_fetch_array($result,MYSQL_ASSOC)) {
	$audit_fields = preg_split('/\^/', $row['audit_fields']);
	$responce->rows[$i]=$responce->rows[$i]['cell']=array($row['child_id'],$row['person_id'],$row['first_name'],$row['last_name'],$row['dob'],$row['gender'],$row['language'],$row['address'],$row['city'],$row['state'],$row['zip_code'],$row['email'],$row['weeks_premature'],$row['presenting_issue'],$row['family_id'],$row['notes'],$audit_fields[0],$audit_fields[1],$audit_fields[2],$audit_fields[3]);
    $i++;
} 
//echo $json->encode($responce); // coment if php 5
echo json_encode($responce);
unset($_SESSION['app_audit_source']);


?>

Javascript – d3.js

A few years ago I had a lead at work related to creating an app for reading sleep studies, and i threw together a quick test for doing it in the browser. Live demo here

<html>
  <head>
    <title>Focus + Context</title>
    <link type="text/css" rel="stylesheet" href="style.css"/>
    <script type="text/javascript" src="protovis.js"></script>
    <script type="text/javascript" src="zoom.js"></script>
    <script type="text/javascript" src="prototype.js"></script>
    <script type="text/javascript" src="livepipe.js"></script>
    <script type="text/javascript" src="contextmenu.js"></script>
    <style type="text/css">

#fig {
  width: 860px;
  height: 390px;
}

    </style>
  </head>
  <body><div id="center"><div id="fig">
    <div style="text-align:right;padding-right:20;">
      <input id="scale" type="hidden" onchange="vis.render()"
      >
    </div>
    <script type="text/javascript+protovis">


/* Scales and sizing. */
var w = 810,
    h1 = 100,
    h2 = 30,
	h3 = 50,
	h4 = 30,
	h5 = 50,
	h6 = 30,
	h7 = 50,
	h8 = 30,
	h9 = 50,
	h10 = 30,
    x = pv.Scale.linear(start, end).range(0, w),
    y = pv.Scale.linear(0, pv.max(data, function(d) d.y)).range(0, h2);

/* Interaction state. Focus scales will have domain set on-render. */
var i = {x:200, dx:100},
    fx = pv.Scale.linear().range(0, w),
    fy = pv.Scale.linear().range(0, h1),
	fxs = pv.Scale.linear().range(0, w);

/* Interaction state. Focus scales will have domain set on-render. */
var fx2 = pv.Scale.linear().range(0, w),
    fy2 = pv.Scale.linear().range(0, h3),
	fx2s = pv.Scale.linear().range(0, w);

/* Interaction state. Focus scales will have domain set on-render. */
var fx3 = pv.Scale.linear().range(0, w),
    fy3 = pv.Scale.linear().range(0, h5),
	fx3s = pv.Scale.linear().range(0, w);
	

/* Interaction state. Focus scales will have domain set on-render. */
var fx4 = pv.Scale.linear().range(0, w),
    fy4 = pv.Scale.linear().range(0, h7),
	fx4s = pv.Scale.linear().range(0, w);



/* Root panel. */
var vis = new pv.Panel()
    .width(w)
    .height(h1 + 20 + h2 + 20 + h3 + 20 + h4 +20 +h5 + 20 + h6 + 20 + h7 + 20 + h8 + 40)
    .bottom(20)
    .left(30)
    .right(20)
    .top(5);

var vis_context = vis.add(pv.Panel)
	.top(0)
	.height(h2 + 20 + h4 + 20 + h6 + 20 + h8 + 20);

var vis_focus = vis.add(pv.Panel)
	.bottom(0)
	.height(h1 + 20 + h3 + 20 + h5 + 20 + h8 + 20);

/* Focus panel (zoomed in). */
var focus = vis_focus.add(pv.Panel)
    .def("init", function() {
        var d1 = x.invert(i.x),
            d2 = x.invert(i.x + i.dx),
            dd = data.slice(
                Math.max(0, pv.search.index(data, d1, function(d) d.x) - 1),
                pv.search.index(data, d2, function(d) d.x) + 1);
        fx.domain(d1, d2);
        fy.domain(scale.checked ? [0, pv.max(dd, function(d) d.y)] : y.domain());
        return dd;
      })
	.def("init_sel", function() {
        var d1 = x.invert(i.x),
            d2 = x.invert(i.x + i.dx);
			ddsel = data4sel.slice(
                Math.max(0, pv.search.index(data4sel, d1, function(d) d.x) - 200 ),
                Math.max(0,pv.search.index(data4sel, x.invert(i.x + w), function(d) d.x) +200));
		fx4s.domain(d1, d2);
        return ddsel;
      })
    .bottom(0)
    .height(h1);

/* X-axis ticks. */
focus.add(pv.Rule)
    .data(function() fx.ticks())
    .left(fx)
    .strokeStyle("#eee")
  .anchor("bottom").add(pv.Label)
    .text(fx.tickFormat);

/* Y-axis ticks. */
focus.add(pv.Rule)
    .data(function() fy.ticks(7))
    .bottom(fy)
    .strokeStyle(function(d) d ? "#aaa" : "#000")
  .anchor("left").add(pv.Label)
    .text(fy.tickFormat);

/* Focus area chart. */
focus.add(pv.Panel)
    .overflow("hidden")
  .add(pv.Area)
    .data(function() focus.init())
    .left(function(d) fx(d.x))
    .bottom(1)
    .height(function(d) fy(d.y))
    .fillStyle("none")
  .anchor("top").add(pv.Line)
    .fillStyle(null)
    .strokeStyle("steelblue")
    .lineWidth(2);

/* Focus panel2 (zoomed in). ---------------------------*/
var focus2 = vis_focus.add(pv.Panel)
    .def("init", function() {
        var d1 = x.invert(i.x),
            d2 = x.invert(i.x + i.dx),
            dd = data2.slice(
                Math.max(0, pv.search.index(data2, d1, function(d) d.x) - 1),
                pv.search.index(data2, d2, function(d) d.x) + 1);
        fx2.domain(d1, d2);
        fy2.domain(scale.checked ? [0, pv.max(dd, function(d) d.y)] : y.domain());
        return dd;
      })
	.def("init_sel", function() {
        var d1 = x.invert(i.x),
            d2 = x.invert(i.x + i.dx);
			ddsel = data4sel.slice(
                Math.max(0, pv.search.index(data4sel, d1, function(d) d.x) - 200 ),
                Math.max(0,pv.search.index(data4sel, x.invert(i.x + w), function(d) d.x) +200));
		fx4s.domain(d1, d2);
        return ddsel;
      })
    .bottom(h1+20)
    .height(h3);

/* X-axis ticks. */
focus2.add(pv.Rule)
    .data(function() fx2.ticks())
    .left(fx2)
    .strokeStyle("#eee")
  .anchor("bottom").add(pv.Label)
    .text(fx2.tickFormat);

/* Y-axis ticks. */
focus2.add(pv.Rule)
    .data(function() fy2.ticks(7))
    .bottom(fy2)
    .strokeStyle(function(d) d ? "#aaa" : "#000")
  .anchor("left").add(pv.Label)
    .text(fy2.tickFormat);

/* focus2 area chart. */
focus2.add(pv.Panel)
    .overflow("hidden")
  .add(pv.Area)
    .data(function() focus2.init())
    .left(function(d) fx2(d.x))
    .bottom(1)
    .height(function(d) fy2(d.y))
    .fillStyle("none")
  .anchor("top").add(pv.Line)
    .fillStyle(null)
    .strokeStyle("steelblue")
    .lineWidth(2);

/*------------------------------------------------------------*/
var focus3 = vis_focus.add(pv.Panel)
    .def("init", function() {
        var d1 = x.invert(i.x),
            d2 = x.invert(i.x + i.dx),
            dd = data3.slice(
                Math.max(0, pv.search.index(data3, d1, function(d) d.x) - 1),
                pv.search.index(data3, d2, function(d) d.x) + 1);
        fx3.domain(d1, d2);
        fy3.domain(scale.checked ? [0, pv.max(dd, function(d) d.y)] : y.domain());
        return dd;
      })
	.def("init_sel", function() {
        var d1 = x.invert(i.x),
            d2 = x.invert(i.x + i.dx);
			ddsel = data3sel.slice(
                Math.max(0, pv.search.index(data4sel, d1, function(d) d.x) - 200 ),
                Math.max(0,pv.search.index(data4sel, x.invert(i.x + w), function(d) d.x) +200));
		fx3s.domain(d1, d2);
        return ddsel;
      })
    .bottom(h1+20+h3+20)
    .height(h5);

/* X-axis ticks. */
focus3.add(pv.Rule)
    .data(function() fx3.ticks())
    .left(fx3)
    .strokeStyle("#eee")
  .anchor("bottom").add(pv.Label)
    .text(fx3.tickFormat);

/* Y-axis ticks. */
focus3.add(pv.Rule)
    .data(function() fy3.ticks(7))
    .bottom(fy3)
    .strokeStyle(function(d) d ? "#aaa" : "#000")
  .anchor("left").add(pv.Label)
    .text(fy3.tickFormat);

/* focus3 area chart. */
var f3gp = focus3.add(pv.Panel)
f3gp
    .overflow("hidden")
  .add(pv.Area)
    .data(function() focus3.init())
    .left(function(d) fx3(d.x))
    .bottom(1)
    .height(function(d) fy3(d.y))
    .fillStyle("none")
  .anchor("top").add(pv.Line)
    .fillStyle(null)
    .strokeStyle("steelblue")
    .lineWidth(2);

f3gp
  .add(pv.Bar)
    .data(function() focus3.init_sel())
    .left(function(d) fx3s(d.x))
	.width(function(d) (fx3s(d.dx)-fx3s(d.x)))
    .bottom(1)
    .fillStyle(function(d) d.selcolor);

/*----------------focusSelect*/
var focsel3 = {x:0, dx:0};
var sel_width3 = 0;
var sel_start3 = 0;
var beh_sel13 = focus3.add(pv.Panel)
	.data([focsel3])
	.cursor("crosshair")
    .events("all")
    .event("mousedown", pv.Behavior.select());
    
	var sel_bar3 = beh_sel13.event("select", function(dt){
		focus3.render();
		if(sel_start3 == 0){
			sel_start3 = dt.x;
			console.log('sel_start3 ' + sel_start3); 
		}
		sel_width3 = dt.dx;
		console.log('sel_width3 ' + sel_width3);
	})
	.add(pv.Bar);

	sel_bar3.left(function(d) d.x)
    .width(function(d) d.dx)
    .fillStyle("rgba(255, 100, 100, .4)")
    .cursor("move");

	beh_sel13.event("selectend", function(e){
		
			if(sel_width3 != 0){
			var sel_width3_end = sel_width3;
			var sel_start3_end = sel_start3;
			var context_menu_three = new Control.ContextMenu(document.body);  
context_menu_three.addItem({  
    label: 'Snore',  
    callback: function(){
	   data3sel[sel_start3_end] = ({x:fx3s.invert(sel_start3_end), dx:fx3s.invert(sel_start3_end + sel_width3_end), seltyp:'sn', selcolor:"rgba(255, 100, 100, .4)"});  
	   focus3.render();/*console.log('test'+ fx3s.invert(sel_start3_end) + ' ' + fx3s.invert( sel_start3_end + sel_width3_end) + ' -- ' + sel_start3_end + ' ' + sel_width3_end  );*/
       context_menu_three.destroy();   
    }  
});  
context_menu_three.addItem({  
    label: 'Apnia',  
    callback: function(){  
       data3sel[sel_start3_end] = ({x:fx3s.invert(sel_start3_end), dx:fx3s.invert(sel_start3_end + sel_width3_end), seltyp:'ap', selcolor:"rgba(100, 255, 100, .4)"});  
	   focus3.render();
		context_menu_three.destroy();       
    },  
    enabled: true  
});  
context_menu_three.addItem({  
    label: 'Hypapnia',  
    callback: function(){  
       data3sel[sel_start3_end] = ({x:fx3s.invert(sel_start3_end), dx:fx3s.invert(sel_start3_end + sel_width3_end), seltyp:'ha', selcolor:"rgba(100, 100, 255, .4)"});  
	   focus3.render();
		context_menu_three.destroy();      
    },  
    enabled: true  
});  

		}
sel_width3 = 0;
sel_start3 = 0;
focus3.render();
		});
    
/*------------------------------------------------------------*/

/*------------------------------------------------------------*/
var focus4 = vis_focus.add(pv.Panel)
    .def("init", function() {
        var d1 = x.invert(i.x),
            d2 = x.invert(i.x + i.dx),
            dd = data4.slice(
                Math.max(0, pv.search.index(data4, d1, function(d) d.x) - 1),
                pv.search.index(data4, d2, function(d) d.x) + 1);
        fx4.domain(d1, d2);
        fy4.domain(scale.checked ? [0, pv.max(dd, function(d) d.y)] : y.domain());
        return dd;
      })
	 .def("init_sel", function() {
        var d1 = x.invert(i.x),
            d2 = x.invert(i.x + i.dx);
			ddsel = data4sel.slice(
                Math.max(0, pv.search.index(data4sel, d1, function(d) d.x) - 200 ),
                Math.max(0,pv.search.index(data4sel, x.invert(i.x + w), function(d) d.x) +200));
		fx4s.domain(d1, d2);
        return ddsel;
      })
    .bottom(h1+20+h3+20+h5+20)
    .height(h7);

/* X-axis ticks. */
focus4.add(pv.Rule)
    .data(function() fx4.ticks())
    .left(fx4)
    .strokeStyle("#eee")
  .anchor("bottom").add(pv.Label)
    .text(fx4.tickFormat);

/* Y-axis ticks. */
focus4.add(pv.Rule)
    .data(function() fy4.ticks(7))
    .bottom(fy4)
    .strokeStyle(function(d) d ? "#aaa" : "#000")
  .anchor("left").add(pv.Label)
    .text(fy4.tickFormat);

/* focus4 area chart. */
var f4gp = focus4.add(pv.Panel);
f4gp
    .overflow("hidden")
  .add(pv.Area)
    .data(function() focus4.init())
    .left(function(d) fx4(d.x))
    .bottom(1)
    .height(function(d) fy4(d.y))
    .fillStyle("none")
  .anchor("top").add(pv.Line)
    .fillStyle(null)
    .strokeStyle("steelblue")
    .lineWidth(2);

f4gp
  .add(pv.Bar)
    .data(function() focus4.init_sel())
    .left(function(d) fx4s(d.x))
	.width(function(d) (fx4s(d.dx)-fx4s(d.x)))
    .bottom(1)
    .fillStyle(function(d) d.selcolor);
    


/*----------------focusSelect*/
var focsel = {x:0, dx:0};
var sel_width = 0;
var sel_start = 0;
var beh_sel1 = focus4.add(pv.Panel)
	.data([focsel])
	.cursor("crosshair")
    .events("all")
    .event("mousedown", pv.Behavior.select());
    
	var sel_bar = beh_sel1.event("select", function(dt){
		focus4.render();
		if(sel_start == 0){
			sel_start = dt.x;
			console.log('sel_start ' + sel_start); 
		}
		sel_width = dt.dx;
		console.log('sel_width ' + sel_width);
	})
	.add(pv.Bar);

	sel_bar.left(function(d) d.x)
    .width(function(d) d.dx)
    .fillStyle("rgba(255, 100, 100, .4)")
    .cursor("move");

	beh_sel1.event("selectend", function(e){
		
			if(sel_width != 0){
			var sel_width_end = sel_width;
			var sel_start_end = sel_start;
			var context_menu_two = new Control.ContextMenu(document.body);  
context_menu_two.addItem({  
    label: 'Flow Restriction',  
    callback: function(){
	   data4sel[sel_start_end] = ({x:fx4s.invert(sel_start_end), dx:fx4s.invert(sel_start_end + sel_width_end), seltyp:'fl', selcolor:"rgba(255, 100, 100, .4)"});  
	   focus4.render();/*console.log('test'+ fx4s.invert(sel_start_end) + ' ' + fx4s.invert( sel_start_end + sel_width_end) + ' -- ' + sel_start_end + ' ' + sel_width_end  );*/
       context_menu_two.destroy();   
    }  
});  
context_menu_two.addItem({  
    label: 'Apnia',  
    callback: function(){  
       data4sel[sel_start_end] = ({x:fx4s.invert(sel_start_end), dx:fx4s.invert(sel_start_end + sel_width_end), seltyp:'ap', selcolor:"rgba(100, 255, 100, .4)"});  
	   focus4.render();
		context_menu_two.destroy();       
    },  
    enabled: true  
});  
context_menu_two.addItem({  
    label: 'Hypapnia',  
    callback: function(){  
       data4sel[sel_start_end] = ({x:fx4s.invert(sel_start_end), dx:fx4s.invert(sel_start_end + sel_width_end), seltyp:'ha', selcolor:"rgba(100, 100, 255, .4)"});  
	   focus4.render();
		context_menu_two.destroy();      
    },  
    enabled: false  
});  

		}
sel_width = 0;
sel_start = 0;
focus4.render();
		});
 
	


/*------------------------------------------------------------*/

/* Context panel (zoomed out). */
var context = vis_context.add(pv.Panel)
    .top(0)
    .height(h2);

/* X-axis ticks. */
context.add(pv.Rule)
    .data(x.ticks())
    .left(x)
    .strokeStyle("#eee")
  .anchor("bottom").add(pv.Label)
    .text(x.tickFormat);

/* Y-axis ticks. */
context.add(pv.Rule)
    .bottom(0);

/* Context area chart. */
context.add(pv.Area)
    .data(data)
    .left(function(d) x(d.x))
    .bottom(1)
    .height(function(d) y(d.y))
    .fillStyle("none")
  .anchor("top").add(pv.Line)
    .strokeStyle("steelblue")
    .lineWidth(2);

/*-------------------------------------------------*/
var context3 = vis_context.add(pv.Panel)
    .top(h2 + 20 + h4 +20 )
    .height(h6);

/* X-axis ticks. */
context3.add(pv.Rule)
    .data(x.ticks())
    .left(x)
    .strokeStyle("#eee")
  .anchor("bottom").add(pv.Label)
    .text(x.tickFormat);

/* Y-axis ticks. */
context3.add(pv.Rule)
    .bottom(0);

/* context3 area chart. */
context3.add(pv.Area)
    .data(data3)
    .left(function(d) x(d.x))
    .bottom(1)
    .height(function(d) y(d.y))
    .fillStyle("none")
  .anchor("top").add(pv.Line)
    .strokeStyle("steelblue")
    .lineWidth(2);
/*-------------------------------------------------*/

/*-------------------------------------------------*/
var context4 = vis_context.add(pv.Panel)
    .top(h2 + 20 + h4 +20 + h6 + 20)
    .height(h8);

/* X-axis ticks. */
context4.add(pv.Rule)
    .data(x.ticks())
    .left(x)
    .strokeStyle("#eee")
  .anchor("bottom").add(pv.Label)
    .text(x.tickFormat);

/* Y-axis ticks. */
context4.add(pv.Rule)
    .bottom(0);

/* context4 area chart. */
context4.add(pv.Area)
    .data(data4)
    .left(function(d) x(d.x))
    .bottom(1)
    .height(function(d) y(d.y))
    .fillStyle("none")
  .anchor("top").add(pv.Line)
    .strokeStyle("steelblue")
    .lineWidth(2);
/*-------------------------------------------------*/

/* Context panel2 (zoomed out).--------------- */
var context2 = vis_context.add(pv.Panel)
    .top(h4 +20 )
    .height(h4);

/* X-axis ticks. */
context2.add(pv.Rule)
    .data(x.ticks())
    .left(x)
    .strokeStyle("#eee")
  .anchor("bottom").add(pv.Label)
    .text(x.tickFormat);

/* Y-axis ticks. */
context2.add(pv.Rule)
    .bottom(0);

/* context2 area chart. */
context2.add(pv.Area)
    .data(data2)
    .left(function(d) x(d.x))
    .bottom(1)
    .height(function(d) y(d.y))
    .fillStyle("none")
  .anchor("top").add(pv.Line)
    .strokeStyle("steelblue")
    .lineWidth(2);

/* The selectable, draggable focus region. */
var dragpanel2 = vis_context.add(pv.Panel);

var sel_behavior = dragpanel2
	.data([i])
    .cursor("crosshair")
    .events("all")
    .event("mousedown", pv.Behavior.select());
    
	sel_behavior.event("select", function(){
			focus.render();
			focus2.render();
			focus3.render();
			focus4.render();
		});

    

var dragbar2 =  sel_behavior.add(pv.Bar);

var drag_behavior = dragbar2
    .left(function(d) d.x)
    .width(function(d) d.dx)
    .fillStyle("rgba(255, 100, 100, .4)")
    .cursor("move")
    .event("mousedown", pv.Behavior.drag());
    
	drag_behavior.event("drag",  function(){
			focus.render();
			focus2.render();
			focus3.render();	
			focus4.render();	
		});

vis.render();

    </script>
  </div></div></body>
</html>

WebGL – First Responder Tracking

Years ago, one morning I thought it would be fun to port a project I did at UB from C# & OGRE to WebGL. It’s a simulation of tracking first responders. Live demo here

<html>
<head> 
	<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
	<script type="text/javascript" src="js/copperlicht.js"></script>
	<script type="text/javascript" src="js/jquery.min.js"></script>
</head>
<body>
	<div align="center">
		<b>raw</b><br/>
		<canvas id="3darea" width="800" height="600" style="background-color:#000000">
		
		</canvas>
		
		<div style="float:left;"> 
			<select id="responders_select">
				<option value=-1>Origin</option>
				<option value=-2>Orbit</option>
			</select>
		</div>
	</div>
	<script type="text/javascript">
	<!--
	var responderTree = { "responders":[]};
	var dynNodeTree = { "visited":[]};
	var responderCount = 10;
	var responderSelectId = 'responders_select';
	var trackingResponderId = -1;
	var trackedResponderId = -1;
	
	main = function()
	{
		// create the 3d engine
		var engine = new CL3D.CopperLicht('3darea'); 
		engine.load('meshes/fireman_cube_plane_s_white.ccbjs');//startCopperLichtFromFile('3darea', 'meshes/fireman_cube.ccbjs');
		var scene;
		if (!engine.initRenderer())
			return; // this browser doesn't support WebGL
		
		var loopcount = 0;
		var loopcount2 = 0;
		var cameraNodeId;
		var cam;
		var animator;
		var camAnimator2;
		var camModAnimator;
		var doSceneInit = true;
		var animateFunctionsToRun = new Array;
		engine.OnLoadingComplete = function() 
		{
			scene = engine.getScene();
			  if (scene)
			  {
					
				
			  }
		};
			
		
		engine.OnAnimate = function() 
		{
			scene = engine.getScene();
			  if (scene)
			  {
				  if(doSceneInit){
					  doSceneInit = false;
					  // add a new 3d scene
						//engine.gotoSceneByName('cube');
						//var scene = new CL3D.Scene()//engine.getScene();
						//engine.addScene(scene);
						
						//scene.setBackgroundColor(CL3D.createColor(1, 0, 0, 0));
						//var lightnode = new CL3D.LightSceneNode();
						//scene.getRootSceneNode().addChild(lightnode);
						
						// add our own scene node
						//var mynode = new MySceneNode(engine, scene.getSceneNodeFromName('cube_mesh').getMesh().GetMeshBuffers()[0].Mat);
						//scene.getRootSceneNode().addChild(mynode);
						//mynode.addAnimator(new CL3D.AnimatorRotation(new CL3D.Vect3d(0, 0.6, 0.8)));
						
						
						// now, we want to use a custom material for our cube, lets write
						// a vertex and a fragment shader:
						
						  var vertex_shader_source = "\
						   #ifdef GL_ES                    \n\
						   precision highp float;          \n\
						   #endif                          \n\
						   uniform mat4 worldviewproj;     \
						   attribute vec4 vPosition;       \
						   attribute vec4 vNormal;         \
						   attribute vec2 vTexCoord1;      \
						   attribute vec2 vTexCoord2;      \
						   varying vec2 v_texCoord1;       \
						   varying vec2 v_texCoord2;       \
						   void main()                     \
						   {                               \
							gl_Position = worldviewproj * vPosition;\
							v_texCoord1 = vTexCoord1.st;   \
							v_texCoord2 = vTexCoord2.st;   \
						   }";
						   
						  var fragment_shader_source = "\
						   #ifdef GL_ES                    \n\
						   precision highp float;          \n\
						   #endif                          \n\
						   uniform sampler2D texture1;     \
						   uniform sampler2D texture2;     \
							                               \
						   varying vec2 v_texCoord1;       \
						   varying vec2 v_texCoord2;       \
										                   \
						   void main()                     \
						   {                               \
							vec2 texCoord = vec2(v_texCoord1.s, v_texCoord1.t);  \
							gl_FragColor = texture2D(texture1, texCoord) * 2.0;  \
						   }";
						
						// create a solid material using the shaders. For transparent materials, take a look
						// at the other parameters of createMaterialType
						
						var newMaterialType = engine.getRenderer().createMaterialType(vertex_shader_source, fragment_shader_source);
						
						
											
						// add a user controlled camera with a first person shooter style camera controller
						cam = new CL3D.CameraSceneNode();
						cam.Pos.X = 50;
						cam.Pos.Y = 20;
						cameraNodeId = cam.Id;
						
						animator = new CL3D.AnimatorCameraFPS(cam, engine);//CL3D.AnimatorCameraModelViewer(cam, engine);								
						cam.addAnimator(animator);										
						animator.lookAt(new CL3D.Vect3d(0,20,0));	
						//cam.setTarget(new CL3D.Vect3d(0,0,0));
						
						cam2 = new CL3D.CameraSceneNode();
						cam2.Pos.X = 50;
						cam2.Pos.Y = 20;
						
						camAnimator2  = new CL3D.AnimatorCameraModelViewer(cam, engine);								
						camAnimator2.Radius = 4;
						camAnimator2.RotateSpeed = 100;
						//cam2.addAnimator(camAnimator2);
						
						camModAnimator  = new CL3D.AnimatorFlyCircle(new CL3D.Vect3d(0,0,0), 100, 0, 0.01)
						
						scene.getRootSceneNode().addChild(cam);
						scene.setActiveCamera(cam);		
						
						
						
						
						for(var i = 0; i< responderCount; i++){
							addResponder(i, newMaterialType, scene, engine);
						}
						
						visitNode(0,0,0, 0, newMaterialType, scene, engine);
						
				  }
				  else{
					switch(loopcount){ 
				  	
					/*case 0:
						visitNode(1,0,0, 0, null, scene, engine);
				  		break;
					case 2:
						visitNode(2,0,0, 0, null, scene, engine);
						break;
					case 3:
						visitNode(0,1,0, 0, null, scene, engine);
						break;
					case 4:
						visitNode(0,2,0, 0, null, scene, engine);
						break;
					case 5:
						visitNode(0,0,1, 0, null, scene, engine);
						break;
					case 6:
						visitNode(0,0,1, 0, null, scene, engine);
						break;*/
					
					default:
						/*if(loopcount2<=10)
							visitNode(0,0,loopcount2, 0, null, scene, engine);
						else if(loopcount2<=20)
							visitNode(0,(loopcount2-10),10, 0, null, scene, engine);
						else if(loopcount2<=30)
							visitNode((loopcount2-20),10,10, 0, null, scene, engine);
				
						loopcount2++;*/
						
						//animator.animateNode(scene.getSceneNodeFromId(cameraNodeId), loopcount);
						
						
						//if((loopcount%5) == 0){
							 var respid = Math.floor((Math.random()*responderCount));
							for(respid = 0; respid<responderCount; respid++){
								var resper = responderTree.responders[respid];
								var moveby = Math.floor((Math.random()*2));
								var moveDir = Math.floor((Math.random()*5))+1;
								
								var xn = Math.floor(resper.responderNode.Pos.X/2), yn = Math.floor(resper.responderNode.Pos.Y/2)+1, zn = Math.floor(resper.responderNode.Pos.Z/2);
								if(moveDir == 0)
									xn += moveby;
								else if(moveDir == 1)
									xn -= moveby;
								else if(moveDir == 2)
									yn += moveby;
								else if(moveDir == 3)
									yn -= moveby;
								else if(moveDir == 4)
									zn += moveby;
								else if(moveDir == 5)
									zn -= moveby;
								
								visitNode(xn,yn,zn, respid, null, scene, engine);
								if(respid == trackingResponderId){
									var np = new CL3D.Vect3d(2*xn,2*yn,2*zn); 
									//if(trackedResponderId != trackingResponderId)
										animator.lookAt(np);	
									cam.setTarget(np);
									camAnimator2.Radius = 5;
									camAnimator2.animateNode(resper.responderNode, loopcount);//(new Date()).getTime() - scene.getStartTime());
									trackedResponderId = trackingResponderId;
								}
								
									
							}
							if( trackingResponderId == -2){
								camModAnimator.animateNode(cam, loopcount);//(new Date()).getTime() - scene.getStartTime());
								animator.lookAt(new CL3D.Vect3d(0,0,0));
								
							}
						//}
						break;
				  	}				  
					loopcount++;
					animateFunctionsToRun = new Array();
				  }
			  }
		};
	}
	
	
	// helper function for quickly creating a 3d vertex from 3d position and texture coodinates
	createVertex = function(x, y, z, s, t)
	{
		var vtx = new CL3D.Vertex3D(true);
		vtx.Pos.X = x;
		vtx.Pos.Y = y;
		vtx.Pos.Z = z;
		vtx.TCoords.X = s;
		vtx.TCoords.Y = t;
		return vtx;
	}
	
	// our own scene node implementation
	MySceneNode = function(engine, mymat)
	{
		this.init();  // init scene node specific members
		
		// create a 3d mesh with one mesh buffer
		
		this.MyMesh = new CL3D.Mesh();
		var buf = new CL3D.MeshBuffer();
		this.MyMesh.AddMeshBuffer(buf);
		
		// set indices and vertices
							
		buf.Indices = [0,2,3, 2,1,3, 1,0,3, 2,0,1];
		
		buf.Vertices.push(createVertex( 0, 0,  1,   0, 0));
		buf.Vertices.push(createVertex( 0,0, -1,   1, 0));
		buf.Vertices.push(createVertex( 0, 1, 0,    0, 1));
		buf.Vertices.push(createVertex(-1,1,-1,   1, 1));
		
		// set the texture of the material
		
		buf.Mat.setFrom(mymat);
	}
	MySceneNode.prototype = new CL3D.SceneNode(); // derive from SceneNode
	
	MySceneNode.prototype.OnRegisterSceneNode = function(scene)
	{
		scene.registerNodeForRendering(this, CL3D.Scene.RENDER_MODE_DEFAULT);
		CL3D.SceneNode.prototype.OnRegisterSceneNode.call(this, scene); // call base class
	}
	
	MySceneNode.prototype.render = function(renderer)
	{
		renderer.setWorld(this.getAbsoluteTransformation());
		renderer.drawMesh(this.MyMesh);
	}
	
	
	
	// our own scene node implementation
	/*VisitedSceneNode =*/ function createVisitedNode(key, engine, material, scene, priority)
	{
		//this.init();  // init scene node specific members
	

		//engine.load("./meshes/cube.ccbjs");
		var cubenode = scene.getSceneNodeFromName('cube_mesh');//'plane_mesh_side');
		var ret = cubenode.createClone(scene.getRootSceneNode());
		
		/*var newmat = new CL3D.Material();
		newmat.Type = CL3D.Material.EMT_TRANSPARENT_ALPHA_CHANNEL;
		ret.getMesh().GetMeshBuffers()[0].Mat = newmat;
		 var priority = 50;
         var textures = new Array();
 		for(var i = 0; i<=30; i++){
 			textures.push(engine.getTextureManager().getTexture(getTextureUrl(59 + priority, 171, 229, 0.01 * i), true));
 		}
 		for(var i = 30; i>=0; i--){
 			textures.push(engine.getTextureManager().getTexture(getTextureUrl(59 + priority, 171, 229, 0.01 * i), true));
 		}
 		var animtexture = new CL3D.AnimatorAnimateTexture(textures, 100, false);
 		nh.addAnimator(animtexture);
 		animtexture.animateNode(ret, (new Date()).getTime() - scene.getStartTime());
        */
 		
		
		
		ret.Name = key;
		ret.Visible = true;
		var visited = {"positionKey":key,"visitedNode":ret,"visitCount":0};
		cubenode.Visible = false;
		dynNodeTree.visited[key] = visited;
		
		
		
		return ret;
	}
	//VisitedSceneNode.prototype = new CL3D.MeshSceneNode(); // derive from SceneNode
	
	// our own scene node implementation
	/*ResponderSceneNode = */function createResponderNode(responderID, engine, material, scene)
	{
		//this.init();  // init scene node specific members
		
		// create a 3d mesh with one mesh buffer
		
		//engine.load("./meshes/fireman.ccbjs");
		var fireman = scene.getSceneNodeFromName('fireman_mesh');
		//this.MyMesh = fireman.getMesh();
		// set the texture of the material
		var ret = fireman.createClone(scene.getRootSceneNode());
		ret.Visible = true;
	    //ret.Id =  responderID*81;
		ret.Name = "responder_" + responderID.toString();
	    var responder = {"responderID":responderID,"responderNode":ret};
	    fireman.Visible = false;
		responderTree.responders[responderID] = responder;
		$("#"+responderSelectId).append("<option id=\"opt_resp_"+responderID.toString()+"\" value=\""+responderID.toString()+"\">FireFighter:"+responderID.toString()+"</option>")
		
		
		return ret;
		
		//this.MyMesh.getMeshBuffers().Mat.Tex1 = engine.getTextureManager().getTexture("images/default_white.jpg", true);
	}
	
	function getTextureUrl(r, g, b, a){
	    var c=document.getElementById("texture_canvas");
	    var cxt=c.getContext("2d");
	    cxt.fillStyle="rgba("+r+","+g+","+b+","+a+")";
	    cxt.rect (0, 0, 1, 1);
	    cxt.fill();
	    return c.toDataURL("image/png");
	}
	
	//ResponderSceneNode.prototype = new CL3D.MeshSceneNode(); // derive from SceneNode
	/// <summary>
    /// Adds a responder to the scene with the given ID and meterial
    /// </summary>
    /// <param name="responderID">unique ID of the responder (key for data structure)</param>
    /// <param name="Material">name of the material texture script for the 3d engine to apply to the responder</param>
    function addResponder(responderID, Material, scene, engine)
    {
        //send IRMSView to techdemo
      
        //getDisplay(this);
        // create an entity from a model
        //Console.WriteLine("adding responder: " + responderID + " " + Material);
        var respNode = createResponderNode(responderID, engine, Material, scene);//new ResponderSceneNode(responderID, engine, Material, scene);
       
        //respEnt.MaterialName = Material;

       
        // TEXT Display
        

        //var scale = new CL3D.Vect3d(2, 2, 2);
      
       // respNode.Scale = scale;
        //respNode.Position = new CL3D.Vect3d(0,0,0); //or provide values
        //respNode.Orientation = new CL3D.Quaternion(1,1,1,1); //or ...
        //respNode.Scale(Vector3.UnitScale); //or ... 
      
        
 
        //responder.setText(new ObjectTextDisplay(responder.pResponderEntity, camera, responderID.ToString()));
		
        scene.getRootSceneNode().addChild(respNode);
        return respNode;
    }
	
    
  /// <summary>
    /// visitNode attaches a r to a espondernode in the scene
    ///   the node has x, y and z simple positions
    ///     these simple positions will be converted to more
    ///        complicated vectors for the view but we dont need to worry
    ///         about that complication for comparing and such
    /// </summary>
    /// <param name="x"></param>
    /// <param name="y"></param>
    /// <param name="z"></param>
    function visitNode(x, y, z, responderID, Material, scene, engine)
    {
       var pos = new CL3D.Vect3d( x*2,  y*2, z*2 );
        var key = x;
        key = key +'_'+ y;
        key = key +'_'+ z;       
        var nh = null;
        var cube;
        var resp = null;
        var responder = null;
        if (responderTree.responders[responderID])
        {
            responder = /*scene.getSceneNodeFromId(responderID*81);*/responderTree.responders[responderID].responderNode;//.Id);
            resp = (responderTree.responders[responderID]);
            
            
        }
        else
        {
            Console.log("the Responder Could not be found: cannot visit node");
            return;
        }
        if (! dynNodeTree.visited[key])
        {
            // create new node, set its position and add it to 
            //   the data structure that holds all of the nodes
            nh = createVisitedNode(key, engine, Material, scene);//new VisitedSceneNode(key, engine, Material, scene);
            nh.Pos = pos;
            
            dynNodeTree.visited[key].visitCount++;
                  
			scene.getRootSceneNode().addChild(nh);

            
        }
        else
        {
            // if there already is a node in the tree with the current position
            //   get the node from the tree
            dynNodeTree.visited[key].visitCount++;
        }
        // if everything went ok up to this point in the method
            //  the node will not be null and we can set the responder's
            //  node position to the current position
            
                responder.Pos.X = x*2;
                responder.Pos.Y = (y*2)-0.95;
                responder.Pos.Z = z*2;
        								//= new CL3D.Vect3d (   x*2, (y*2)-.95,  z*2);
                responder.updateAbsolutePosition()        
        //Console.WriteLine("visit Node: " + x + " : " + y + " : " + z + " " + cube.ToString());
       
                
    }

	
	main();
	-->
	$("#"+responderSelectId).change(function(event){
		if(event.target.value == 0)
			trackingResponderId = -1;
		else
			trackingResponderId = event.target.value;
	});
	</script>
</body>
</html>

Java – Program the Computer To Program for You

Often it is too tedious to do something by hand. I wrote this Java code once apon a time to generate SQL code and user interface code from field lists

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


public class RegexReplace {

	public static void main(String[] args) {
		//qbxmlRequestIDReplace();
		//qbxmlAccounts();
		
		fieldListToFMX();
		//fieldListToSQLite();
		
		//fieldListToDFMUniQueries("C:\\Users\\Mike\\Desktop\\EM Systems\\TRACS\\UTT_lookup_fields_DB.txt", "C:\\Users\\Mike\\Desktop\\EM Systems\\TRACS\\UTT_DFM_Lookup_Uniqueries.txt");
		//fieldListToDFMUniQueries("C:\\Users\\Mike\\Desktop\\EM Systems\\TRACS\\UTT_fields_DB.txt", "C:\\Users\\Mike\\Desktop\\EM Systems\\TRACS\\UTT_DFM_Uniqueries.txt");
		
		//fieldListToDFMUniQueries("C:\\Users\\Mike\\Desktop\\EM Systems\\TRACS\\UTT_lookup_fields_DB_datasource.txt", "C:\\Users\\Mike\\Desktop\\EM Systems\\TRACS\\UTT_DFM_Lookup_Uniqueries.txt");
		//fieldListToDFMUniDataSources("C:\\Users\\Mike\\Desktop\\EM Systems\\TRACS\\UTT_lookup_fields_DB_datasource.txt", "C:\\Users\\Mike\\Desktop\\EM Systems\\TRACS\\UTT_DFM_Lookup_Unidatasources.txt");
		
		//fieldListToBindingListAndSources();
		
		//lookupFieldListToLookupBindingSources();
		
		//tableListTofilteredSQL();
		
	}

	public static void qbxmlAccounts(){

		BufferedReader reader;
		try {
			String file = "C:\\Users\\Mike\\Desktop\\EM Systems\\Koehler-Gibson\\add_items.xml";
			reader = new BufferedReader(new FileReader(file));
			final StringBuilder contents = new StringBuilder();
		       
		    String line;
		    Pattern p=Pattern.compile("<(\\w+)AccountRef>\\n<ListID>([\\w\\d-]+)</ListID>\\n</\\w+AccountRef>", Pattern.MULTILINE);
	        Matcher m;
	        int count=0;
	        try {
				while(reader.ready()) {
					line = reader.readLine();
					
					contents.append(line).append("\n");
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
	        reader.close();
	        
	        String fcont = contents.toString();
	        HashMap<String, String> accounts = new  HashMap<String, String>();
	        m=p.matcher(fcont);
			while(m.find()) {
		    	count++;
		    	accounts.put(m.group(2), m.group(1));
		    }
	        
	        BufferedWriter writer = null;
	        try
	        {
	            writer = new BufferedWriter( new FileWriter( "C:\\Users\\Mike\\Desktop\\EM Systems\\Koehler-Gibson\\add_items.accounts.txt"));
	            writer.write( accounts.toString());
	        }catch ( IOException e){
	        	e.printStackTrace();
	        }
	        writer.close();
	        
        } catch (FileNotFoundException e1) {
			e1.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	
	}
	
	public static void qbxmlRequestIDReplace(){

		BufferedReader reader;
		try {
			String file = "C:\\Users\\Mike\\Desktop\\EM Systems\\Koehler-Gibson\\add_cust_items.xml";
			reader = new BufferedReader(new FileReader(file));
			final StringBuilder contents = new StringBuilder();
		       
		    String line;
		    Pattern p=Pattern.compile("requestID=\"1\"");
	        Matcher m;
	        int count=0;
	        try {
				while(reader.ready()) {
					line = reader.readLine();
					m=p.matcher(line);
					while(m.find()) {
				    	count++;
				    	line = m.replaceFirst("requestID=\""+count+"\"");
				    	m=p.matcher(line);
				    }
					contents.append(line).append("\n");
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
	        reader.close();
	        
	        BufferedWriter writer = null;
	        try
	        {
	            writer = new BufferedWriter( new FileWriter( file));
	            writer.write( contents.toString());
	        }catch ( IOException e){
	        	e.printStackTrace();
	        }
	        writer.close();
	        
        } catch (FileNotFoundException e1) {
			e1.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	
	}
	
	
	public static void fieldListToFMX(){

		BufferedReader reader;
		try {
			String file = "C:\\Users\\Mike\\Desktop\\EM Systems\\TRACS\\UTT_fields_FMX.txt";
			reader = new BufferedReader(new FileReader(file));
			final StringBuilder contents = new StringBuilder();
		       
		    String line;
		    Pattern groupp=Pattern.compile("\\n([ \\w]+)\\n[\\w\\d _\\n-]+\\n;", Pattern.MULTILINE);
		    Matcher groupm;
	        
		    Pattern p=Pattern.compile("([ \\w]+)-([\\w \\d]+)", Pattern.MULTILINE);
	        Matcher m;
	        int count=0;
	        try {
				while(reader.ready()) {
					line = reader.readLine();
					
					contents.append(line).append("\n");
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
	        reader.close();
	        
	        String fcont = contents.toString();
	        String output = "";
	        String groupname, groupnameClean, ftype, fname, fnameClean, fnameDB, gobj="", oobj = "";
	        int labelWidth, totalWidth, additionalHeight;
	        groupm = groupp.matcher(fcont);
	        ArrayList<String> processedNames = new ArrayList<String>(); 
	        
	        while(groupm.find()) {
	        	totalWidth = 0;
	        	additionalHeight = 0;
	        	groupname = groupm.group(1);
	        	groupnameClean = groupname.replaceAll(" ", "");
	        	gobj = "object gb"+groupnameClean+": TGroupBox\n"+
	        			" Align = Top\n"+
	        			" Padding.Top = 30.000000000000000000\n"+
	        			" Margins.Top = 30.000000000000000000\n"+
	        			" #----height_gb----#\n"+
	        			" Size.Width = 682.000000000000000000\n"+
	        			" Size.PlatformDefault = False\n"+
	        			" Text = \'"+groupname+"\'\n"+
	        			  " TextSettings.Font.Size = 22.000000000000000000\n"+
	        		      " TextSettings.Font.Style = [fsBold]\n"+
	        			//" Size.Width = 682.000000000000000000\n"+
	        			" TabOrder = 0\n"+
	        			" object fl"+groupnameClean+": TFlowLayout\n"+
	        			"  Align = Client\n"+
	        			//"  Size.Height = 257.000000000000000000\n"+
	        			"  #----height_fl----#\n"+
	        			"  Size.PlatformDefault = False\n"+
	        			"  TabOrder = 1\n"+
	        			"  Justify = Left\n"+
	        			"  JustifyLastLine = Left\n"+
	        			"  FlowDirection = LeftToRight\n"+
	        			"  VerticalGap = 10.000000000000000000\n";
	        	m=p.matcher(groupm.group());
	        	while(m.find()) {
			    	count++;
			    	ftype = m.group(1);
			    	fname = m.group(2);
			    	fnameClean = fname.replaceAll(" ", "");
			    	if(processedNames.contains(fnameClean)){
			    		fnameClean = groupnameClean + fnameClean;
			    	}
			    	processedNames.add(fnameClean);
			    	fnameDB = fname.replaceAll(" ", "_").toUpperCase();
			    	labelWidth = fname.length()*10;
			    	totalWidth += 150+labelWidth;
		    		switch(ftype){
				    	case "dbe":
				    		oobj = "object lo"+fnameClean+": TLayout\n"+
				    				//" Position.X = 168.000000000000000000\n"+
				    				//" Position.Y = 260.000000000000000000\n"+
				    				" Size.Width = "+String.valueOf(150+labelWidth)+".000000000000000000\n"+
				    				" Size.Height = 21.000000000000000000\n"+
				    				" Size.PlatformDefault = False\n"+
				    				" TabOrder = 0\n"+
				    				"object lbl"+fnameClean+": TLabel\n"+
				    				" Align = Left\n"+
				    	            " AutoSize = True\n"+
				    				" ClipChildren = True\n"+
				    				" Margins.Left = 20.000000000000000000\n"+
				    	            " Position.X = 20.000000000000000000\n"+
				    	            //" Position.X = 225.000000000000000000\n"+
				    				//" Position.Y = 27.000000000000000000\n"+
				    				//" Size.Width = "+String.valueOf(labelWidth)+".000000000000000000\n"+
				    				" Size.Height = 21.000000000000000000\n"+
				    				" Size.PlatformDefault = False\n"+
				    				" TextSettings.HorzAlign = Trailing\n"+
				    				" TextSettings.WordWrap = False\n"+
				    				" Text = \'"+fname+":\'\n"+
				    				"end\n"+
				    				"object dbe"+fnameClean+": TEdit\n"+
				    				"  Hint = \'"+fname+"\'\n"+
				    				"  Touch.InteractiveGestures = [LongTap, DoubleTap]\n"+
				    				//"  TabOrder = 46\n"+
				    				"  TextSettings.Font.Style = [fsBold]\n"+
				    				" Align = Client\n"+
				    	            //"  Position.X = "+String.valueOf(labelWidth)+".000000000000000000\n"+
				    				//"  Position.Y = 27.000000000000000000\n"+
				    				"  ClipChildren = True\n"+
				    				//"  Size.Width = 150.000000000000000000\n"+
				    				"  Size.Height = 21.000000000000000000\n"+
				    				"  Size.PlatformDefault = False\n"+
				    				//"  TextPrompt = \'"+fname+"\'\n"+
				    				"  StyledSettings = [Family, Size, FontColor]\n"+
				    				"end\n"+
				    				"end\n";
	
				    		break;
				    	case "dblcb":
				    		oobj = "object lo"+fnameClean+": TLayout\n"+
				    				//" Position.X = 168.000000000000000000\n"+
				    				//" Position.Y = 260.000000000000000000\n"+
				    				" Size.Width = "+String.valueOf(150+labelWidth)+".000000000000000000\n"+
				    				" Size.Height = 21.000000000000000000\n"+
				    				" Size.PlatformDefault = False\n"+
				    				" TabOrder = 0\n"+
				    				"object lbl"+fnameClean+": TLabel\n"+
				    				" ClipChildren = True\n"+
				    				//" Position.X = 225.000000000000000000\n"+
				    				//" Position.Y = 27.000000000000000000\n"+
				    				" Size.Width = "+String.valueOf(labelWidth)+".000000000000000000\n"+
				    				" Size.Height = 21.000000000000000000\n"+
				    				" Size.PlatformDefault = False\n"+
				    				" TextSettings.HorzAlign = Trailing\n"+
				    				" TextSettings.WordWrap = False\n"+
				    				" Text = \'"+fname+":\'\n"+
				    				"end\n"+
				    				"object dblcb"+fnameClean+": TwwLookupComboEdit\n"+
				    				" Hint = \'"+fname+"\'\n"+
				    				" Touch.InteractiveGestures = [LongTap, DoubleTap]\n"+
				    				//" TabOrder = 24\n"+
				    				" DropDownGrid.Touch.InteractiveGestures = [Pan]\n"+
				    				" DropDownGrid.DisableGridUpdates = True\n"+
				    				" DropDownGrid.Align = Client\n"+
				    				" DropDownGrid.EnableDragHighlight = False\n"+
				    				" DropDownGrid.Size.Width = 50.000000000000000000\n"+
				    				" DropDownGrid.Size.Height = 50.000000000000000000\n"+
				    				" DropDownGrid.Size.PlatformDefault = False\n"+
				    				" DropDownGrid.TabOrder = 0\n"+
				    				" DropDownGrid.LineAttributes.DataRowLines = False\n"+
				    				" DropDownGrid.LineAttributes.LineOpacity = 1.000000000000000000\n"+
				    				" DropDownGrid.Options = [dgTitles, dgColumnResize, dgColumnReorder, dgTabs, dgRowSelect, dgConfirmDelete, dgCancelOnExit, dgWordWrap]\n"+
				    				" DisableFocusEffect = True\n"+
				    				" DropDownWidth = 0\n"+
				    				" Font.Style = [fsBold]\n"+
				    				" LookupOptions.DialogPositionX = 0\n"+
				    				" LookupOptions.DialogPositionY = 0\n"+
				    				" Position.X = "+String.valueOf(labelWidth)+".000000000000000000\n"+
				    				//" Position.Y = 27.000000000000000000\n"+
				    				" ClipChildren = True\n"+
				    				" Size.Width = 150.000000000000000000\n"+
				    				" Size.Height = 21.000000000000000000\n"+
				    				" Size.PlatformDefault = False\n"+
				    				"end\n"+
				    				"end\n";
				    		break;
				    	case "dbdtp":
				    		oobj = "object lo"+fnameClean+": TLayout\n"+
				    				//" Position.X = 168.000000000000000000\n"+
				    				//" Position.Y = 260.000000000000000000\n"+
				    				" Size.Width = "+String.valueOf(80+labelWidth)+".000000000000000000\n"+
				    				" Size.Height = 21.000000000000000000\n"+
				    				" Size.PlatformDefault = False\n"+
				    				" TabOrder = 0\n"+
				    				"object lbl"+fnameClean+": TLabel\n"+
				    				" ClipChildren = True\n"+
				    				//" Position.X = 149.000000000000000000\n"+
				    				" Size.Width = "+String.valueOf(labelWidth)+".000000000000000000\n"+
				    				" Size.Height = 21.000000000000000000\n"+
				    				" Size.PlatformDefault = False\n"+
				    				" TextSettings.HorzAlign = Trailing\n"+
				    				" TextSettings.WordWrap = False\n"+
				    				" Text = \'"+fname+":\'\n"+
				    				"end\n"+
				    				"object dbdtp"+fnameClean+": TwwCalendarEdit\n"+
				    				" Touch.InteractiveGestures = [LongTap, DoubleTap]\n"+
				    				//" TabOrder = 40\n"+
				    				" Cursor = crIBeam\n"+
				    				" Font.Style = [fsBold]\n"+
				    				" KeyboardType = NumbersAndPunctuation\n"+
				    				//" Text = \'4/7/2016\'\n"+
				    				" Epoch = 0\n"+
				    				//" Date = 42467.000000000000000000\n"+
				    				" Position.X = "+String.valueOf(labelWidth)+".000000000000000000\n"+
				    				" ClipChildren = True\n"+
				    				" Size.Width = 80.000000000000000000\n"+
				    				" Size.Height = 21.000000000000000000\n"+
				    				" Size.PlatformDefault = False\n"+
				    				" DisplayFormat = \'\'\n"+
				    				"end\n"+
				    				"end\n";
				    		break;
				    	case "dbmmo":
				    		additionalHeight += 85;
				    		oobj = "object lo"+fnameClean+": TLayout\n"+
				    				//" Position.X = 168.000000000000000000\n"+
				    				//" Position.Y = 260.000000000000000000\n"+
				    				" Size.Width = "+String.valueOf(500+labelWidth)+".000000000000000000\n"+
				    				" Size.Height = 89.000000000000000000\n"+
				    				" Size.PlatformDefault = False\n"+
				    				" TabOrder = 0\n"+
				    				"object lbl"+fnameClean+": TLabel\n"+
				    				" ClipChildren = True\n"+
				    				//" Position.X = 149.000000000000000000\n"+
				    				" Size.Width = "+String.valueOf(labelWidth)+".000000000000000000\n"+
				    				" Size.Height = 21.000000000000000000\n"+
				    				" Size.PlatformDefault = False\n"+
				    				" TextSettings.HorzAlign = Trailing\n"+
				    				" TextSettings.WordWrap = False\n"+
				    				" Text = \'"+fname+":\'\n"+
				    				"end\n"+
				    				"object dbmmo"+fnameClean+": TMemo\n"+
				    				" Touch.InteractiveGestures = [Pan, LongTap, DoubleTap]\n"+
				    				" DataDetectorTypes = []\n"+
				    				" ClipChildren = True\n"+
				    				" Position.X = "+String.valueOf(labelWidth)+".000000000000000000\n"+
				    				//" Position.Y = 300.000000000000000000\n"+
				    				" Size.Width = 500.000000000000000000\n"+
				    				" Size.Height = 89.000000000000000000\n"+
				    				" Size.PlatformDefault = False\n"+
				    				//" TabOrder = 82\n"+
				    				" Viewport.Width = 500.000000000000000000\n"+
				    				" Viewport.Height = 85.000000000000000000\n"+
				    				"end\n"+
				    				"end\n";
				    		break;
				    	case "dbimg":
				    		additionalHeight += 70;
				    		oobj = "object lo"+fnameClean+": TLayout\n"+
				    				//" Position.X = 168.000000000000000000\n"+
				    				//" Position.Y = 260.000000000000000000\n"+
				    				" Size.Width = "+String.valueOf(220+labelWidth)+".000000000000000000\n"+
				    				" Size.Height = 69.000000000000000000\n"+
				    				" Size.PlatformDefault = False\n"+
				    				" TabOrder = 0\n"+
				    				"object lbl"+fnameClean+": TLabel\n"+
				    				" ClipChildren = True\n"+
				    				//" Position.X = 149.000000000000000000\n"+
				    				" Size.Width = "+String.valueOf(labelWidth)+".000000000000000000\n"+
				    				" Size.Height = 21.000000000000000000\n"+
				    				" Size.PlatformDefault = False\n"+
				    				" TextSettings.HorzAlign = Trailing\n"+
				    				" TextSettings.WordWrap = False\n"+
				    				" Text = \'"+fname+":\'\n"+
				    				"end\n"+
				    				"object dbimg"+fnameClean+": TImageControl\n"+
				    				" Size.Width = 220.000000000000000000\n"+
				    				" Size.Height = 69.000000000000000000\n"+
				    				" Size.PlatformDefault = False\n"+
				    				" Position.X = "+String.valueOf(labelWidth)+".000000000000000000\n"+
				    				//" Position.Y = 252.000000000000000000\n"+
				    				//" TabOrder = 0\n"+
				    				"end\n"+
				    				"end\n";
				    		break;
			    	}
			    	gobj += oobj;
	        	}
	        	gobj = gobj.replace("#----height_gb----#","Size.Height = "+String.valueOf(((totalWidth/682)*31)+60+additionalHeight)+".000000000000000000");
	        	gobj = gobj.replace("#----height_fl----#","Size.Height = "+String.valueOf(((totalWidth/682)*31)+additionalHeight)+".000000000000000000");
	        	gobj += " end\n"+
    			"end\n";	
			    output += gobj;
		    }
	        
	        BufferedWriter writer = null;
	        try
	        {
	            writer = new BufferedWriter( new FileWriter( "C:\\Users\\Mike\\Desktop\\EM Systems\\TRACS\\UTT_FMX.txt"));
	            writer.write( output);
	        }catch ( IOException e){
	        	e.printStackTrace();
	        }
	        writer.close();
	        
        } catch (FileNotFoundException e1) {
			e1.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	
	}
	
	
	public static void fieldListToSQLite(){

		BufferedReader reader;
		try {
			String file = "C:\\Users\\Mike\\Desktop\\EM Systems\\TRACS\\UTT_fields_DB.txt";
			reader = new BufferedReader(new FileReader(file));
			final StringBuilder contents = new StringBuilder();
		       
		    String line;
		    Pattern tablep=Pattern.compile("\\n(\\w+)\\n[\\w\\d _\\n()-]+\\n;", Pattern.MULTILINE);
		    Matcher tablem;
	        Pattern p=Pattern.compile("([\\w()]+)-([\\w_\\d]+)", Pattern.MULTILINE);
	        Matcher m;
	        int count=0;
	        try {
				while(reader.ready()) {
					line = reader.readLine();
					
					contents.append(line).append("\n");
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
	        reader.close();
	        
	        String fcont = contents.toString();
	        String output = "";
	        String tablename, ftype, fname, oobj = "";
	        tablem=tablep.matcher(fcont);
	        while(tablem.find()) {
	        	tablename = tablem.group(1);
	        	oobj = "CREATE TABLE IF NOT EXISTS "+tablename+"(\n";
	        	 m=p.matcher(tablem.group());
	        	 while(m.find()) {
			    	count++;
			    	ftype = m.group(1);
			    	fname = m.group(2);
			    	oobj += " " + fname + " " + ftype+" ,\n";
			    	
	        	 }
	        	 oobj = oobj.substring(0, oobj.lastIndexOf(",")-1);
	        	 oobj += "\n);\n\n";
	        	 output += oobj;
		    }
	        
	        BufferedWriter writer = null;
	        try
	        {
	            writer = new BufferedWriter( new FileWriter( "C:\\Users\\Mike\\Desktop\\EM Systems\\TRACS\\UTT_DB.sql"));
	            writer.write( output);
	        }catch ( IOException e){
	        	e.printStackTrace();
	        }
	        writer.close();
	        
        } catch (FileNotFoundException e1) {
			e1.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	
	}
	
	
	public static void fieldListToDFMUniQueries(String file, String outFile){

		BufferedReader reader;
		try {
			//String file = "C:\\Users\\Mike\\Desktop\\EM Systems\\TRACS\\UTT_lookup_fields_DB.txt";
			reader = new BufferedReader(new FileReader(file));
			final StringBuilder contents = new StringBuilder();
		       
		    String line;
		    Pattern tablep=Pattern.compile("\\n\\[?([\\w ]+)\\]?\\n[\\w\\d ()_\\n-]+\\n;", Pattern.MULTILINE);
		    Matcher tablem;
	        Pattern p=Pattern.compile("([\\w()]+)-\\[?([\\w_\\d ]+)\\]?", Pattern.MULTILINE);
	        Matcher m;
	        int count=0;
	        try {
				while(reader.ready()) {
					line = reader.readLine();
					
					contents.append(line).append("\n");
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
	        reader.close();
	        
	        String fcont = contents.toString();
	        String output = "";
	        String tablename, ftype, fname, tableObjName, fieldObjName, sqlTableName, uniquery = "", oobj = "";
	        int top = 10, left = 500;
	        tablem=tablep.matcher(fcont);
	        while(tablem.find()) {
	        	tablename = tablem.group(1);
	        	if(tablename.contains(" ") || tablename.contains("-") || tablename.contains(">") || tablename.contains("<") || tablename.contains("?") )
	        		sqlTableName = "["+tablename+"]";
	        	else
	        		sqlTableName = tablename;
	        	tableObjName = tablename.replaceAll(" ", "");
	        	uniquery = "object uq"+tableObjName+": TUniQuery\n"+
	        			" Connection = ucLocal\n"+
	        			" SQL.Strings = (\n"+
	        			" \'select * from "+sqlTableName+"\')\n"+
	        			" Left = "+String.valueOf(left)+"\n"+
	        			" Top = "+String.valueOf(top)+"\n";
	        	if(left > 1500){
	        		left = 500;
	        		top += 50;
	        	}	
	        	else
	        		left += 50;
	        	 m=p.matcher(tablem.group());
	        	 while(m.find()) {
			    	count++;
			    	ftype = m.group(1);
			    	fname = m.group(2);
			    	fieldObjName = tableObjName+fname.replaceAll(" ", "");
			    	switch(ftype){
				    	case "INT":
				    	case "INTEGER":
				    		oobj = "object uq"+fieldObjName+": TIntegerField\n"+
					    			" FieldName = \'"+fname+"\'\n"+
					    			"end\n";
				    		break;
				    	case "NUMBER":	
					    	oobj = "object uq"+fieldObjName+": TFloatField\n"+
					    			" FieldName = \'"+fname+"\'\n"+
					    			"end\n";
				    		break;
				    	case "BOOLEAN":
				    		oobj = "object uq"+fieldObjName+": TBooleanField\n"+
				        			" FieldName = \'"+fname+"\'\n"+
				        			"end\n";
				    		break;
				    	case "VARCHAR2":
				    		oobj = "object uq"+fieldObjName+": TStringField\n"+
				        			" FieldName = \'"+fname+"\'\n"+
				        			"end\n";
				    		break;
				    	case "TEXT":
					    case "CLOB":
				    		oobj = "object uq"+fieldObjName+": TMemoField\n"+
			        			" FieldName = \'"+fname+"\'\n"+
			        			"end\n";
				    		break;
				    	case "DATETIME":
				    		oobj = "object uq"+fieldObjName+": TDateTimeField\n"+
				        			" FieldName = \'"+fname+"\'\n"+
				        			"end\n";
				    		break;
				    	case "DATE":
				    		oobj = "object uq"+fieldObjName+": TDateField\n"+
				        			" FieldName = \'"+fname+"\'\n"+
				        			"end\n";
				    		break;
				    	case "BLOB":
				    		oobj = "object uq"+fieldObjName+": TBlobField\n"+
				        			" FieldName = \'"+fname+"\'\n"+
				        			"end\n";
				    		break;
			    	}
			    	uniquery += oobj;
	        	 }
	        	 uniquery += "end\n";
	        	 output += uniquery;
		    }
	        
	        BufferedWriter writer = null;
	        try
	        {
	            writer = new BufferedWriter( new FileWriter( outFile ));
	            writer.write( output);
	        }catch ( IOException e){
	        	e.printStackTrace();
	        }
	        writer.close();
	        
        } catch (FileNotFoundException e1) {
			e1.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	
	}
	
	
	public static void fieldListToDFMUniDataSources(String file, String outFile){

		BufferedReader reader;
		try {
			//String file = "C:\\Users\\Mike\\Desktop\\EM Systems\\TRACS\\UTT_lookup_fields_DB.txt";
			reader = new BufferedReader(new FileReader(file));
			final StringBuilder contents = new StringBuilder();
		       
		    String line;
		    Pattern tablep=Pattern.compile("\\n\\[?([\\w ]+)\\]?\\n[\\w\\d ()_\\n-]+\\n;", Pattern.MULTILINE);
		    Matcher tablem;
	        Pattern p=Pattern.compile("([\\w()]+)-\\[?([\\w_\\d ]+)\\]?", Pattern.MULTILINE);
	        Matcher m;
	        int count=0;
	        try {
				while(reader.ready()) {
					line = reader.readLine();
					
					contents.append(line).append("\n");
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
	        reader.close();
	        
	        String fcont = contents.toString();
	        String output = "";
	        String tablename, ftype, fname, tableObjName, fieldObjName, sqlTableName, unidatasource = "", oobj = "";
	        int top = 500, left = 500;
	        tablem=tablep.matcher(fcont);
	        while(tablem.find()) {
	        	tablename = tablem.group(1);
	        	if(tablename.contains(" ") || tablename.contains("-") || tablename.contains(">") || tablename.contains("<") || tablename.contains("?") )
	        		sqlTableName = "["+tablename+"]";
	        	else
	        		sqlTableName = tablename;
	        	tableObjName = tablename.replaceAll(" ", "");
	        	unidatasource = "object ds"+tableObjName+": TUniDataSource\n"+
	        			" DataSet = uq"+tableObjName+"\n"+
	        			" Left = "+String.valueOf(left)+"\n"+
	        			" Top = "+String.valueOf(top)+"\n";
	        	if(left > 1500){
	        		left = 500;
	        		top += 50;
	        	}	
	        	else
	        		left += 50;
	        	 m=p.matcher(tablem.group());
	        	 while(m.find()) {
			    	count++;
			    	ftype = m.group(1);
			    	fname = m.group(2);
			    	fieldObjName = tableObjName+fname.replaceAll(" ", "");
			    	
	        	 }
	        	 unidatasource += "end\n";
	        	 output += unidatasource;
		    }
	        
	        BufferedWriter writer = null;
	        try
	        {
	            writer = new BufferedWriter( new FileWriter( outFile ));
	            writer.write( output);
	        }catch ( IOException e){
	        	e.printStackTrace();
	        }
	        writer.close();
	        
        } catch (FileNotFoundException e1) {
			e1.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	
	}
	
	
	public static void fieldListToBindingListAndSources(){

		BufferedReader reader;
		try {
			HashMap<String, String> dbFieldsToTables = getFieldListDBFieldToTablesMap("C:\\Users\\Mike\\Desktop\\EM Systems\\TRACS\\UTT_fields_DB.txt");
			String file = "C:\\Users\\Mike\\Desktop\\EM Systems\\TRACS\\UTT_fields_FMX.txt";
			reader = new BufferedReader(new FileReader(file));
			final StringBuilder contents = new StringBuilder();
		       
		    String line;
		    Pattern groupp=Pattern.compile("\\n([_ \\w]+)\\n[\\w\\d _\\n-]+\\n;", Pattern.MULTILINE);
		    Matcher groupm;
	        
		    Pattern p=Pattern.compile("(\\w+)-([\\w \\d]+)", Pattern.MULTILINE);
	        Matcher m;
	        int count=0;
	        try {
				while(reader.ready()) {
					line = reader.readLine();
					
					contents.append(line).append("\n");
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
	        reader.close();
	        
	        int top = 100, left = 100, fcount=0;
	        String fcont = contents.toString();
	        String output = "";
	        String groupname, fmxGroupnameClean, groupnameClean, ftype, fBindListCtrlName, fname, fnameClean, fnameDB, bindListObj = "", gobj="", oobj = "";
	        int labelWidth;
	        groupm = groupp.matcher(fcont);
	        
	        bindListObj = "object BindingsList1: TBindingsList\n"+
        			" Methods = <>\n"+
        			" OutputConverters = <>\n"+
        			" Left = "+(left-50)+"\n"+
        			" Top = "+(top-50)+"\n";
	        
	        Iterator<Entry<String, String>> groupIter = dbFieldsToTables.entrySet().iterator();
	        Entry<String, String> currEntry;
	        ArrayList<String> processedGroups = new ArrayList<String>();
	        while(groupIter.hasNext()){
	        	currEntry = groupIter.next();
	        	if(!processedGroups.contains(currEntry.getValue())){
	        		gobj +=  "object bs"+currEntry.getValue()+": TBindSourceDB\n"+
							" DataSource = dmSummons.ds"+currEntry.getValue()+"\n"+
							" ScopeMappings = <>\n"+
							" Left = "+left+"\n"+
							" Top = "+top+"\n"+
							"end\n";
		        	
		        	if(left > 500){
		        		left = 50;
		        		top += 50;
		        	}	
		        	else
		        		left += 50;
		        	processedGroups.add(currEntry.getValue());
	        	}
	        }
	        
	        while(groupm.find()) {
	        	groupname = groupm.group(1);
	        	fmxGroupnameClean = groupname.replaceAll(" ", "_").toUpperCase();
	        	
	        	
	        	m=p.matcher(groupm.group());
	        	while(m.find()) {
			    	count++;
			    	ftype = m.group(1);
			    	fname = m.group(2);
			    	fnameClean = fname.replaceAll(" ", "");
			    	fnameDB = fname.replaceAll(" ", "_").toUpperCase();
			    	fBindListCtrlName = fnameDB;
			    	labelWidth = fname.length()*8;
			    	if(dbFieldsToTables.containsKey(fnameDB)){
			    		groupnameClean = dbFieldsToTables.get(fnameDB);
			    		if(!groupnameClean.equals(fmxGroupnameClean)){
			    			System.out.println("--- group name does not match for field: "+ fnameDB + " : " + groupnameClean + " != " + fmxGroupnameClean);
			    			if(dbFieldsToTables.containsKey(fmxGroupnameClean+"---"+fnameDB)){
			    				groupnameClean = fmxGroupnameClean;
			    				fBindListCtrlName = fmxGroupnameClean + fnameDB;
			    				System.out.println("--- using fmxGroup");
			    			}
			    		}
			    	}
			    	else{
			    		System.err.println("Error--- field: "+ fnameDB + " not in db to group map ");
			    		groupnameClean = fmxGroupnameClean;
			    	}
		    		switch(ftype){
				    	case "dbe":
				    		oobj = "object LinkControlToField"+fBindListCtrlName+": TLinkControlToField\n"+
				    				" Category = \'Quick Bindings\'\n"+
				    				" DataSource = bs"+groupnameClean+"\n"+
				    				" FieldName = \'"+fnameDB+"\'\n"+
				    				" Control = "+ftype+fnameClean+"\n"+
				    				" Track = False\n"+
				    				"end\n";
				    		break;
				    	case "dblcb":
				    		oobj = "object LinkControlToField"+fBindListCtrlName+": TLinkControlToField\n"+
				    				" Category = \'Quick Bindings\'\n"+
				    				" DataSource = bs"+groupnameClean+"\n"+
				    				" FieldName = \'"+fnameDB+"\'\n"+
				    				" Control = "+ftype+fnameClean+"\n"+
				    				" Track = False\n"+
				    				"end\n"; 
				    		
				    		break;
				    	case "dbdtp":
				    		oobj = "object LinkControlToField"+fBindListCtrlName+": TLinkControlToField\n"+
				    				" Category = \'Quick Bindings\'\n"+
				    				" DataSource = bs"+groupnameClean+"\n"+
				    				" FieldName = \'"+fnameDB+"\'\n"+
				    				" Control = "+ftype+fnameClean+"\n"+
				    				" Track = False\n"+
				    				"end\n";

				    		break;
				    	case "dbmmo":
				    		oobj = "object LinkControlToField"+fBindListCtrlName+": TLinkControlToField\n"+
				    				" Category = \'Quick Bindings\'\n"+
				    				" DataSource = bs"+groupnameClean+"\n"+
				    				" FieldName = \'"+fnameDB+"\'\n"+
				    				" Control = "+ftype+fnameClean+"\n"+
				    				" Track = False\n"+
				    				"end\n"; 

				    		break;
				    	case "dbimg":
				    		oobj = "object LinkControlToField"+fBindListCtrlName+": TLinkControlToField\n"+
				    				" Category = \'Quick Bindings\'\n"+
				    				" DataSource = bs"+groupnameClean+"\n"+
				    				" FieldName = \'"+fnameDB+"\'\n"+
				    				" Control = "+ftype+fnameClean+"\n"+
				    				" Track = False\n"+
				    				"end\n"; 

				    		break;
				    	default:
				    		System.err.println("Error--- missing field type: " + ftype);
				    		break;
			    	}
		    		fcount++;
		    		bindListObj += oobj;
	        	}
	        	
		    }
	        bindListObj += " end\n"+
	        			"end\n";	
		    output += gobj + bindListObj;
	        
		    System.out.println("field count: " + fcount);
	        BufferedWriter writer = null;
	        try
	        {
	            writer = new BufferedWriter( new FileWriter( "C:\\Users\\Mike\\Desktop\\EM Systems\\TRACS\\UTT_FMX_Bindings.txt"));
	            writer.write( output);
	        }catch ( IOException e){
	        	e.printStackTrace();
	        }
	        writer.close();
	        
        } catch (FileNotFoundException e1) {
			e1.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	
	}
	
	
	public static HashMap<String, String> getFieldListDBFieldToTablesMap(String file){

		BufferedReader reader;
		 HashMap<String, String> dbFieldsToTableMap = new  HashMap<String, String>();
		try {
			//String file = "C:\\Users\\Mike\\Desktop\\EM Systems\\TRACS\\UTT_lookup_fields_DB.txt";
			reader = new BufferedReader(new FileReader(file));
			final StringBuilder contents = new StringBuilder();
		       
		    String line;
		    Pattern tablep=Pattern.compile("\\n\\[?([\\w ]+)\\]?\\n[\\w\\d ()_\\n-]+\\n;", Pattern.MULTILINE);
		    Matcher tablem;
	        Pattern p=Pattern.compile("([\\w\\d()]+)-\\[?([\\w_\\d ]+)\\]?", Pattern.MULTILINE);
	        Matcher m;
	        int count=0;
	        try {
				while(reader.ready()) {
					line = reader.readLine();
					
					contents.append(line).append("\n");
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
	        reader.close();
	        
	        String fcont = contents.toString();
	        String output = "";
	        String groupNameDupField, groupnameClean, tablename, ctrlType, ftype, fname, fnameDB, fnameClean,  tableObjName, fieldObjName, sqlTableName, bindListObj = "", gobj="", oobj = "";
	        tablem=tablep.matcher(fcont);
	        while(tablem.find()) {
	        	
	        	tablename = tablem.group(1);
	        	if(tablename.contains(" ") || tablename.contains("-") || tablename.contains(">") || tablename.contains("<") || tablename.contains("?") )
	        		sqlTableName = "["+tablename+"]";
	        	else
	        		sqlTableName = tablename;
	        	tableObjName = tablename.replaceAll(" ", "");
	        	groupnameClean = tablename.replaceAll(" ", "_").toUpperCase();
	        	//System.out.println(" group: "  + groupnameClean);
		    	
	        	 m=p.matcher(tablem.group());
	        	 while(m.find()) {
			    	count++;
			    	ftype = m.group(1);
			    	fname = m.group(2);
			    	fieldObjName = tableObjName+fname.replaceAll(" ", "");
			    	fnameClean = toTitleCase(fname.replaceAll("_", " ")).replaceAll(" ", "");
			    	fnameDB = fname;
			    	//System.out.println("field -> group: " + fnameDB + " -> " + groupnameClean);
			    	groupNameDupField = dbFieldsToTableMap.put(fnameDB, groupnameClean);
			    	if(groupNameDupField != null){
			    		System.out.println("Duplicate_Field: " + fnameDB + ":  " + groupNameDupField +" <-> "+ groupnameClean);
			    		dbFieldsToTableMap.put(groupNameDupField+"---"+fnameDB, groupNameDupField);
			    	}
	        	 } 
		    }   
        } catch (FileNotFoundException e1) {
			e1.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return dbFieldsToTableMap;
	}
	
	
	public static void lookupFieldListToLookupBindingSources(){

		BufferedReader reader;
		try {
			HashMap<String, String> dbFieldsToTables = getLookupFieldListDBFieldToTablesMap("C:\\Users\\Mike\\Desktop\\EM Systems\\TRACS\\UTT_lookup_fields_DB_datasource.txt");
			
	        int top = 100, left = 100, fcount=0;
	        String output = "";
	        String gobj="", oobj = "";
	        int labelWidth;
	        
	       
	        Iterator<Entry<String, String>> groupIter = dbFieldsToTables.entrySet().iterator();
	        Entry<String, String> currEntry;
	        ArrayList<String> processedGroups = new ArrayList<String>();
	        while(groupIter.hasNext()){
	        	currEntry = groupIter.next();
	        	if(!processedGroups.contains(currEntry.getValue())){
	        		gobj +=  "object bs"+currEntry.getValue()+": TBindSourceDB\n"+
							" DataSource = dmSummons.ds"+currEntry.getValue()+"\n"+
							" ScopeMappings = <>\n"+
							" Left = "+left+"\n"+
							" Top = "+top+"\n"+
							"end\n";
		        	
		        	if(left > 500){
		        		left = 50;
		        		top += 50;
		        	}	
		        	else
		        		left += 50;
		        	processedGroups.add(currEntry.getValue());
	        	}
	        }
	        
	       
		    output += gobj;
	        
		    System.out.println("field count: " + fcount);
	        BufferedWriter writer = null;
	        try
	        {
	            writer = new BufferedWriter( new FileWriter( "C:\\Users\\Mike\\Desktop\\EM Systems\\TRACS\\UTT_FMX_lookup_Bindings.txt"));
	            writer.write( output);
	        }catch ( IOException e){
	        	e.printStackTrace();
	        }
	        writer.close();
	        
        } catch (FileNotFoundException e1) {
			e1.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	
	}
	
	
	public static HashMap<String, String> getLookupFieldListDBFieldToTablesMap(String file){

		BufferedReader reader;
		 HashMap<String, String> dbFieldsToTableMap = new  HashMap<String, String>();
		try {
			//String file = "C:\\Users\\Mike\\Desktop\\EM Systems\\TRACS\\UTT_lookup_fields_DB.txt";
			reader = new BufferedReader(new FileReader(file));
			final StringBuilder contents = new StringBuilder();
		       
		    String line;
		    Pattern tablep=Pattern.compile("\\n\\[?([\\w ]+)\\]?\\n[\\w\\d ()_\\n-]+\\n;", Pattern.MULTILINE);
		    Matcher tablem;
	        Pattern p=Pattern.compile("([\\w\\d()]+)-\\[?([\\w_\\d ]+)\\]?", Pattern.MULTILINE);
	        Matcher m;
	        int count=0;
	        try {
				while(reader.ready()) {
					line = reader.readLine();
					
					contents.append(line).append("\n");
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
	        reader.close();
	        
	        String fcont = contents.toString();
	        String output = "";
	        String groupNameDupField, groupnameClean, tablename, ctrlType, ftype, fname, fnameDB, fnameClean,  tableObjName, fieldObjName, sqlTableName, bindListObj = "", gobj="", oobj = "";
	        tablem=tablep.matcher(fcont);
	        while(tablem.find()) {
	        	
	        	tablename = tablem.group(1);
	        	if(tablename.contains(" ") || tablename.contains("-") || tablename.contains(">") || tablename.contains("<") || tablename.contains("?") )
	        		sqlTableName = "["+tablename+"]";
	        	else
	        		sqlTableName = tablename;
	        	tableObjName = tablename.replaceAll(" ", "");
	        	groupnameClean = tablename;//.replaceAll(" ", "_").toUpperCase();
	        	//System.out.println(" group: "  + groupnameClean);
		    	
	        	 m=p.matcher(tablem.group());
	        	 while(m.find()) {
			    	count++;
			    	ftype = m.group(1);
			    	fname = m.group(2);
			    	fieldObjName = tableObjName+fname.replaceAll(" ", "");
			    	fnameClean = toTitleCase(fname.replaceAll("_", " ")).replaceAll(" ", "");
			    	fnameDB = fname;
			    	//System.out.println("field -> group: " + fnameDB + " -> " + groupnameClean);
			    	groupNameDupField = dbFieldsToTableMap.put(fnameDB, groupnameClean);
			    	if(groupNameDupField != null){
			    		System.out.println("Duplicate_Field: " + fnameDB + ":  " + groupNameDupField +" <-> "+ groupnameClean);
			    		dbFieldsToTableMap.put(groupNameDupField+"---"+fnameDB, groupNameDupField);
			    	}
	        	 } 
		    }   
        } catch (FileNotFoundException e1) {
			e1.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return dbFieldsToTableMap;
	}
	
	public static void tableListTofilteredSQL(){

		BufferedReader reader;
		try {
			String file = "C:\\Users\\Mike\\Desktop\\EM Systems\\TRACS\\UTT_lookup_tables_DB.txt";
			String sqlFile = "C:\\Users\\Mike\\Desktop\\EM Systems\\TRACS\\Support.sql";
			reader = new BufferedReader(new FileReader(file));
			
			ArrayList<String> tables = new ArrayList<String>();
			String line;
			    
			 try {
					while(reader.ready()) {
						line = reader.readLine();
						tables.add(line);
					}
				} catch (IOException e) {
					e.printStackTrace();
				}
		        reader.close();
			
	        reader = new BufferedReader(new FileReader(sqlFile));
			
	        
		   Pattern dropp=Pattern.compile("DROP TABLE IF EXISTS ([\\w\\d_]+)", Pattern.MULTILINE);
		   Pattern createp=Pattern.compile("CREATE TABLE ([\\w\\d_]+)" , Pattern.MULTILINE);
		   Pattern insertp=Pattern.compile("INSERT INTO ([\\w\\d_]+)", Pattern.MULTILINE);
		    Matcher dropm;
		    Matcher createm;
		    Matcher insertm;
		    
	       
	        BufferedWriter writer = null;
	        
	        String tableName;
	        
	        try {
	        	writer = new BufferedWriter( new FileWriter( "C:\\Users\\Mike\\Desktop\\EM Systems\\TRACS\\UTT_lookup_DB.sql"));
	            while(reader.ready()) {
					line = reader.readLine();
					dropm = dropp.matcher(line);
			        if(dropm.find()) {
			        	tableName = dropm.group(1);
			        	if(tables.contains(tableName))
			        		writer.write( line+ "\r\n");
			        }
			        else {
			        	createm = createp.matcher(line);
			        	if(createm.find()) {
					        	tableName = createm.group(1);
					        	if(tables.contains(tableName))
					        		writer.write( line+ "\r\n");
					    }
			        	else {
			        		 insertm = insertp.matcher(line);
				        	 if(insertm.find()) {
						        	tableName = insertm.group(1);
						        	if(tables.contains(tableName))
						        		writer.write( line + "\r\n");
						     }
					    }
			        }
					
		            
					
				}
	            
			} catch (IOException e) {
				e.printStackTrace();
			}
	        writer.close();
            reader.close();
	        
	       
	        
		} catch (FileNotFoundException e1) {
			e1.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

		
	class User {
		int id;
	      String sid;
	      String username;
	      String lastName;
	      String firstName;
		public int getId() {
			return id;
		}
		public void setId(int id) {
			this.id = id;
		}
		public String getSid() {
			return sid;
		}
		public void setSid(String sid) {
			this.sid = sid;
		}
		public String getUsername() {
			return username;
		}
		public void setUsername(String username) {
			this.username = username;
		}
		public String getLastName() {
			return lastName;
		}
		public void setLastName(String lastName) {
			this.lastName = lastName;
		}
		public String getFirstName() {
			return firstName;
		}
		public void setFirstName(String firstName) {
			this.firstName = firstName;
		}
	      
	      
	}
	
	public static String toTitleCase(String s) {

	    final String ACTIONABLE_DELIMITERS = " '-/"; // these cause the character following
	                                                 // to be capitalized

	    StringBuilder sb = new StringBuilder();
	    boolean capNext = true;

	    for (char c : s.toCharArray()) {
	        c = (capNext)
	                ? Character.toUpperCase(c)
	                : Character.toLowerCase(c);
	        sb.append(c);
	        capNext = (ACTIONABLE_DELIMITERS.indexOf((int) c) >= 0); // explicit cast not needed
	    }
	    return sb.toString();
	}
}

Learning Scala

This is the first scala code that I ever wrote. It was a learning exercise.

import scala.util.parsing.json._

object ScalaTest {
  def main(args: Array[String]) : Unit = {
    //Tuples and pattern matching
    divmod(567, 333) match {
      case (n, d) => println("quotient: " + n + ", rest: " + d)
    }
    
     strReverseCnt("hello scala!") match {
      case (n, d, i) => println("str: " + n + ", rev: " + d + ", cnt: " + i)
    }
     
    //Functions
    println("plus1: " +plus1(2)) 
     
    //Lists 
    println("sorted list:" + isort(List(3, 1, 4, 2)))
    //same as
    println("sorted list:" + isort(3 :: (1 :: (4 :: (2 :: Nil)))))
    
    //Tail Recursion
    //no tr
    println("factorial:" + factorial(11));
    //with tr
    println("factorialTail:" + factorialTail(1, 11));
    
    //json parsing
    parseJsonObject("""{"records":[{"id":"6b156841-d323-4d3f-9efe-3a3106118aed","userid":"u28205423","username":"Mike Brachmann","deviceid":"788514002","start_date":"2016-09-02T13:10:31Z","end_date":"2016-09-02T13:10:38Z","fee":0.00,"currency":"None","billing_state":"Bill","notes":"api test"}],"records_remaining":0}""")
  }
  
  //Tuples
  //def divmod(x: Int, y: Int) = new Tuple2[Int, Int](x / y, x % y)
  //or more concisely
  def divmod(x: Int, y: Int): (Int, Int) = (x / y, x % y)
  def strReverseCnt(str: String): (String, String, Int) = (str, str.reverse, str.length())

  //functions
  // most verbose
  /*val plus1: Function1[Int, Int] = {
    class Local extends Function1[Int, Int] {
      def apply(x: Int): Int = x + 1
    }
    new Local: Function1[Int, Int]
  }*/
  
  //same but more concise
  /*val plus1: Function1[Int, Int] = new Function1[Int, Int] {
    def apply(x: Int): Int = x + 1
  }*/

  //most concise but same as two above and is expanded by compiler to above
  val plus1: (Int => Int) = (x: Int) => x + 1


  //Lists
  def isort(xs: List[Int]): List[Int] = xs match {
    case List() => List()
    case x :: xs1 => insert(x, isort(xs1))
  }
  
  def insert(x: Int, xs: List[Int]): List[Int] = xs match {
    case List() => List(x)
    case y :: ys => if (x <= y) x :: xs else y :: insert(x, ys)
  }
  
  
  //Tail Recursion
  //not tr
  def factorial(n: Int): Int = {
    if (n == 0) {
      println("factorial: if: " + n );
      new Throwable().getStackTrace().foreach { x => println(x) };
      1 
    }
    else {
      n * factorial(n - 1)
    }
  }
  //tr
  def factorialTail(curVal: Int, n: Int): Int = {
    if (n == 0) {
      println("factorialTail: if: " + n + " - " + curVal);
      new Throwable().getStackTrace().foreach { x => println(x) };
      curVal 
    }
    else {
      factorialTail(curVal*n, n - 1)
    }
  }
  
  
  //json parsing
  def parseJsonObject(jsonStr: String) : Map[String, Any] = {
    val parsedJson =JSON.parseFull(jsonStr)
    parsedJson match {
        case Some(jsonMap:Map[String,Any]) => {
          jsonMap
        }
        case _ => {
          Map()
        }
    }
  }
  
}

Fun With Scala

Back-end of app to predict what I am working on for semi-automated time entry and tracking. It grabs my recent email, phone calls, and TeamViewer connections (a remote computer support app) so that they can be associated with tasks and time entries for later predctions and analysis

import scalikejdbc._
import scala.util.parsing.json._
import org.apache.spark.sql.SQLContext
import org.apache.spark.{SparkContext, SparkConf}
// $example on$
import org.apache.spark.ml.Pipeline
import org.apache.spark.ml.PipelineModel
import org.apache.spark.ml.classification.DecisionTreeClassifier
import org.apache.spark.ml.classification.DecisionTreeClassificationModel
import org.apache.spark.ml.feature.{StringIndexer, IndexToString, VectorIndexer}
import org.apache.spark.ml.evaluation.MulticlassClassificationEvaluator
// $example off$
import java.io.StringReader
import scala.collection.mutable.ArrayBuffer;
import org.apache.lucene.util.Version
import org.apache.lucene.analysis.en.EnglishAnalyzer
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute
import scala.io.Source
import org.apache.spark.ml.feature.{HashingTF, Tokenizer} 
//import org.apache.spark.mllib.classification.{NaiveBayes, NaiveBayesModel}
import org.apache.spark.ml.classification.NaiveBayes
import org.apache.spark.ml.classification.RandomForestClassifier
import org.apache.spark.mllib.regression.LabeledPoint
import org.apache.spark.ml.feature.StringIndexer
import org.apache.spark.mllib.tree.RandomForest
import org.apache.spark.mllib.tree.model.RandomForestModel
import org.apache.spark.mllib.linalg.{Vectors, Vector}
import org.apache.spark.mllib.evaluation.MulticlassMetrics
import org.apache.spark.sql.Row
import org.apache.spark.rdd.RDD
import org.apache.spark.sql.SQLImplicits
import org.apache.spark.sql.functions.udf
import org.apache.spark.ml.feature.RegexTokenizer
import org.apache.spark.ml.classification.LogisticRegression
import org.apache.spark.ml.Pipeline
import org.apache.spark.mllib.regression.LabeledPoint
import javax.servlet.http.{HttpServlet,
                       HttpServletRequest, 
                       HttpServletResponse}
import java.text.SimpleDateFormat
import java.util.Date

case class LabeledRecord(id: String, topic: String, text: String)
case class ReverseIndexRecord(prediction: Double)
class TimeEntry(val id: Int, val userID: Int, val taskID: Int, val hours: Double, val subject:String, val summary:String, val details:String)

class PredictServlet(sc : SparkContext, model : PipelineModel ) extends HttpServlet {
  var otherTaskIDs = List(1306)
  val userToExtn = Map( "1011" -> Seq("1234", "603") ) 
  val requestHandlers = Map( "prediction_info" -> makeTaskPredictions _, "time_entry" -> createTimeEntry _, "task_list" -> getTaskList _ )  
  override def doPost(req : HttpServletRequest, resp : HttpServletResponse) = {
      val text = scala.io.Source.fromInputStream(req.getInputStream).mkString 
      println("Request: " + text )
      val parsedJson = JSON.parseFull(text)//.getOrElse(yourDefault)
      parsedJson match {
        case Some(jsonMap:Map[String,Any]) => {
          val requestID = jsonMap("request_id")
          val responseStr = requestHandlers(requestID.asInstanceOf[String])(jsonMap) 
          println(responseStr);
          resp.getWriter().print(responseStr)
        }
        case None => {
          println("Failed.")
          resp.getWriter().print("[{\"status\": \"Failed\"}]")
        }
      }
    }
    def makeTaskPredictions(jsonMapA:Map[String,Any]) : String  = {
      val jsonMap = jsonMapA.asInstanceOf[Map[String,String]];
      val sqlContext = new SQLContext(sc)
      import sqlContext.implicits._ 
      val intervalMilis = jsonMap("hours").toInt
      val intervalSeconds = intervalMilis/1000;
      val inEmails = ExchangeManager.getRecentInboxEmails(intervalSeconds*1000, 10)
      val outEmails = ExchangeManager.getRecentSentEmails(intervalSeconds*1000, 10)
      val emailsContactNames = (inEmails.map { email =>  getContactNamesFromEmail(email).mkString(" ") } ++ outEmails.map { email =>  getContactNamesFromEmail(email).mkString(" ") }).toArray
      val testData = LabeledRecord("1306", "", jsonMap("text") +" " + emailsContactNames.mkString(" ")) :: Nil
      val predictions = model.transform(testData.toDF())
      val predictionProbabilityAndIndexes = predictions.select("probability").rdd.map(r => r(0)).collect().map { item =>
        item.asInstanceOf[org.apache.spark.mllib.linalg.DenseVector].toArray.zipWithIndex 
      }
      val top5PredictionProbabilit = predictionProbabilityAndIndexes(0).sortBy(_._1).reverse.slice(0, 5)
      val sortedPredictionProbabilitLabels = top5PredictionProbabilit.map { case(element, index) =>
         val predictionProbability = ReverseIndexRecord(index.toDouble) :: Nil
         ( model.stages(model.stages.length-1).transform(predictionProbability.toDF()).select("predictedLabel").rdd.map { x => x(0) }.collect()(0).toString(), element ) 
      }
      predictions.show()
      
      val lookupPredictedTaskJsonObjs = NamedDB("etask") readOnly { implicit session =>
        val taskIDs = sortedPredictionProbabilitLabels.map { case(taskID, taskProbability ) => taskID }
        sql"select t.task_id, t.SUBJECT, p.NAME, c.SHORT_NAME, c.NAME AS CNAME from tasks t join project p on p.project_id = t.PROJECT_ID join customers c on c.CUSTOMER_ID = p.CUSTOMER_ID where t.task_id in (${taskIDs.toSeq})" // don't worry, prevents SQL injection
        .map(rs => JSONObject( Map("task_id" -> rs.string("task_id"), "task_name" -> rs.string("SUBJECT"), "project" -> rs.string("NAME"), "customer_short" -> rs.string("SHORT_NAME"), "customer" -> rs.string("CNAME"), "probability" -> sortedPredictionProbabilitLabels(sortedPredictionProbabilitLabels.indexWhere { case (taskID, taskProbability ) => taskID == rs.string("task_id") })._2))) // extracts values from rich java.sql.ResultSet
        .list                   // single, list, traversable
        .apply() 
      }
      
      val lookupOtherTaskJsonObjs = NamedDB("etask") readOnly { implicit session =>
        val taskIDs = otherTaskIDs
        sql"select t.task_id, t.SUBJECT, p.NAME, c.SHORT_NAME, c.NAME AS CNAME from tasks t join project p on p.project_id = t.PROJECT_ID join customers c on c.CUSTOMER_ID = p.CUSTOMER_ID where t.task_id in (${taskIDs})" // don't worry, prevents SQL injection
        .map(rs => JSONObject( Map("task_id" -> rs.string("task_id"), "task_name" -> rs.string("SUBJECT"), "project" -> rs.string("NAME"), "customer_short" -> rs.string("SHORT_NAME"), "customer" -> rs.string("CNAME"), "probability" -> 0.0 ))) // extracts values from rich java.sql.ResultSet
        .list                   // single, list, traversable
        .apply() 
      }
      
      val lookupRecentPhoneCalls = NamedDB("phone") readOnly { implicit session =>
        val taskIDs = sortedPredictionProbabilitLabels.map { case(taskID, taskProbability ) => taskID }
        sql"select calldate, src, dst, dcontext, duration, uniqueid from cdr where DATE_SUB(NOW(),INTERVAL ${intervalSeconds} SECOND) <= DATE_ADD(calldate,INTERVAL duration SECOND) and (src in (${userToExtn(jsonMap("user"))}) or dst in (${userToExtn(jsonMap("user"))}))" // don't worry, prevents SQL injection
        .map(rs => JSONObject( Map("calldate" -> rs.string("calldate"), "src" -> rs.string("src"), "dst" -> rs.string("dst"), "dst_context" -> rs.string("dcontext"), "duration" -> rs.string("duration"), "id" -> rs.string("uniqueid")))) // extracts values from rich java.sql.ResultSet
        .list                   // single, list, traversable
        .apply() 
      }
      
      val lookupRecentTeamViewerConnections = TeamViewerInterface.getConnections(new Date(new Date().getTime - (intervalMilis*3)), new Date())("records").asInstanceOf[List[Map[String, String]]].map(connectionMap => JSONObject(connectionMap))
      
      val responseStr = "{ \"predicted_tasks\" : [" + lookupOtherTaskJsonObjs.mkString(",") + "," + lookupPredictedTaskJsonObjs.mkString(",") + "], \"tv_connections\":["+lookupRecentTeamViewerConnections.mkString(",")+"], \"phone_calls\": ["+lookupRecentPhoneCalls.mkString(",")+"], \"emails_in\":["+inEmails.mkString(",") + "], \"emails_out\":["+outEmails.mkString(",")+ "], \"text\":\""+predictions.first().getString(2)+"\" }"
      responseStr
    }
    def createTimeEntry(jsonMap:Map[String,Any]) : String = {
      //println("TODO: create time entry and assoc records here!")
      otherTaskIDs = (jsonMap("task").asInstanceOf[Map[String, String]])("task_id").asInstanceOf[String].toInt :: otherTaskIDs
      otherTaskIDs = otherTaskIDs.removeDuplicates
      if(otherTaskIDs.length > 5)
        otherTaskIDs = otherTaskIDs.reverse.tail
      val entryID = addOrCreateTimeEntryRecord(jsonMap("user").asInstanceOf[String].toInt, (jsonMap("task").asInstanceOf[Map[String, String]])("task_id").asInstanceOf[String].toInt, jsonMap("hours").asInstanceOf[String].toDouble, "", jsonMap("summary_text").asInstanceOf[String], jsonMap("keystroke_text").asInstanceOf[String])
      NamedDB("etask") autoCommit { implicit session =>
        val windows = jsonMap("windows").asInstanceOf[List[Map[String,String]]].toArray
        for(window <- windows){
          sql"insert into time_entry_window_assoc VALUES(0, ${entryID}, ${window.asInstanceOf[Map[String, String]]("window_title")}, ${window.asInstanceOf[Map[String, String]]("count").toInt} ) "
						.update()
						.apply()
        }
        sql"insert into time_entry_keystroke_assoc VALUES(0, ${entryID}, ${jsonMap("keystroke_text").asInstanceOf[String]} ) "
				  .update()
					.apply()
      }
      val emails = jsonMap("emails").asInstanceOf[List[Map[String,Any]]].toArray
      for(emailJson <- emails){
        val email = Email.fromJson(emailJson)
        val emailID = createEmailRecord(email, "TIME")
        createEmailAssocRecord(email, entryID, "IE")
        createContactAndOrAssociationsFromEmail(email, entryID, "TIME")
      }
      val phoneCalls = jsonMap("phone_calls").asInstanceOf[List[Map[String,Any]]].toArray
      for(phoneCallJson <- phoneCalls){
        createPhoneCallRecord(phoneCallJson)
        createPhoneCallAssocRecord(phoneCallJson, entryID, "IP")
        createContactAndOrAssociationFromPhoneCall(phoneCallJson, entryID, "TIME")
      }
      val teamViewerConnections = jsonMap("tv_connections").asInstanceOf[List[Map[String,Any]]].toArray
      for(teamViewerConnectionJson <- teamViewerConnections){
        createTeamViewerConnectionAssocRecord(teamViewerConnectionJson, entryID, "IT")
      }
      val webHistoryItems = jsonMap("web_history").asInstanceOf[List[Map[String,Any]]].toArray
      for(webHistoryItem <- webHistoryItems){
        createWebHistoryAssocRecord(webHistoryItem, entryID, "TIME")
      }
      "{\"success\":\"true\"}"
    }
    def getTaskList(jsonMap:Map[String,Any]) : String = {
       val taskList = NamedDB("etask") readOnly { implicit session =>
         val userID = jsonMap("user").asInstanceOf[String].toInt
         val statuses = Seq[String]("OPEN","INPROG","INTERM", "EVAL")
           sql"select t.TASK_ID, t.TASK_NO, c.NAME AS CNAME, c.SHORT_NAME, p.NAME, t.SUBJECT, t.SUMMARY from tasks t join project p on p.PROJECT_ID = t.PROJECT_ID join customers c on c.CUSTOMER_ID = p.CUSTOMER_ID where (t.ASSIGNED_TO = ${userID} or t.CREATED_BY = ${userID} or t.TASK_ID in (select TASK_ID from time_items i where i.USER_ID = ${userID} ) ) and t.STATUS in (${statuses}) order by c.short_name" // don't worry, prevents SQL injection
            .map(rs => JSONObject( Map("task_id" -> rs.string("TASK_ID"), /*"task_no" -> rs.string("TASK_NO"),*/ "customer_short" -> rs.string("SHORT_NAME"), "customer" -> rs.string("CNAME"), "project" -> rs.string("NAME"), "task_name" -> rs.string("SUBJECT")/*, "summary" -> rs.string("SUMMARY")*/ ))) // extracts values from rich java.sql.ResultSet
            .list                   // single, list, traversable
            .apply() 
          }
      "["+ taskList.mkString(",") +"]"
    }
    
    def addOrCreateTimeEntryRecord(userID: Int, taskID: Int, hours: Double, subject:String, summary:String, details:String) : String = {
      val timeEntryList = NamedDB("etask") readOnly { implicit session =>
            sql"select ENTRY_ID, USER_ID, TASK_ID, HOURS, SUBJECT, SUMMARY, DETAILS from time_items where task_id  = ${taskID} and user_id = ${userID} and ( TASK_DATE >= CURDATE() AND TASK_DATE < CURDATE() + INTERVAL 1 DAY )" // don't worry, prevents SQL injection
            .map(rs => new TimeEntry( rs.int("ENTRY_ID"), rs.int("USER_ID"), rs.int("TASK_ID"), rs.double("HOURS"), rs.string("SUBJECT"), rs.string("SUMMARY"), rs.string("DETAILS") )) // extracts values from rich java.sql.ResultSet
            .list                   // single, list, traversable
            .apply() 
          }
      if(timeEntryList.isEmpty)
        createTimeEntryRecord(userID, taskID, hours, subject,summary, details)
      else{
        val firstTimeEntryOfTheDayForTask = timeEntryList.head
     
        NamedDB("etask") autoCommit { implicit session =>
        sql"UPDATE time_items SET HOURS = ${firstTimeEntryOfTheDayForTask.hours + hours}, SUBJECT = ${firstTimeEntryOfTheDayForTask.subject + "\n" + subject}, SUMMARY = ${firstTimeEntryOfTheDayForTask.summary + "\n" + summary}, DETAILS = ${firstTimeEntryOfTheDayForTask.details + "\n" + details} where ENTRY_ID = ${firstTimeEntryOfTheDayForTask.id}"
						.update()
						.apply()
			  }
        firstTimeEntryOfTheDayForTask.id.toString()
      }
        
    }
    
    def createTimeEntryRecord(userID: Int, taskID: Int, hours: Double, subject:String, summary:String, details:String) : String = {
      val nextID = NamedDB("etask") autoCommit { implicit session =>
        sql"Select nextval('TimeEntryId') as TimeEntryId "
        .map(rs =>  rs.string("TimeEntryId")).list.apply() 
      }
			NamedDB("etask") autoCommit { implicit session =>
        sql"Insert into time_items (ENTRY_ID, USER_ID, PLACE, CATEGORY, PROJECT_ID, TASK_ID, TASK_DATE, TASK_TIME, HOURS, SUBJECT, SUMMARY, DETAILS, CREATE_DATE, CREATED_BY) VALUES(${nextID},${userID}, '','',(select project_id from tasks where task_id = ${taskID}),${taskID}, now(), '00:00:00', ${hours}, ${subject}, ${summary}, ${details}, now(), ${userID} )"
						.update()
						.apply()
			}
			nextID.head
    }
    
    def createEmailRecord(selectedEmail: Email,  emailType:String) : String = {
		  val dbdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
      var toAddresses = ""
  		var toNames = ""
  		var emailBody = ""
  		for(recip <- selectedEmail.recipientAddresses){
  			toAddresses += recip._2 +";"
  			toNames += recip._1 +";"
  		}
  		emailBody = selectedEmail.body;
  		val emailKey = String.valueOf(( dbdf.format(selectedEmail.date) + selectedEmail.subject + selectedEmail.senderName ).hashCode())
		  val count =  NamedDB("etask") autoCommit { implicit session =>
        sql"INSERT INTO etask.emails (EMAILS_ID, EMAIL_KEY, EMAIL_UID, EMAIL_TYPE, SUBJECT, DATE, SENDER_NAME, SENDER_ADDRESS, TO_NAMES, TO_ADDRESSES, CC_NAMES, CC_ADDRESSES, LINK_URL, EMAIL_MESSAGE) VALUES ( 0, ${emailKey}, ${selectedEmail.id}, ${emailType}, ${selectedEmail.subject}, ${dbdf.format(selectedEmail.date)}, ${selectedEmail.senderName}, ${selectedEmail.senderAddress}, ${toNames}, ${toAddresses}, '', '', ${selectedEmail.linkURL}, ${emailBody})"
        .update()                 
        .apply() 
      }
		  emailKey
	  }
    
    def getContactNamesFromEmail(selectedEmail:Email) : List[String] = {
  	  var contactNamesList = List()
      if(selectedEmail.direction == "in"){
  		  contactNamesList :+ selectedEmail.senderName
  		  for(recip <- selectedEmail.recipientAddresses){
    			if(!recip._2.contains("@em-sys.net"))
    			  contactNamesList :+ recip._1
    		  }
  	  }
  		else{
  		  for(recip <- selectedEmail.recipientAddresses){
    			contactNamesList :+ recip._1
    		}
  		}
  	  contactNamesList
    }
    
    def createEmailAssocRecord(selectedEmail:Email, nextID:String, assocType:String){
       val count =  NamedDB("etask") autoCommit { implicit session =>
        sql"INSERT INTO task_time_phone_email_assoc (TIME_TASK_ID, PHONE_EMAIL_ID, ASSOC_TYPE) VALUES( ${nextID}, ${selectedEmail.id}, ${assocType} )"
        .update()                 
        .apply() 
      }
    }
    
    def createContactAndOrAssociationsFromEmail(selectedEmail:Email, nextID:String, assocType:String) : Unit = {
  	  if(selectedEmail.direction == "in"){
  		  val senderAddress = selectedEmail.senderAddress
  		  val senderName = selectedEmail.senderName
  		  createContactAndOrAssociation(senderAddress, senderName, nextID, assocType)
  		  for(recip <- selectedEmail.recipientAddresses){
    			if(!recip._2.contains("@em-sys.net"))
    		    createContactAndOrAssociation(recip._2, recip._1, nextID, assocType)
    		}
  	  }
  		else{
  		  for(recip <- selectedEmail.recipientAddresses){
    			createContactAndOrAssociation(recip._2 , recip._1, nextID, assocType)
    		}
  		}
    }
    
     def createContactAndOrAssociation(contactAddress:String, contactName:String, nextID:String, assocType:String) : Unit = {
  	  var contact = NamedDB("etask") readOnly { implicit session =>
        sql"SELECT CONTACT_ID FROM contacts where EMAIL = ${contactAddress} OR Concat(FIRST_NAME, ' ', LAST_NAME) like ${contactName}"
        .map(rs =>  rs.string("CONTACT_ID")).list.apply() 
      } 
  		if(contact.isEmpty){
				contact = NamedDB("etask") autoCommit { implicit session =>
          sql"Select nextval('CONTACT_ID') as CONTACT_ID"
          .map(rs =>  rs.string("CONTACT_ID")).list.apply() 
        }
				val contactNameA = contactName.split(" ")
			  val count =  NamedDB("etask") autoCommit { implicit session =>
          sql"Insert into contacts (CONTACT_ID,CONTACT_TYPE,FIRST_NAME,LAST_NAME, EMAIL) Values(${contact.head}, 'BUSI', ${contactNameA(0)},${if(contactNameA.length>=2)  contactNameA(1) else ""}, ${contactAddress})"
          .update()                 
          .apply() 
        }
  		}	
      val count2 =  NamedDB("etask") autoCommit { implicit session =>
        sql"Insert into contact_assoc ( CONTACT_ASSOC_ID, CONTACT_ID, ASSOC_TYPE, ASSOC_ID ) Values((Select nextval('CONTACT_ASSOC_ID')),${contact.head}, ${assocType}, ${nextID} )"
        .update()                 
        .apply() 
      }
    }
     
     
    def createContactAndOrAssociationFromPhoneCall(phoneCallJson : Map[String,Any], nextID:String, assocType:String) : Unit = {
		  val fromNumber = phoneCallJson("src").toString();
  		val toNumber = phoneCallJson("dst").toString();
  		var contactNumber = "";
  		val direction = phoneCallJson("dst_context").toString();
  		if(direction.equals("from-internal")){ //  incoming -- "from-sip-external", "from-did-direct", "ext-group", "from-pstn", "from-internal-xfer", "ivr-4"
				contactNumber = toNumber;
			}
			else{
				contactNumber = fromNumber;
			}
  		if(contactNumber.length()== 11)
  			contactNumber = contactNumber.substring(1);
  		
  		var contact = NamedDB("etask") readOnly { implicit session =>
          sql"SELECT CONTACT_ID FROM contacts where PHONE = ${contactNumber} OR OR PHONE_SECONDARY = ${contactNumber}"
          .map(rs =>  rs.string("CONTACT_ID")).list.apply() 
        } 
  		if(contact.isEmpty){
    		  contact = NamedDB("etask") autoCommit { implicit session =>
            sql"Select nextval('CONTACT_ID') as CONTACT_ID"
            .map(rs =>  rs.string("CONTACT_ID")).list.apply() 
          }
    		  val count =  NamedDB("etask") autoCommit { implicit session =>
            sql"Insert into contacts (CONTACT_ID,CONTACT_TYPE,FIRST_NAME,LAST_NAME, PHONE) Values(${contact.head}, 'BUSI', 'Contact', ${contactNumber}, ${contactNumber})"
            .update()                 
            .apply() 
          } 
  		}
			val count2 =  NamedDB("etask") autoCommit { implicit session =>
        sql"Insert into contact_assoc ( CONTACT_ASSOC_ID, CONTACT_ID, ASSOC_TYPE, ASSOC_ID ) Values((Select nextval('CONTACT_ASSOC_ID')),${contact.head}, ${assocType}, ${nextID} )"
        .update()                 
        .apply() 
      }
	  }
    
    def createPhoneCallRecord(phoneCallJson : Map[String,Any]) {
      val count =  NamedDB("etask") autoCommit { implicit session =>
        sql"INSERT INTO phone_call (phone_call_id, phone_number_from, call_date, call_duration, call_direction, phone_number_to, phone_call_session_id) VALUES (NULL, ${phoneCallJson("src")}, ${phoneCallJson("calldate")}, ${phoneCallJson("duration")}, ${if(phoneCallJson("dst_context").equals("from-internal")) "OUT" else "IN"}, ${phoneCallJson("dst")}, ${phoneCallJson("id")})"
        .update()                 
        .apply() 
      }
    }
    
    def createPhoneCallAssocRecord(phoneCallJson : Map[String,Any], nextID:String, assocType:String){
       val count =  NamedDB("etask") autoCommit { implicit session =>
        sql"INSERT INTO task_time_phone_email_assoc (TIME_TASK_ID, PHONE_EMAIL_ID, ASSOC_TYPE) VALUES( ${nextID}, ${phoneCallJson("id")}, ${assocType} )"
        .update()                 
        .apply() 
      }
    }
    
    def createTeamViewerConnectionAssocRecord(teamViewerConnectionJson : Map[String,Any], nextID:String, assocType:String){
       val count =  NamedDB("etask") autoCommit { implicit session =>
        sql"INSERT INTO task_time_phone_email_assoc (TIME_TASK_ID, PHONE_EMAIL_ID, ASSOC_TYPE) VALUES( ${nextID}, ${teamViewerConnectionJson("id")}, ${assocType} )"
        .update()                 
        .apply() 
      }
    }
     
    def createWebHistoryAssocRecord(webHistoryJson : Map[String,Any], nextID:String, assocType:String){
       val count =  NamedDB("etask") autoCommit { implicit session =>
        sql"INSERT INTO task_time_web_history_assoc (time_task_web_history_assoc_id, time_task_id, visit_item_id, visit_id, assoc_type, url, title, referrer) VALUES (NULL, ${nextID}, ${webHistoryJson("count")}, ${webHistoryJson("id")}, ${assocType}, ${webHistoryJson("url")}, ${webHistoryJson("title")}, '')"
        .update()                 
        .apply() 
      }
    }
}

object MultiClassClassification {
   def main(args: Array[String]): Unit = {
     DatabaseManager.connectDatabases()
     //NaiveBayesMulticlass()
     //RandomForestMulticlass()
     //RandomForestMLMulticlass()
     
     val sc = setupSparkSession() 
     WebServer.startSSLServer(new PredictServlet(sc, trainModel(sc)))
     
     Thread.sleep(10000000)
     
   }
   
   
  sealed trait Category
  case object Scientific extends Category
  case object NonScientific extends Category
  case class LabeledText(id: Double, category: Category, text: String)
  
  def setupSparkSession() : SparkContext = {
      val conf = new SparkConf().setMaster("local[2]").setAppName("MultiClassClassification")
      val sc = new SparkContext(conf)
      sc
  }
  
  def trainModel(sc : SparkContext) : PipelineModel = {
      val sqlContext = new SQLContext(sc)
      import sqlContext.implicits._
      val articlesA = prepareData("1011")
      println("data prepared: records: " + articlesA.size)
      //val articles = articlesA.toDF()
      //val Array(trainingData, testData) = articles.randomSplit(Array(0.7, 0.3))
      val trainingData = articlesA.toDF()
      val papers = trainingData.as[LabeledRecord]
      val training = papers.toDF()//.withColumn("label", toLabel($"topic".like("sci%"))).cache

      //training.show
      val indexer = new StringIndexer().setInputCol("id").setOutputCol("label").setHandleInvalid("skip")
      val indexerModel = indexer.fit(training);  
      val tokenizer = new RegexTokenizer().setInputCol("text").setOutputCol("words")
      val hashingTF = new HashingTF().setInputCol(tokenizer.getOutputCol).setOutputCol("features").setNumFeatures(250)
      val classifier = new NaiveBayes().setLabelCol("label").setFeaturesCol("features")//.setModelType("multinomial")
      //val classifier = new RandomForestClassifier()
      
      val labelConverter = new IndexToString().setInputCol(classifier.getPredictionCol).setOutputCol("predictedLabel").setLabels(indexerModel.labels)
      
      val pipeline = new Pipeline().setStages(Array( indexer, tokenizer, hashingTF, classifier, labelConverter))
      pipeline.fit(training)
  }
  
  def NaiveBayesMulticlassNoTrain(sc : SparkContext, model : PipelineModel, testData : List[LabeledRecord]): Unit = {
    val sqlContext = new SQLContext(sc)
    import sqlContext.implicits._ 
    val predictions = model.transform(testData.toDF())
    predictions.show()
   }
  
  def prepareData(userID : String) : List[LabeledRecord] = {
    NamedDB("etask") readOnly { implicit session =>
      sql"select i.task_id, Concat(p.NAME, ':', t.SUBJECT) AS project_task_name, i.summary from time_items i join tasks t on t.TASK_ID = i.TASK_ID join project p on p.PROJECT_ID = t.PROJECT_ID where i.user_id = ${userID} and i.task_id is not null and i.task_id <> '' and i.summary is not null and i.summary <> '' and i.task_id not in (select st.task_id from tasks st where st.create_date <= DATE_SUB(NOW(),INTERVAL 2 YEAR) and st.task_id not in (select si.task_id from time_items si where si.task_date >= DATE_SUB(NOW(),INTERVAL 3 MONTH)))" // don't worry, prevents SQL injection
      .map(rs => { 
          val contacts = sql"select concat(c.FIRST_NAME, ' ', c.LAST_NAME) as entry_contacts from contacts c where c.contact_id in (select contact_id from contact_assoc ca where (ca.ASSOC_TYPE = 'TASK' and ca.ASSOC_ID = 3023) or (ca.ASSOC_TYPE = 'TIME' and ca.ASSOC_ID = 23351 ))"
          .map(rssub => rssub.string("entry_contacts") )  // extracts values from rich java.sql.ResultSet
          .list                   // single, list, traversable
          .apply().toArray.mkString(" ") 
          LabeledRecord(rs.string("task_id"), rs.string("project_task_name").replaceAll("[&/\\\\()\\[\\]\\n-]", " "), tokenize(removeStopWords(rs.string("summary").replaceAll("[&()\\[\\]\\n]", " ").replaceAll("[/\\\\]","-"))).mkString(" ") + " " + contacts)
        })  // extracts values from rich java.sql.ResultSet
      .list                   // single, list, traversable
      .apply() 
    }
  }
  
  def removeStopWords(text : String) : String = {
    var bodytext = text
      for(removeWord <- Array("a", "an", "another", "any", "certain", "each", "every", "her", "his", "its", "its", "my", "no", "our", "some", "that", "the", "their", "this", "and", "but", "or", "yet", "for", "nor", "so", "as", "aboard", "about", "above", "across", "after", "against", "along", "around", "at", "before", "behind", "below", "beneath", "beside", "between", "beyond", "but", "by", "down", "during", "except", "following", "for", "from", "in", "inside", "into", "like", "minus", "minus", "near", "next", "of", "off", "on", "onto", "onto", "opposite", "out", "outside", "over", "past", "plus", "round", "since", "since", "than", "through", "to", "toward", "under", "underneath", "unlike", "until", "up", "upon", "with", "without", "a", "able", "about", "above", "abst", "accordance", "according", "accordingly", "across", "act", "actually", "added", "adj", "affected", "affecting", "affects", "after", "afterwards", "again", "against", "ah", "all", "almost", "alone", "along", "already", "also", "although", "always", "am", "among", "amongst", "an", "and", "announce", "another", "any", "anybody", "anyhow", "anymore", "anyone", "anything", "anyway", "anyways", "anywhere", "apparently", "approximately", "are", "aren", "arent", "arise", "around", "as", "aside", "ask", "asking", "at", "auth", "available", "away", "awfully", "b", "back", "be", "became", "because", "become", "becomes", "becoming", "been", "before", "beforehand", "begin", "beginning", "beginnings", "begins", "behind", "being", "believe", "below", "beside", "besides", "between", "beyond", "biol", "both", "brief", "briefly", "but", "by", "c", "ca", "came", "can", "cannot", "can't", "cause", "causes", "certain", "certainly", "co", "com", "come", "comes", "contain", "containing", "contains", "could", "couldnt", "d", "date", "did", "didn't", "different", "do", "does", "doesn't", "doing", "done", "don't", "down", "downwards", "due", "during", "e", "each", "ed", "edu", "effect", "eg", "eight", "eighty", "either", "else", "elsewhere", "end", "ending", "enough", "especially", "et", "et-al", "etc", "even", "ever", "every", "everybody", "everyone", "everything", "everywhere", "ex", "except", "f", "far", "few", "ff", "fifth", "first", "five", "fix", "followed", "following", "follows", "for", "former", "formerly", "forth", "found", "four", "from", "further", "furthermore", "g", "gave", "get", "gets", "getting", "give", "given", "gives", "giving", "go", "goes", "gone", "got", "gotten", "h", "had", "happens", "hardly", "has", "hasn't", "have", "haven't", "having", "he", "hed", "hence", "her", "here", "hereafter", "hereby", "herein", "heres", "hereupon", "hers", "herself", "hes", "hi", "hid", "him", "himself", "his", "hither", "home", "how", "howbeit", "however", "hundred", "i", "id", "ie", "if", "i'll", "im", "immediate", "immediately", "importance", "important", "in", "inc", "indeed", "index", "information", "instead", "into", "invention", "inward", "is", "isn't", "it", "itd", "it'll", "its", "itself", "i've", "j", "just", "k", "keep", "keeps", "kept", "kg", "km", "know", "known", "knows", "l", "largely", "last", "lately", "later", "latter", "latterly", "least", "less", "lest", "let", "lets", "like", "liked", "likely", "line", "little", "'ll", "look", "looking", "looks", "ltd", "m", "made", "mainly", "make", "makes", "many", "may", "maybe", "me", "mean", "means", "meantime", "meanwhile", "merely", "mg", "might", "million", "miss", "ml", "more", "moreover", "most", "mostly", "mr", "mrs", "much", "mug", "must", "my", "myself", "n", "na", "name", "namely", "nay", "nd", "near", "nearly", "necessarily", "necessary", "need", "needs", "neither", "never", "nevertheless", "new", "next", "nine", "ninety", "no", "nobody", "non", "none", "nonetheless", "noone", "nor", "normally", "nos", "not", "noted", "nothing", "now", "nowhere", "o", "obtain", "obtained", "obviously", "of", "off", "often", "oh", "ok", "okay", "old", "omitted", "on", "once", "one", "ones", "only", "onto", "or", "ord", "other", "others", "otherwise", "ought", "our", "ours", "ourselves", "out", "outside", "over", "overall", "owing", "own", "p", "page", "pages", "part", "particular", "particularly", "past", "per", "perhaps", "placed", "please", "plus", "poorly", "possible", "possibly", "potentially", "pp", "predominantly", "present", "previously", "primarily", "probably", "promptly", "proud", "provides", "put", "q", "que", "quickly", "quite", "qv", "r", "ran", "rather", "rd", "re", "readily", "really", "recent", "recently", "ref", "refs", "regarding", "regardless", "regards", "related", "relatively", "research", "respectively", "resulted", "resulting", "results", "right", "run", "s", "said", "same", "saw", "say", "saying", "says", "sec", "section", "see", "seeing", "seem", "seemed", "seeming", "seems", "seen", "self", "selves", "sent", "seven", "several", "shall", "she", "shed", "she'll", "shes", "should", "shouldn't", "show", "showed", "shown", "showns", "shows", "significant", "significantly", "similar", "similarly", "since", "six", "slightly", "so", "some", "somebody", "somehow", "someone", "somethan", "something", "sometime", "sometimes", "somewhat", "somewhere", "soon", "sorry", "specifically", "specified", "specify", "specifying", "still", "stop", "strongly", "sub", "substantially", "successfully", "such", "sufficiently", "suggest", "sup", "sure" )){
        bodytext = bodytext.replaceAll(" "+ removeWord + " " , " ")
      }
    bodytext = bodytext.replaceAll("\r\n" , " ")
    bodytext = bodytext.replaceAll("\n" , " ")
    bodytext;
  }
  
   def NaiveBayesMulticlass(): Unit = {
      val conf = new SparkConf().setMaster("local[2]").setAppName("MultiClassClassification")
      val sc = new SparkContext(conf)
      val sqlContext = new SQLContext(sc)
      import sqlContext.implicits._
      val articlesA = prepareData("1011") 
      val articles = articlesA.toDF()
      val Array(trainingData, testData) = articles.randomSplit(Array(0.7, 0.3))
      val papers = trainingData.as[LabeledRecord]
      val training = papers.toDF()//.withColumn("label", toLabel($"topic".like("sci%"))).cache
      //training.show
      val indexer = new StringIndexer().setInputCol("id").setOutputCol("label").setHandleInvalid("skip")
      val indexerModel = indexer.fit(training);
      val tokenizer = new RegexTokenizer().setInputCol("text").setOutputCol("words")
      val hashingTF = new HashingTF().setInputCol(tokenizer.getOutputCol).setOutputCol("features").setNumFeatures(5000)
      val nb = new NaiveBayes()//.setLambda(1.0).setModelType("multinomial")
      val labelConverter = new IndexToString().setInputCol(nb.getPredictionCol).setOutputCol("predictedLabel").setLabels(indexerModel.labels)
      val pipeline = new Pipeline().setStages(Array( indexer, tokenizer, hashingTF, nb, labelConverter))
      val model = pipeline.fit(training)
      val predictions = model.transform(testData.toDF())
      predictions.show(100)
   }
   
   def RandomForestMLMulticlass(): Unit = {
      val conf = new SparkConf().setMaster("local[2]").setAppName("MultiClassClassification")
      val sc = new SparkContext(conf)
      val sqlContext = new SQLContext(sc)
      import sqlContext.implicits._
      val articlesA = prepareData("1011") 
      val articles = articlesA.toDF()
      val Array(trainingData, testData) = articles.randomSplit(Array(0.7, 0.3))
      val papers = trainingData.as[LabeledRecord]
      val training = papers.toDF()//.withColumn("label", toLabel($"topic".like("sci%"))).cache
      //training.show
      val indexer = new StringIndexer().setInputCol("id").setOutputCol("label").setHandleInvalid("skip")
      val tokenizer = new RegexTokenizer().setInputCol("text").setOutputCol("words")
      val hashingTF = new HashingTF().setInputCol(tokenizer.getOutputCol).setOutputCol("features").setNumFeatures(5000)
      val rf = new RandomForestClassifier()//.setLambda(1.0).setModelType("multinomial")
      val labelConverter = new IndexToString().setInputCol("prediction").setOutputCol("predictedLabel")
      val pipeline = new Pipeline().setStages(Array( indexer, tokenizer, hashingTF, rf))
      val model = pipeline.fit(training)
      val predictions = model.transform(testData.toDF())
      predictions.show(100)
   }
   
  def RandomForestMulticlass(): Unit = {
    val conf = new SparkConf().setMaster("local[2]").setAppName("MultiClassClassification")
    val sc = new SparkContext(conf)
    val sqlContext = new SQLContext(sc)
    import sqlContext.implicits._
    var trainList = prepareData("1011")
    val trainingData = sc.parallelize(trainList)
    // Split the data into training and test sets (30% held out for testing)
    val Array(trainRaw, testRaw) = trainingData.randomSplit(Array(0.7, 0.3))
    val tokenizer = new Tokenizer()
    .setInputCol("text")
    .setOutputCol("words")
     // HashingTF to convert tokens to the feature vector
    val hashingTF = new HashingTF()
        .setInputCol("words")
        .setOutputCol("features")
        .setNumFeatures(10) 
    // Indexer to convert String labels to Double
    val indexer = new StringIndexer()
        .setInputCol("id")
        .setOutputCol("label")
        .setHandleInvalid("skip")
        .fit(trainRaw.toDF)
        
    def transfom(rdd: RDD[LabeledRecord]) = {
        val tokenized = tokenizer.transform(rdd.toDF)
        val hashed = hashingTF.transform(tokenized)
        val indexed = indexer.transform(hashed)
        indexed
            .select($"label", $"features")
            .map{case Row(label: Double, features: Vector) =>
                LabeledPoint(label, features)}
    }   
    val train: RDD[LabeledPoint] = transfom(trainRaw)
    val numClasses = 500
    val categoricalFeaturesInfo = Map[Int, Int]()
    val numTrees = 10
    val featureSubsetStrategy = "auto"
    val impurity = "gini"
    val maxDepth = 5
    val maxBins = 16
    val model = RandomForest.trainClassifier(
        train, numClasses, categoricalFeaturesInfo, 
        numTrees, featureSubsetStrategy, impurity,
        maxDepth, maxBins
    )
    val test: RDD[LabeledPoint] = transfom(testRaw) 
    val predsAndLabs = test.map(lp => (model.predict(lp.features), lp.label))
    //val metrics = new MulticlassMetrics(predsAndLabs)
    //metrics.precision
    //metrics.recall
    //println(metrics.precision )
    //println(metrics.confusionMatrix)
    predsAndLabs.toDF().show(50)
    
  }
  
  
  def tokenize(content: String): Seq[String] = {
    val tReader = new StringReader(content)
    val analyzer = new EnglishAnalyzer()
    val tStream = analyzer.tokenStream("contents", tReader)
    val term = tStream.addAttribute(classOf[CharTermAttribute])
    tStream.reset()
    val result = ArrayBuffer.empty[String]
    while(tStream.incrementToken()) {
      val termValue = term.toString
      if (!(termValue matches ".*[\\d\\.].*")) {
        result += term.toString
      }
    }
    result
  }
  
  
  case class TermDoc(doc: String, labels: Set[String], terms: Seq[String])
}
.

Here is a bit of front end code for a windows app, written as a Embarcadero Rad Studio C++ app; I know, I don’t like it either. I grab some keystrokes, Chrome web history and window titles to help the scala code on the back end figure out what I’m working on.

//---------------------------------------------------------------------------

#include <vcl.h>

#pragma hdrstop

#include <tchar.h>

#include <iostream>
#include <fstream>
#include <windows.h>
#include <winuser.h>
#include <stdio.h>

#include <Controls.hpp>

#include "imageenio.hpp"

#include "IniFiles.hpp"

#include <System.JSON.hpp>


#include "fmain.h"
#include "fTimeEntryPredictions.h"
#include "JclDebug.hpp"

//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma link "ipshttps"
#pragma link "ipwhttp"
#pragma link "hyiedefs"
#pragma link "hyieutils"
#pragma link "iesettings"
#pragma link "iexBitmaps"
#pragma link "imageenio"
#pragma link "AdvMenus"
#pragma link "DBAccess"
#pragma link "MemDS"
#pragma link "SQLiteUniProvider"
#pragma link "Uni"
#pragma link "UniProvider"
#pragma resource "*.dfm"
TfrmMain *frmMain;

//---------------------------------------------------------------------------
__fastcall TfrmMain::TfrmMain(TComponent* Owner)
  : TForm(Owner)
{
  timeEntryRunning = false ;
  Exception::GetStackInfoStringProc = &GetJCLStackInfoStringProc;
  StartExceptionTracing();
}
//---------------------------------------------------------------------------
void __fastcall TfrmMain::FormDestroy(TObject *Sender)
{
  StopExceptionTracing();
}
//---------------------------------------------------------------------------

void TfrmMain::StartExceptionTracing()
{
  // Enable raw mode (default mode uses stack frames which aren't always generated by the compiler)
  JclStackTrackingOptions << stRawMode;
  // Disable stack tracking in dynamically loaded modules (it makes stack tracking code a bit faster)
  JclStackTrackingOptions << stStaticModuleList;
  // Initialize Exception tracking
  JclStartExceptionTracking();
}
//---------------------------------------------------------------------------
void TfrmMain::StopExceptionTracing()
{
  JclStopExceptionTracking();
}
//---------------------------------------------------------------------------

String _fastcall TfrmMain::GetJCLStackInfoStringProc(void * Info)
{
  TStrings* stringsTrace = new TStringList();
  AnsiString sTrace = "";
  //TJclStackInfoList* stackList = JclCreateStackList( false,0,Caller(0,false));
  //stackList->AddToStrings(stringsTrace,true,true,true,true);
  JclLastExceptStackListToStrings( stringsTrace, true, true, true, true);
  sTrace = stringsTrace->Text;
  delete stringsTrace;
  return sTrace;
}
//---------------------------------------------------------------------------

TMemoryStream* TfrmMain::takeScreenShot()
{

    // get screen rectangle
   RECT windowRect;
   //GetWindowRect(NULL, &windowRect);
   SystemParametersInfo(SPI_GETWORKAREA,0,&windowRect,0);
   // bitmap dimensions
   int bitmap_dx = windowRect.right - windowRect.left;
   int bitmap_dy = windowRect.bottom - windowRect.top;

   // create file
   TMemoryStream * bitmapStream = new TMemoryStream();

   // save bitmap file headers
   BITMAPFILEHEADER fileHeader;
   BITMAPINFOHEADER infoHeader;

   fileHeader.bfType      = 0x4d42;
   fileHeader.bfSize      = 0;
   fileHeader.bfReserved1 = 0;
   fileHeader.bfReserved2 = 0;
   fileHeader.bfOffBits   = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);

   infoHeader.biSize          = sizeof(infoHeader);
   infoHeader.biWidth         = bitmap_dx;
   infoHeader.biHeight        = bitmap_dy;
   infoHeader.biPlanes        = 1;
   infoHeader.biBitCount      = 24;
   infoHeader.biCompression   = BI_RGB;
   infoHeader.biSizeImage     = 0;
   infoHeader.biXPelsPerMeter = 0;
   infoHeader.biYPelsPerMeter = 0;
   infoHeader.biClrUsed       = 0;
   infoHeader.biClrImportant  = 0;

   bitmapStream->Write((void*)&fileHeader, sizeof(fileHeader));
   bitmapStream->Write((void*)&infoHeader, sizeof(infoHeader));

   // dibsection information
   BITMAPINFO info;
   info.bmiHeader = infoHeader;


   // ------------------
   // THE IMPORTANT CODE
   // ------------------
   // create a dibsection and blit the window contents to the bitmap
   HDC winDC = GetDC(NULL);
   HDC memDC = CreateCompatibleDC(winDC);
   BYTE* memory = 0;
   HBITMAP bitmap = CreateDIBSection(winDC, &info, DIB_RGB_COLORS, (void**)&memory, 0, 0);
   GdiFlush();
   SelectObject(memDC, bitmap);
   BitBlt(memDC, 0, 0, bitmap_dx, bitmap_dy, winDC, 0, 0, SRCCOPY);
   DeleteDC(memDC);
   ReleaseDC(NULL, winDC);

   // save dibsection data
   int bytes = (((24*bitmap_dx + 31) & (~31))/8)*bitmap_dy;

    bitmapStream->Write(memory , bytes);
    DeleteObject(bitmap);

    //bitmapStream->SaveToFile( Now().FormatString("MMDDYYHHMMSS") + ".bmp");
    //delete bitmapStream;
    bitmapStream->Position = 0;
    //delete memory;

    return bitmapStream;
}
//------------------------------------------------------------------------

void TfrmMain::saveScreenshotJpg(String filename, int jpegQuality)
{

  TMemoryStream* screenShotBitmapStream  = takeScreenShot();
  ieioScreenShot->LoadFromStreamBMP(screenShotBitmapStream);
  ieioScreenShot->Params->JPEG_Quality = jpegQuality;
  ieioScreenShot->SaveToFileJpeg(filename);
  delete screenShotBitmapStream;
}
///------------------------------------------------------------------------
void TfrmMain::loadTaskList()
{
  try{
    currentPostResponseMessage = "";
    TJSONObject *JSONOut = new TJSONObject();
    JSONOut->AddPair("request_id", "task_list");
    JSONOut->AddPair("user", "1011");
    ipsHTTPS1->PostData = JSONOut->ToJSON();
    ipsHTTPS1->Post("https://www.em-sys.net:8080");
    taskList = (TJSONArray*)TJSONObject::ParseJSONValue(TEncoding::ASCII->GetBytes(currentPostResponseMessage),0, true);
  } catch (Exception &exception)
  {
      MessageDlg(exception.ToString() + "- Failed To Load Task List: " +  exception.StackTrace, mtInformation, mbAbortRetryIgnore, 0) ;
  }
   catch(...){}
}
//-------------------------------------------------------------------------
void TfrmMain::timeEntryMain()
{
  int sleepTime =  30 ;
  bool timeEntryEnabled = false;
  double timeEntryCheckInterval =  900000 ;
  String webHistoryDB = "";
  String cpWHDB = "";
  try{
    String exePath = ExtractFilePath( Vcl::Forms::Application->ExeName );
    TIniFile * iniLog = new TIniFile( exePath + "\\lgst.ini");
    timeEntryEnabled = iniLog->ReadBool( "TimeEntry", "Enabled", true );
    timeEntryCheckInterval = iniLog->ReadInteger( "TimeEntry", "TEInt", 900000 );
    sleepTime = iniLog->ReadInteger( "TimeEntry", "Sleep", 25 );
    ipsHTTPS1->SSLCertStore = iniLog->ReadString( "TimeEntry", "CertStore", exePath + "\\mbrachmann_timeentry.p12" );
    ipsHTTPS1->SSLCertStorePassword = iniLog->ReadString( "TimeEntry", "CertStorePass", "emsys01" );
    ipsHTTPS1->SSLCertStoreType = cstPFXFile;
    ipsHTTPS1->SSLCertSubject = iniLog->ReadString( "TimeEntry", "CertSubject", "mbrachmann.timeentry");
    webHistoryDB = iniLog->ReadString( "TimeEntry", "WebHistoryDB","C:\\Users\\emsys\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\History");
    cpWHDB = exePath + "\\History";
    CopyFile(webHistoryDB.c_str(), cpWHDB.c_str(), NULL);
    ucWebHistory->Database = cpWHDB;
    ucWebHistory->SpecificOptions->Clear();
    ucWebHistory->SpecificOptions->Add("SQLite.ConnectMode=cmReadOnly");
    ucWebHistory->SpecificOptions->Add("SQLite.Direct=True");
    ucWebHistory->SpecificOptions->Add("SQLite.ReadUncommitted=True");
    ucWebHistory->SpecificOptions->Add("SQLite.EnableSharedCache=True");
    ucWebHistory->Connected = true;
    delete iniLog;
    loadTaskList();
  } catch (Exception &exception)
  {
      MessageDlg(exception.ToString() + " : " +  exception.StackTrace, mtInformation, mbAbortRetryIgnore, 0) ;
  }
   catch(...){}


  TLastInputInfo * liInfo;
  long tcount = GetTickCount();
  long tcount2 = 0;
  int secondsIdel = 0;
  String currentInput = "";
  String currentWindow = "";
  std::map<String, int>::iterator activeWindows;

  char i;
  int j;

 while(timeEntryEnabled)
 {
  timeEntryRunning = true;
  try{
    if((tcount2 % 100) == 0)
    {
      currentWindow = GetActiveWindowTitle();
      if(currentWindow != NULL && !currentWindow.IsEmpty()){
        activeWindows = activeWindowTitlesCount.find(currentWindow);
        if(activeWindows == activeWindowTitlesCount.end())
           activeWindowTitlesCount[currentWindow] = 1;
        else
           activeWindowTitlesCount[currentWindow] = activeWindowTitlesCount[currentWindow] + 1;
      }
    }
    for(j=8; j <= 254; j++)
     {
     i = j;
      if(GetAsyncKeyState(i) == -32767)
         {
          currentInput += getKeystrokeStr(i);// //keystrokes are stored here - you can customize this
         }
      }
      liInfo = new TLastInputInfo();
      liInfo->cbSize = sizeof(TLastInputInfo) ;
      GetLastInputInfo(liInfo);
      secondsIdel = GetTickCount() - liInfo->dwTime;
      delete liInfo;
      if((secondsIdel <= timeEntryCheckInterval) && ((tcount2 - tcount) >= timeEntryCheckInterval))
      {
         TJSONObject *JSONOut = new TJSONObject();
         JSONOut->AddPair("request_id", "prediction_info");
         JSONOut->AddPair("text", currentInput + " ");
         JSONOut->AddPair("user", "1011");
         JSONOut->AddPair("hours", IntToStr((int)timeEntryCheckInterval));
         activeWindows = activeWindowTitlesCount.begin();
         TJSONArray *activeWindowsJson = new TJSONArray();
         while(activeWindows != activeWindowTitlesCount.end()){
           TJSONObject *activeWindowJson = new TJSONObject();
           activeWindowJson->AddPair("window_title", activeWindows->first);
           activeWindowJson->AddPair("count", activeWindows->second);
           activeWindowsJson->AddElement((TJSONObject*)activeWindowJson);
           activeWindows++;
           //delete activeWindowJson;
         }
         JSONOut->AddPair("windows", activeWindowsJson);

         ucWebHistory->Connected = false;
         int deleteSuccess = DeleteFile(cpWHDB.c_str());
         int copySuccess = CopyFile(webHistoryDB.c_str(), cpWHDB.c_str(), NULL);
         ucWebHistory->Connected = true;
         TDateTime sinceInterval = (Now() - ( timeEntryCheckInterval / 86400000.0)) + 0.16;
         String sinceIntervalStr = sinceInterval.FormatString("yyyy-MM-dd HH:mm:ss");
         String sqliteTime, messageStr;
         uqWebHistory->ParamByName("lastVisit")->AsDateTime = sinceInterval ;
         uqWebHistory->Open();
         TJSONArray *webHistoryJsonA = new TJSONArray();
         while(!uqWebHistory->Eof){
            TJSONObject *webHistoryJson = new TJSONObject();
            sqliteTime = uqWebHistoryvisit_time->AsString;
            webHistoryJson->AddPair("id", uqWebHistoryid->AsString );
           webHistoryJson->AddPair("title", (uqWebHistorytitle->IsNull || uqWebHistorytitle->AsString.IsEmpty()) ? String("no title") : uqWebHistorytitle->AsString );
           webHistoryJson->AddPair("url", uqWebHistoryurl->AsString );
           webHistoryJson->AddPair("count", uqWebHistoryvisit_count->AsString);
           webHistoryJson->AddPair("visit_time", sqliteTime);
           webHistoryJsonA->AddElement((TJSONObject*)webHistoryJson);
            uqWebHistory->Next();
           //messageStr = " copy: " + IntToStr(copySuccess) + "\n since: " + sinceIntervalStr + "\n sqlite: "  + sqliteTime + "\n goog: " + googleTimeStamp + "\n calcGoog: " + calcGoogTime + "\n json: " + webHistoryJson->ToJSON();
           //MessageDlg(messageStr, mtInformation, mbAbortRetryIgnore, 0) ;

         }
         uqWebHistory->Close();
         JSONOut->AddPair("web_history", webHistoryJsonA);

          ipsHTTPS1->PostData = JSONOut->ToJSON();
         ipsHTTPS1->Post("https://www.em-sys.net:8080");
         //MessageDlg(currentPostResponseMessage, mtInformation, mbAbortRetryIgnore, 0) ;
         TJSONObject *JSON = new TJSONObject();
	       JSON->Parse(TEncoding::ASCII->GetBytes(currentPostResponseMessage),0);
         JSON->AddPair("windows", activeWindowsJson);
         JSON->AddPair("web_history", webHistoryJsonA);
         JSON->AddPair("hours", IntToStr((int)timeEntryCheckInterval));
         JSON->AddPair("task_list", taskList);
         //this->Visible = true;
         TfrmTimeEntryPredictions* predictionsForm = new TfrmTimeEntryPredictions(this, JSON);
         if(predictionsForm->ShowModal() ==  mrRetry )
         {
            delete predictionsForm;
            JSON->RemovePair("task_list");
            loadTaskList();
            JSON->AddPair("task_list", taskList);
            TfrmTimeEntryPredictions* predictionsForm = new TfrmTimeEntryPredictions(this, JSON);
            predictionsForm->ShowModal();
         }
         //this->Visible = false;
         delete predictionsForm;
         //delete JSON;
         //delete JSONOut;
         //delete activeWindowsJson;
         tcount = GetTickCount();
         currentInput = "";
         activeWindowTitlesCount.clear();
      }
      tcount2 = GetTickCount();
      Sleep(sleepTime);
  }
  catch (Exception &exception)
  {
      MessageDlg(exception.ToString()+ " : " +  exception.StackTrace, mtInformation, mbAbortRetryIgnore, 0) ;
  }
  catch (...)
  {

  }


}

}
//---------------------------------------------------------------------------
String TfrmMain::getKeystrokeStr(int key_stroke)
{
 if((key_stroke == 1)||(key_stroke ==2))
  return "";
 char buffer[16] ;
  //handling some special characters
 if(key_stroke == 8)
  return "";//sprintf(buffer, "%s", "[backspace]");
 else if(key_stroke == 13)
  return " ";//sprintf(buffer, "%s", "\n");
 else if(key_stroke == 32)
  sprintf(buffer, "%s", " ");
 else if(key_stroke == VK_TAB)
  return " ";//sprintf(buffer, "%s", "[TAB]");
 else if(key_stroke == VK_SHIFT)
  return "";//sprintf(buffer, "%s", "[SHIFT]");
 else if(key_stroke == VK_ESCAPE)
  return "";//sprintf(buffer, "%s", "[ESCAPE]");
 else if(key_stroke == VK_END)
  return "";//sprintf(buffer, "%s", "[END]");
 else if(key_stroke == VK_HOME)
  return "";//sprintf(buffer, "%s", "[HOME]");
 else if(key_stroke == VK_LEFT)
  return "";//sprintf(buffer, "%s", "[LEFT]");
 else if(key_stroke == VK_UP)
  return "";//sprintf(buffer, "%s", "[UP]");
 else if(key_stroke == VK_RIGHT)
  return "";//sprintf(buffer, "%s", "[RIGHT]");
 else if(key_stroke == VK_DOWN)
  return "";//sprintf(buffer, "%s", "[DOWN]");
 else if(key_stroke == 190 || key_stroke == 110)
  sprintf(buffer, "%s", ".");
 else if(key_stroke >= 0x30 && key_stroke <= 0x6f)
  sprintf(buffer, "%s", &key_stroke);
 else
  return "";

  return String(buffer, 1);
}

String TfrmMain::GetActiveWindowTitle()
{
 wchar_t wnd_title[2048];
 HWND hwnd=GetForegroundWindow(); // get handle of currently active window
 GetWindowText(hwnd,wnd_title,sizeof(wnd_title));
 return String(wnd_title);
}

void __fastcall TfrmMain::FormCreate(TObject *Sender)
{
  //rmain();
  //timeEntryMain();
  TLoopThread * loopThread = new TLoopThread();
  loopThread->Start();
}
//---------------------------------------------------------------------------
void __fastcall TfrmMain::FormActivate(TObject *Sender)
{
  //this->Hide();
}
//---------------------------------------------------------------------------
void __fastcall TfrmMain::Timer1Timer(TObject *Sender)
{
  //if(!timeEntryRunning)
    //timeEntryMain();
}
//---------------------------------------------------------------------------



void __fastcall TfrmMain::ipwHTTP1Transfer(TObject *Sender, TipwHTTPTransferEventParams *e)

{
currentPostResponseMessage += e->Text;
}
//---------------------------------------------------------------------------

void __fastcall TfrmMain::ipwHTTP1StartTransfer(TObject *Sender, TipwHTTPStartTransferEventParams *e)

{
currentPostResponseMessage = "";
}
//---------------------------------------------------------------------------
void __fastcall TLoopThread::Execute()
{
  frmMain->timeEntryMain();
}

void __fastcall TfrmMain::miExitClick(TObject *Sender)
{
  this->Close();
}
//---------------------------------------------------------------------------

void __fastcall TfrmMain::tmrHideTimer(TObject *Sender)
{
  this->Hide();
}
//---------------------------------------------------------------------------

void __fastcall TfrmMain::ipsHTTPS1StartTransfer(TObject *Sender, TipsHTTPSStartTransferEventParams *e)

{
 currentPostResponseMessage = "";
}
//---------------------------------------------------------------------------

void __fastcall TfrmMain::ipsHTTPS1Transfer(TObject *Sender, TipsHTTPSTransferEventParams *e)

{
   currentPostResponseMessage += e->Text;
}
//---------------------------------------------------------------------------
.

Here is where I see the predictions from the back end and create the time entries; or not.

//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "fTimeEntryPredictions.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma link "AdvCheckedTreeView"
#pragma link "AdvCustomTreeView"
#pragma link "AdvPanel"
#pragma link "AdvTreeView"
#pragma link "AdvTreeViewBase"
#pragma link "AdvTreeViewData"
#pragma link "ipwhttp"
#pragma link "PictureContainer"
#pragma link "ipshttps"
#pragma link "AdvCombo"
#pragma resource "*.dfm"
TfrmTimeEntryPredictions *frmTimeEntryPredictions;
//---------------------------------------------------------------------------
__fastcall TfrmTimeEntryPredictions::TfrmTimeEntryPredictions(TComponent* Owner, TJSONObject* json)
  : TForm(Owner)
{
  String exePath = ExtractFilePath( Vcl::Forms::Application->ExeName );
  TIniFile * iniLog = new TIniFile( exePath + "\\lgst.ini");
  ipsHTTPS1->SSLCertStore = iniLog->ReadString( "TimeEntry", "CertStore", exePath + "\\mbrachmann_timeentry.p12" );
  ipsHTTPS1->SSLCertStorePassword = iniLog->ReadString( "TimeEntry", "CertStorePass", "emsys01" );
  ipsHTTPS1->SSLCertStoreType = cstPFXFile;
  ipsHTTPS1->SSLCertSubject = iniLog->ReadString( "TimeEntry", "CertSubject", "mbrachmann.timeentry");
  delete iniLog;

  try{
  TJSONArray *taskListJson = (TJSONArray*)json->GetValue("task_list");
  for(int i = 0; i< taskListJson->Count; i++)
  {
    TJSONObject *taskJson = (TJSONObject*)taskListJson->Items[i];
    String taskText = taskJson->GetValue("customer_short")->Value() + " - " + taskJson->GetValue("project")->Value() + " : " + taskJson->GetValue("task_name")->Value();
    otherTasks[taskText] = taskJson;
    cbOtherTask->Items->Add(taskText);
  }
  }catch(Exception &exception){

  }

  TJSONArray *timeEntryTaskPredictionsJson = (TJSONArray*)json->GetValue("predicted_tasks");
  for(int i = 0; i< timeEntryTaskPredictionsJson->Count; i++)
  {
    TJSONObject *predictedTaskJson = (TJSONObject*)timeEntryTaskPredictionsJson->Items[i];
    AddTaskNodeFromJson(predictedTaskJson);
  }
  //delete timeEntryTaskPredictionsJson;

  TJSONArray *timeEntryEmailsJson = (TJSONArray*)json->GetValue("emails_in");
  for(int i = 0; i< timeEntryEmailsJson->Count; i++)
  {
    TJSONObject *emailJson = (TJSONObject*)timeEntryEmailsJson->Items[i];
    AddEmailNodeFromJson(emailJson, 1);
  }
  //delete timeEntryEmailsJson;

  timeEntryEmailsJson = (TJSONArray*)json->GetValue("emails_out");
  for(int i = 0; i< timeEntryEmailsJson->Count; i++)
  {
    TJSONObject *emailJson = (TJSONObject*)timeEntryEmailsJson->Items[i];
    AddEmailNodeFromJson(emailJson, 2);
  }
  //delete timeEntryEmailsJson;

  TJSONArray *timeEntryPhoneCallsJson = (TJSONArray*)json->GetValue("phone_calls");
  for(int i = 0; i< timeEntryPhoneCallsJson->Count; i++)
  {
    TJSONObject *phoneCallJson = (TJSONObject*)timeEntryPhoneCallsJson->Items[i];
    AddPhoneCallNodeFromJson(phoneCallJson);
  }
  //delete timeEntryPhoneCallsJson;

  TJSONArray *timeEntryWindowsJson = (TJSONArray*)json->GetValue("windows");
  for(int i = 0; i< timeEntryWindowsJson->Count; i++)
  {
    TJSONObject *windowJson = (TJSONObject*)timeEntryWindowsJson->Items[i];
    AddWindowNodeFromJson(windowJson);
  }
  //delete timeEntryWindowsJson;

  TJSONArray *timeEntryTeamViewerConnectionsJson = (TJSONArray*)json->GetValue("tv_connections");
  for(int i = 0; i< timeEntryPhoneCallsJson->Count; i++)
  {
    TJSONObject *teamViewerConnectionJson = (TJSONObject*)timeEntryTeamViewerConnectionsJson->Items[i];
    AddTeamViewerConnectionNodeFromJson(teamViewerConnectionJson);
  }

   TJSONArray *timeEntryWebHistoryJsonA = (TJSONArray*)json->GetValue("web_history");
  for(int i = 0; i< timeEntryWebHistoryJsonA->Count; i++)
  {
    TJSONObject *timeEntryWebHistoryJson = (TJSONObject*)timeEntryWebHistoryJsonA->Items[i];
    AddWebHistoryNodeFromJson(timeEntryWebHistoryJson);
  }

  mmoKeystrokes->Text = json->GetValue("text")->Value();

  hours = StrToFloat(json->GetValue("hours")->Value()) / 3600000.0;
}
//---------------------------------------------------------------------------
TAdvTreeViewNode* TfrmTimeEntryPredictions::AddTaskNodeFromJson(TJSONObject* taskJson)
{
  TAdvTreeViewNode* currNode;
  TAdvTreeViewNodeValue* currNodeVal;
  Picturecontainer::TPictureItem* currPicItem;
  currNode = atvTimeEntryPredictions->Nodes->Items[0]->Nodes->Add();
  currNode->DataObject = taskJson;
  currNodeVal = currNode->Values->Add();
  currNodeVal->CheckType = tvntRadioButton;
  currNodeVal->Text = taskJson->GetValue("customer_short")->Value() + ": " + taskJson->GetValue("project")->Value() + " - " + taskJson->GetValue("task_name")->Value() ;
  currNodeVal->CollapsedIcon->Graphic = PictureContainer1->Items->Items[0]->Picture ;
  return currNode;
}
//---------------------------------------------------------------------------
TAdvTreeViewNode* TfrmTimeEntryPredictions::AddEmailNodeFromJson(TJSONObject* emailJson, int iconIndex)
{
	 TAdvTreeViewNode* currNode;
	TAdvTreeViewNodeValue* currNodeVal;
	Picturecontainer::TPictureItem* currPicItem;
  currNode = atvTimeEntryPredictions->Nodes->Items[1]->Nodes->Add();
    currNode->DataObject = emailJson;
	  currNodeVal = currNode->Values->Add();
	  currNodeVal->CheckType = tvntCheckBox;
	  currNodeVal->Text = emailJson->GetValue("sender_address")->Value() + ": " + emailJson->GetValue("subject")->Value() ;
    currNodeVal->CollapsedIcon->Graphic = PictureContainer1->Items->Items[iconIndex]->Picture ;
	  return currNode;
}
//---------------------------------------------------------------------------
TAdvTreeViewNode* TfrmTimeEntryPredictions::AddPhoneCallNodeFromJson(TJSONObject* phoneCallJson)
{
	 TAdvTreeViewNode* currNode;
	TAdvTreeViewNodeValue* currNodeVal;
	Picturecontainer::TPictureItem* currPicItem;
  currNode = atvTimeEntryPredictions->Nodes->Items[2]->Nodes->Add();
    currNode->DataObject = phoneCallJson;
	  currNodeVal = currNode->Values->Add();
	  currNodeVal->CheckType = tvntCheckBox;
	  currNodeVal->Text = phoneCallJson->GetValue("src")->Value() + " -> " + phoneCallJson->GetValue("dst")->Value() + " : " + phoneCallJson->GetValue("duration")->Value();
    currNodeVal->CollapsedIcon->Graphic = PictureContainer1->Items->Items[3]->Picture ;
	  return currNode;
}
//---------------------------------------------------------------------------
TAdvTreeViewNode* TfrmTimeEntryPredictions::AddWindowNodeFromJson(TJSONObject* windowJson)
{
	 TAdvTreeViewNode* currNode;
	TAdvTreeViewNodeValue* currNodeVal;
	Picturecontainer::TPictureItem* currPicItem;
  currNode = atvTimeEntryPredictions->Nodes->Items[3]->Nodes->Add();
    currNode->DataObject = windowJson;
	  currNodeVal = currNode->Values->Add();
	  currNodeVal->CheckType = tvntCheckBox;
	  currNodeVal->Text = windowJson->GetValue("window_title")->Value() + ": " + windowJson->GetValue("count")->Value() ;
    currNodeVal->CollapsedIcon->Graphic = PictureContainer1->Items->Items[4]->Picture ;
	  return currNode;
}
//---------------------------------------------------------------------------
TAdvTreeViewNode* TfrmTimeEntryPredictions::AddTeamViewerConnectionNodeFromJson(TJSONObject* teamViewerConnectionJson)
{
	 TAdvTreeViewNode* currNode;
	TAdvTreeViewNodeValue* currNodeVal;
	Picturecontainer::TPictureItem* currPicItem;
  currNode = atvTimeEntryPredictions->Nodes->Items[4]->Nodes->Add();
    currNode->DataObject = teamViewerConnectionJson;
	  currNodeVal = currNode->Values->Add();
	  currNodeVal->CheckType = tvntCheckBox;
	  currNodeVal->Text = teamViewerConnectionJson->GetValue("groupname")->Value() + " : " + teamViewerConnectionJson->GetValue("devicename")->Value() + " - " + (teamViewerConnectionJson->GetValue("notes") != NULL ? teamViewerConnectionJson->GetValue("notes")->Value() : String(""));
    currNodeVal->CollapsedIcon->Graphic = PictureContainer1->Items->Items[5]->Picture ;
	  return currNode;
}
//---------------------------------------------------------------------------
TAdvTreeViewNode* TfrmTimeEntryPredictions::AddWebHistoryNodeFromJson(TJSONObject* webHistoryJson)
{
	 TAdvTreeViewNode* currNode;
	TAdvTreeViewNodeValue* currNodeVal;
	Picturecontainer::TPictureItem* currPicItem;
  currNode = atvTimeEntryPredictions->Nodes->Items[5]->Nodes->Add();
    currNode->DataObject = webHistoryJson;
	  currNodeVal = currNode->Values->Add();
	  currNodeVal->CheckType = tvntCheckBox;
	  currNodeVal->Text = webHistoryJson->GetValue("title")->Value() + " : " + webHistoryJson->GetValue("count")->Value() + " - " + webHistoryJson->GetValue("url")->Value() ;
    currNodeVal->CollapsedIcon->Graphic = PictureContainer1->Items->Items[6]->Picture ;
	  return currNode;
}
//---------------------------------------------------------------------------
void __fastcall TfrmTimeEntryPredictions::btnCreateEntryClick(TObject *Sender)
{
   TAdvTreeViewNode * node;
   TJSONObject *JSONOut = new TJSONObject();
   JSONOut->AddPair("request_id", "time_entry");
   for(int i = 0; i< atvTimeEntryPredictions->Nodes->Items[0]->Nodes->Count; i++){
     node = atvTimeEntryPredictions->Nodes->Items[0]->Nodes->Items[i];
     if(node->Values->Items[0]->Checked){
       JSONOut->AddPair("task", (TJSONObject*)node->DataObject);
     }
   }

   String jsonNames[] = {"emails","phone_calls","windows","tv_connections","web_history"};
   for(int j = 1; j<=5; j++){
     TJSONArray *timeEntryItemsJson = new TJSONArray();
     for(int i = 0; i< atvTimeEntryPredictions->Nodes->Items[j]->Nodes->Count; i++){
       node = atvTimeEntryPredictions->Nodes->Items[j]->Nodes->Items[i];
       if(node->Values->Items[0]->Checked){
         timeEntryItemsJson->AddElement((TJSONObject*)node->DataObject);
       }
     }
     JSONOut->AddPair( jsonNames[j-1], timeEntryItemsJson);
     //delete timeEntryItemsJson;
   }


   JSONOut->AddPair("summary_text",mmoSummary->Text + " ");
   JSONOut->AddPair("keystroke_text", mmoKeystrokes->SelText + " ");

   JSONOut->AddPair("user", 1011);
   JSONOut->AddPair("hours", hours);

   transferText = "";
   ipsHTTPS1->PostData = JSONOut->ToJSON();
   ipsHTTPS1->Post("https://www.em-sys.net:8080");
   delete JSONOut;
}
//---------------------------------------------------------------------------
void __fastcall TfrmTimeEntryPredictions::btnFinishedClick(TObject *Sender)
{
  this->ModalResult = mrCancel;
}
//---------------------------------------------------------------------------
void __fastcall TfrmTimeEntryPredictions::FormShow(TObject *Sender)
{
int i = this->Width;
}
//---------------------------------------------------------------------------

void __fastcall TfrmTimeEntryPredictions::ipwHTTP1EndTransfer(TObject *Sender, TipwHTTPEndTransferEventParams *e)

{
  if(e->Direction == 1){
    try{
      TJSONObject *JSONIn = (TJSONObject*)TJSONObject::ParseJSONValue(transferText);
      String success = JSONIn->GetValue("success")->Value() ;
      if(success == "true"){
         TAdvTreeViewNode * node;
         for(int i = 0; i< atvTimeEntryPredictions->Nodes->Items[0]->Nodes->Count; i++){
           node = atvTimeEntryPredictions->Nodes->Items[0]->Nodes->Items[i];
           if(node->Values->Items[0]->Checked){
              atvTimeEntryPredictions->RemoveNode(node);
           }
         }
         for(int j = 1; j<=5; j++){
           for(int i = 0; i< atvTimeEntryPredictions->Nodes->Items[j]->Nodes->Count; i++){
             node = atvTimeEntryPredictions->Nodes->Items[j]->Nodes->Items[i];
             if(node->Values->Items[0]->Checked){
               i--;
               atvTimeEntryPredictions->RemoveNode(node);
             }
           }
         }
         if( mmoKeystrokes->SelLength > 0 ){
            mmoKeystrokes->Text = mmoKeystrokes->Text.SubString(0, mmoKeystrokes->SelStart) + mmoKeystrokes->Text.SubString(mmoKeystrokes->SelStart + mmoKeystrokes->SelLength, mmoKeystrokes->Text.Length() - (mmoKeystrokes->SelStart + mmoKeystrokes->SelLength));
            mmoKeystrokes->ClearSelection();
         }
         mmoSummary->Text = "";
      }
    }
    catch(...){

    }
    transferText = "";
  }
}
//---------------------------------------------------------------------------

void __fastcall TfrmTimeEntryPredictions::ipwHTTP1Transfer(TObject *Sender, TipwHTTPTransferEventParams *e)

{
  if(e->Direction == 1){
    transferText += e->Text;
  }
}
//---------------------------------------------------------------------------



void __fastcall TfrmTimeEntryPredictions::ipsHTTPS1Transfer(TObject *Sender, TipsHTTPSTransferEventParams *e)

{
  if(e->Direction == 1){
    transferText += e->Text;
  }
}
//---------------------------------------------------------------------------

void __fastcall TfrmTimeEntryPredictions::ipsHTTPS1EndTransfer(TObject *Sender, TipsHTTPSEndTransferEventParams *e)

{
  if(e->Direction == 1){
    try{
      TJSONObject *JSONIn = (TJSONObject*)TJSONObject::ParseJSONValue(transferText);
      String success = JSONIn->GetValue("success")->Value() ;
      if(success == "true"){
         TAdvTreeViewNode * node;
         for(int i = 0; i< atvTimeEntryPredictions->Nodes->Items[0]->Nodes->Count; i++){
           node = atvTimeEntryPredictions->Nodes->Items[0]->Nodes->Items[i];
           if(node->Values->Items[0]->Checked){
              atvTimeEntryPredictions->RemoveNode(node);
           }
         }
         for(int j = 1; j<=5; j++){
           for(int i = 0; i< atvTimeEntryPredictions->Nodes->Items[j]->Nodes->Count; i++){
             node = atvTimeEntryPredictions->Nodes->Items[j]->Nodes->Items[i];
             if(node->Values->Items[0]->Checked){
               i--;
               atvTimeEntryPredictions->RemoveNode(node);
             }
           }
         }
         if( mmoKeystrokes->SelLength > 0 ){
            mmoKeystrokes->Text = mmoKeystrokes->Text.SubString(0, mmoKeystrokes->SelStart) + mmoKeystrokes->Text.SubString(mmoKeystrokes->SelStart + mmoKeystrokes->SelLength, mmoKeystrokes->Text.Length() - (mmoKeystrokes->SelStart + mmoKeystrokes->SelLength));
            mmoKeystrokes->ClearSelection();
         }
         mmoSummary->Text = "";
      }
    }
    catch(...){

    }
    transferText = "";
  }
}
//---------------------------------------------------------------------------

void __fastcall TfrmTimeEntryPredictions::cbOtherTaskDblClick(TObject *Sender)
{

    String taskText = cbOtherTask->Text;
    if(!taskText.IsEmpty()) {
      TJSONObject *taskJson = otherTasks[taskText];
      String taskJsonStr = taskJson->ToJSON();
      TJSONObject *taskJsonFixTreeDelete = (TJSONObject*)TJSONObject::ParseJSONValue(taskJsonStr);
      AddTaskNodeFromJson(taskJsonFixTreeDelete);
    }

}
//---------------------------------------------------------------------------

void __fastcall TfrmTimeEntryPredictions::btnAddTaskClick(TObject *Sender)
{
    cbOtherTaskDblClick(Sender);
}
//---------------------------------------------------------------------------

void __fastcall TfrmTimeEntryPredictions::mmoKeystrokesMouseUp(TObject *Sender, TMouseButton Button,
          TShiftState Shift, int X, int Y)
{
   if( mmoKeystrokes->SelLength > 0 ){
      mmoSummary->Text = mmoKeystrokes->SelText;
   }
}
//---------------------------------------------------------------------------

void __fastcall TfrmTimeEntryPredictions::btnRefreshClick(TObject *Sender)
{
  this->ModalResult = mrRetry;
}
//---------------------------------------------------------------------------