#
# plan-fishing.R,  5 Jul 26
#
# Data from:
# The SEA Data Warehouse
# https://softwareexcellencealliance.org/the-sea-data-warehouse/

library("colorspace")
library("plyr")


par(bty="l")
par(las=1)
par(pch="+")
pal_col=rainbow(2)


proj_summary=function(df)
{
# print(nrow(df))
proj=data.frame(num_plans=nrow(df),
		deleted=length(which(df$plan_item_deleted_flag == 1)),
		num_tasks=length(which(!is.na(df$task_key))),
		num_leaf_tasks=length(which((df$plan_item_leaf_task_flag == 1) & !is.na(df$task_key))),
		num_WBS=length(which(is.na(df$task_key))),
		num_leaf_WBS=length(which((df$pla_item_leaf_element_flag == 1) & is.na(df$task_key))),
		elapsed_days=max(df$time_log_end_date, na.rm=TRUE)-min(df$time_log_start_date, na.rm=TRUE)+1,
		minutes_used=sum(df$time_log_delta_minutes, na.rm=TRUE),
		num_devs=length(unique(df$person_key))
	)

return(proj)
}


# Merge tables containing required columns.
# Assumes that sql data has been exported as csv files.
#
# pli=read.csv("csv/plan_item.csv.xz")
# datab=read.csv("csv/data_block.csv.xz")
# timel=read.csv("csv/time_log_fact.csv.xz")
# 
# pli$parent_plan_item_key[pli$parent_plan_item_key == "NULL"]=NA
# pli$parent_plan_item_key=as.numeric(pli$parent_plan_item_key)
# pli$phase_key[pli$phase_key == "NULL"]=NA
# pli$task_key[pli$task_key == "NULL"]=NA
# 
# t1=merge(timel, pli, by="plan_item_key", all=TRUE)
# t2=merge(t1, datab, by="data_block_key", all=TRUE)
# 
# t2$time_log_comment_text_key=NULL
# t2$time_log_start_date_key=NULL
# t2$row_eff_start_date=NULL
# t2$row_last_updated_by_key.x=NULL
# t2$row_eff_end_date=NULL
# t2$plan_item_identifier=NULL
# t2$row_created_by_key.y=NULL
# t2$row_last_updated_by_key.y=NULL
# t2$row_last_updated_by_key=NULL
#
# write.csv(t2, file="plan_time_block.cv", row.names=FALSE)


# read the merged table
ptb=read.csv("plan_time_block.csv.xz")

ptb$time_log_start_date=as.Date(ptb$time_log_start_date, format="%Y-%m-%d")
ptb$time_log_end_date=as.Date(ptb$time_log_end_date, format="%Y-%m-%d")

length(unique(ptb$project_key))

proj_info=ddply(ptb, .(project_key), proj_summary)

proj_sort=proj_info[order(proj_info$num_plans), ]

pal_col=rainbow(3)

plot(proj_sort$num_plans, log="y",
	xaxs="i", yaxs="i",
	xlim=c(0, 1225),
	xlab="Project index", ylab="Occurrences")
points(proj_sort$num_tasks, col=pal_col[3])
points(proj_sort$deleted, col=pal_col[1])
points(proj_sort$num_WBS, col=pal_col[2])
# points(proj_sort$num_tasks/proj_sort$num_WBS, col="pink")

legend("topleft", legend=c("Plans", "Tasks", "WBS", "Deleted plans"), cex=1.2,
                        fill=c("black", pal_col[c(3, 2, 1)]), border="white", bty="n")

dev.copy(dev=png, file="SEA-plans-tasks-WBS.png")
dev.off()


proj_effort=proj_info[order(proj_info$minutes_used), ]

plot(proj_effort$num_devs, log="y", col=pal_col[3],
	xaxs="i", yaxs="i",
	xlim=c(0, 1225), ylim=c(1, 1e4),
	xlab="Project index", ylab="Occurrences")
points(proj_effort$minutes_used/60, col=pal_col[1])
points(proj_effort$elapsed_days, col=pal_col[2])

legend("topleft", legend=c("Total person hours", "Elapsed days", "People"), cex=1.2,
                        fill=pal_col, border="white", bty="n")

dev.copy(dev=png, file="SEA-days_hrs_devs.png")
dev.off()


# Check various QQ plots for a straight line

proj10=subset(proj_sort, num_plans >= 10)

sdist=sort(proj10$num_plans)
n=nrow(proj10)

p_i=((1:n)-0.5)/n
# exponential
q_i=-mean(sdist)*log(1-p_i)
# power law
q_i=-log((n+1-(1:n))/(n+1))
# Weibul
q_i=log(-log(1-p_i))

plot(q_i, proj10$num_plans, log="xy",
	xaxs="i", yaxs="i",
	xlim=c(10, 5e3),
	xlab="Project index", ylab="Occurrences")
lines(c(10, 10000), c(10, 10000), col="grey")
points(q_i, proj10$num_tasks, col="blue")
points(q_i, proj10$deleted, col="red")
points(q_i, proj10$num_WBS, col="green")
# points(proj_sort$num_tasks/proj_sort$num_WBS, col="pink")

exp_mod=glm(log(proj10$num_plans) ~ log(q_i))
summary(exp_mod)


plot(count(ch_pa$levels_from_parent),
	xlab="Levels difference", ylab="Occurrences")



# Build a call graph

library("igraph")

not_self=subset(pli, !is.na(parent_plan_item_key))

# Connections between the nodes in a directed graph
gr_df=data.frame(from=not_self$parent_plan_item_key,
			to=not_self$plan_item_key)
call_gr=graph_from_data_frame(gr_df)

root_node = V(call_gr)[degree(call_gr, mode = "in") == 0]

# Returns the number of nodes reachable from each in-degree-0 "root",
# one element per root.
root_subgraph_sizes=function(g)
{
# All functions that are not called
roots=V(g)[degree(g, mode = "in") == 0]
if (length(roots) == 0L)
   return(setNames(integer(0L), character(0L)))

# How many nodes in each disjoint graph?
sizes=vapply(roots, function(v)
			{
			r=subcomponent(g, v, mode = "out")  # all nodes reachable from v
			return(length(r))
			},
		integer(1L))

names(sizes)=as_ids(roots)  # vertex names if present, else ids
return(sizes)
}

root_gr_cnt=root_subgraph_sizes(call_gr)

plot(sort(root_gr_cnt), log="xy",
	xlab="root index", ylab="Plan Items")

