Esempio n. 1
0
        public static void Main(string[] args) {
            
            TaiConfig config = new TaiConfig(@Grapple.GetThisFolder() + "tai.conf");

            //muahahha anonymouse functions in c# !!!
	        var options = new OptionSet() {
                { "iteration-report", "", _ => {SESSION_ACTION = TAI_COMMAND.REPORT;}},
                { "auto-fill-time", "", _ => {SESSION_ACTION = TAI_COMMAND.BURNDOWN;}},
                { "set-story-build", "", _ => {SESSION_ACTION = TAI_COMMAND.SETBUILDID;}},
                { "create-task", "", _ => {SESSION_ACTION = TAI_COMMAND.CREATETASK;}},
                { "?|h|help", "", _ => Echo.HelpText()},
                                
                {"api-url=", "", url => {config["apiUrl"] = url;}}, 
                {"project-id=", "", proj => {config["projectId"] = proj;}},                
                {"target-user="******"", user => {config["targetUser"] = user;}},
                {"story-id=", "", storyId => {config["storyId"] = storyId;}},
                {"build-id=", "", buildId => {config["buildId"] = buildId;}},
                {"burndown-date=", "", date => {config["burndownDate"] = date;}},
                {"email-greeting=", "", hi => {config["emailGreeting"] = hi;}},
                {"email-signature=", "", me => {config["emailSignature"] = me;}},
                {"iteration-number=", "", num => {config["iterationNumber"] = num;}},
                {"status-report-names=", "", names => {config["statusReportNames"] = names;}}, //test these arrays, i will need to serialize them right
                {"task-name=|task-names=", "", names => {config["taskNames"] = names;}}, //test these arrays, i will need to serialize them right

                {"hours-per-day=", "", hours => {config["hoursPerDay"] = hours;}},
                {"v=|verbosity=", "", noise => {Echo.LOG_LEVEL = Convert.ToByte(noise);}},
                {"no-interaction", "", _ => {Grapple.isAllowingHumanInteraction = false;}},

                /* i don't like the idea of passing credentials, however added it for completion */
                {"username="******"", user => {config["username"] = user;}},
                {"password="******"", pass => {config["password"] = pass;}}
			};

            var badInput = options.Parse(args);

            if(badInput.Count > 0) {
                Echo.ErrorReport(badInput.ToArray());
                Echo.OffensiveGesture();
                Echo.HelpText();
            }

            Echo.WelcomeText();

            config = Grapple.TryGetCredentialsManually(config);
            ApiWrapper.Initialize(config);

            TaiTakeCareOfThis[SESSION_ACTION](config);

            Echo.Out("done", 2);
        }
Esempio n. 2
0
        public static void Main(string[] args)
        {
            TaiConfig config = new TaiConfig(@Grapple.GetThisFolder() + "tai.conf");

            var options = new OptionSet() {
            { "get-iteration-report", "", _ => {SESSION_ACTION = TAI_COMMAND.GET_ITERATION_REPORT;}},
            { "update-rally-time", "", _ => {SESSION_ACTION = TAI_COMMAND.BURNDOWN;}},
            { "update-story-build", "", _ => {SESSION_ACTION = TAI_COMMAND.UPDATE_STORY_BUILDID;}},
            { "insert-task", "", _ => {SESSION_ACTION = TAI_COMMAND.INSERT_TASK;}},
            { "insert-my-tasks-for-story", "", _ => {SESSION_ACTION = TAI_COMMAND.INSERT_MY_STORY_TASKS;}},
            { "insert-dev-lead-boilerplate", "", _ => {SESSION_ACTION = TAI_COMMAND.INSERT_DEV_LEAD_BOILERPLATE;}},
            { "insert-qa-boilerplate", "", _ => {SESSION_ACTION = TAI_COMMAND.INSERT_QA_BOILERPLATE;}},
            { "get-iteration", "", _ => {SESSION_ACTION = TAI_COMMAND.GET_ITERATION;}},
            { "get-my-storys", "", _ => {SESSION_ACTION = TAI_COMMAND.GET_MY_STORYS;}},
            { "get-my-tasks", "", _ => {SESSION_ACTION = TAI_COMMAND.GET_MY_TASKS;}},
            { "get-team-storys", "", _ => {SESSION_ACTION = TAI_COMMAND.GET_TEAM_STORYS;}},
            { "get-team-story-urls", "", _ => {SESSION_ACTION = TAI_COMMAND.GET_TEAM_STORY_URLS;}},

            { "?|h|help", "", _ => {SESSION_ACTION = TAI_COMMAND.NONE;}},
            {"no-interaction", "", _ => {Grapple.isAllowingHumanInteraction = false;}},

            {"username="******"", user => {config["username"] = user;}},
            {"password="******"", pass => {config["password"] = pass;}},
            {"api-url=", "", url => {config["apiUrl"] = url;}},
            {"project-id=", "", proj => {config["projectId"] = proj;}},
            {"target-user="******"", user => {config["targetUser"] = user;}},
            {"story-id=", "", storyId => {config["storyId"] = storyId;}},
            {"build-id=", "", buildId => {config["buildId"] = buildId;}},
            {"burndown-date=", "", date => {config["burndownDate"] = date;}},
            {"email-greeting=", "", hi => {config["emailGreeting"] = hi;}},
            {"email-signature=", "", me => {config["emailSignature"] = me;}},
            {"iteration-number=", "", num => {config["iterationNumber"] = num;}},
            {"hours-per-day=", "", hours => {config["hoursPerDay"] = hours;}},
            {"description=", "", desc => {config["description"] = desc;}},
            {"note=|notes=", "", note => {config["notes"] = note;}},
            {"block=|blocked=", "", isblock => {config["isBlocked"] = isblock;}},
            {"story-size=", "", size => {config["storySize"] = size;}},
            {"estimate-hours=", "", hours => {config["estimateHours"] = hours;}},
            {"task-state=", "", state => {config["taskState"] = state;}}, /* "Defined", "In-Progress", "Completed" */
            {"task-name=", "", name => {config["taskName"] = name;}},
            {"attachment-type=", "", type => {config["attatchmentType"] = type;}}, //take in the mime-type. type == "base64" indicates that tai does not need to encode it

            {"status-report-names=", "", names => {config["statusReportNames"] = names.Split(',').ToJson();}},
            {"task-names=", "", names => {config["taskNames"] = names.Split(',').ToJson();}},
            {"attachment=|attachments=", "", files => {config["attachments"] = files.Split(',').ToJson();}}, //can be the path or raw file, but all types must be the same for this up and comming version

            {"d=|delimiter=", "", sep => {Echo.DELIMITER = sep;}},
            {"l=|log-level=", "", noise => {Echo.LOG_LEVEL = Convert.ToByte(noise);}},

            /*
            later...
            get-tasks

            team-name=

            output-type= json csv none(same as log-level 0) cli(default)
            */
            };

            var badInput = options.Parse(args);

            Echo.WelcomeText();

            if(badInput.Count > 0 || SESSION_ACTION == TAI_COMMAND.NONE) {
                Echo.ErrorReport(badInput.ToArray());
                Echo.OffensiveGesture();
                Echo.HelpText(options);
            }

            config = Grapple.TryGetCredentialsManually(config);
            ApiWrapper.Initialize(config);

            TaiTakeCareOfThis[SESSION_ACTION](config);

            Echo.Out("done", 5);
        }
Esempio n. 3
0
        private static StringBuilder GenerateMicrosoftHtmlTabularReport(List<JToken> storys, TaiConfig config)
        {
            config = SetRequiredProperties(config, "emailGreeting", "emailSignature");

            var emailBody = new StringBuilder();

            var headerRow = string.Format("<tr><td width=125 valign=top style='width:93.5pt;border:solid windowtext 1.0pt;padding:0in 5.4pt 0in 5.4pt'><p class=MsoNormal style='mso-element:frame;mso-element-frame-hspace:9.0pt;mso-element-wrap:around;mso-element-anchor-vertical:paragraph;mso-element-anchor-horizontal:margin;mso-element-top:-.65pt;mso-height-rule:exactly'>{0}<o:p></o:p></p></td><td width=125 valign=top style='width:93.5pt;border:solid windowtext 1.0pt;border-left:none;padding:0in 5.4pt 0in 5.4pt'><p class=MsoNormal style='mso-element:frame;mso-element-frame-hspace:9.0pt;mso-element-wrap:around;mso-element-anchor-vertical:paragraph;mso-element-anchor-horizontal:margin;mso-element-top:-.65pt;mso-height-rule:exactly'>{1}<o:p></o:p></p></td><td width=125 valign=top style='width:93.5pt;border:solid windowtext 1.0pt;border-left:none;padding:0in 5.4pt 0in 5.4pt'><p class=MsoNormal style='mso-element:frame;mso-element-frame-hspace:9.0pt;mso-element-wrap:around;mso-element-anchor-vertical:paragraph;mso-element-anchor-horizontal:margin;mso-element-top:-.65pt;mso-height-rule:exactly'>{2}<o:p></o:p></p></td><td width=125 valign=top style='width:93.5pt;border:solid windowtext 1.0pt;border-left:none;padding:0in 5.4pt 0in 5.4pt'><p class=MsoNormal style='mso-element:frame;mso-element-frame-hspace:9.0pt;mso-element-wrap:around;mso-element-anchor-vertical:paragraph;mso-element-anchor-horizontal:margin;mso-element-top:-.65pt;mso-height-rule:exactly'>{3}<o:p></o:p></p></td><td width=125 valign=top style='width:93.5pt;border:solid windowtext 1.0pt;border-left:none;padding:0in 5.4pt 0in 5.4pt'><p class=MsoNormal style='mso-element:frame;mso-element-frame-hspace:9.0pt;mso-element-wrap:around;mso-element-anchor-vertical:paragraph;mso-element-anchor-horizontal:margin;mso-element-top:-.65pt;mso-height-rule:exactly'>{4}<o:p></o:p></p></td></tr>",
                "Person",
                "Story",
                "Finished On",
                "Status",
                "Risks"
                );

            emailBody.Append(string.Format("<p>{0},</p><br>Here is the latest status report.<br><br>", config["emailGreeting"]));
            emailBody.Append("<table class=MsoTableGrid border=1 cellspacing=0 cellpadding=0 align=left style='border-collapse:collapse;border:none;margin-left:6.75pt;margin-right:6.75pt'>");
            emailBody.Append(headerRow);

            foreach (JToken story in storys) {

                var hero = story.Value<string>("HeroOfTime");
                var name = story.Value<string>("Name");
                var risk = story.Value<string>("BlockedReason");
                var status = story.Value<string>("ScheduleState");
                var date = story.Value<string>("AcceptedDate").GetPrettyDate();

                var dataRow = string.Format("<tr style='height:14.35pt'><td width=125 valign=top style='width:93.5pt;border:solid windowtext 1.0pt;border-top:none;padding:0in 5.4pt 0in 5.4pt;height:14.35pt'><p class=MsoNormal style='mso-element:frame;mso-element-frame-hspace:9.0pt;mso-element-wrap:around;mso-element-anchor-vertical:paragraph;mso-element-anchor-horizontal:margin;mso-element-top:-.65pt;mso-height-rule:exactly'>{0}<o:p></o:p></p></td><td width=125 valign=top style='width:93.5pt;border-top:none;border-left:none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;padding:0in 5.4pt 0in 5.4pt;height:14.35pt'><p class=MsoNormal style='mso-element:frame;mso-element-frame-hspace:9.0pt;mso-element-wrap:around;mso-element-anchor-vertical:paragraph;mso-element-anchor-horizontal:margin;mso-element-top:-.65pt;mso-height-rule:exactly'>{1}<o:p></o:p></p></td><td width=125 valign=top style='width:93.5pt;border-top:none;border-left:none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;padding:0in 5.4pt 0in 5.4pt;height:14.35pt'><p class=MsoNormal style='mso-element:frame;mso-element-frame-hspace:9.0pt;mso-element-wrap:around;mso-element-anchor-vertical:paragraph;mso-element-anchor-horizontal:margin;mso-element-top:-.65pt;mso-height-rule:exactly'>{2}<o:p></o:p></p></td><td width=125 valign=top style='width:93.5pt;border-top:none;border-left:none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;padding:0in 5.4pt 0in 5.4pt;height:14.35pt'><p class=MsoNormal style='mso-element:frame;mso-element-frame-hspace:9.0pt;mso-element-wrap:around;mso-element-anchor-vertical:paragraph;mso-element-anchor-horizontal:margin;mso-element-top:-.65pt;mso-height-rule:exactly'>{3}<o:p></o:p></p></td><td width=125 valign=top style='width:93.5pt;border-top:none;border-left:none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;padding:0in 5.4pt 0in 5.4pt;height:14.35pt'><p class=MsoNormal style='mso-element:frame;mso-element-frame-hspace:9.0pt;mso-element-wrap:around;mso-element-anchor-vertical:paragraph;mso-element-anchor-horizontal:margin;mso-element-top:-.65pt;mso-height-rule:exactly'>{4}<o:p></o:p></p></td></tr>",
                    hero,
                    name,
                    date,
                    status,
                    risk
                    );

                emailBody.Append(dataRow);
            }
            emailBody.Append("</table>");

            var signature = string.Format("<br><br><p>{0},</p><br><p>-{1}</p>", GetFarewell(), config["emailSignature"]);

            emailBody.Append(signature);

            return emailBody;
        }
Esempio n. 4
0
        private static TaiConfig SetRequiredProperties(TaiConfig conf, params string[] requiredProperties)
        {
            //BEWARE: default properties are processed in the order they are given and may have dependecies on another property!
            var defaults = new Dictionary<string, Func<string, string>> () {
                {"targetUser",			val => {return val ?? conf["username"];}},
                {"storyId",				val => {return val ?? "US00000";}}, //todo: get most recent story by latest task update/modified
                {"taskName",			val => {return val ?? "new task";}},
                {"storySize",			val => {return val ?? StorySize.digitToLetter[ApiWrapper.GetStorySize(conf["storyId"])];}},
                {"estimateHours",		val => {return val ?? StorySize.preferredEstimates[conf["storySize"]].ToString();}},
                {"taskState",			val => {return val ?? "Defined";}},
                {"projectId",			val => {return val ?? ApiWrapper.GetProjectId(conf["targetUser"]);}},
                {"iterationNumber",		val => {return val ?? ApiWrapper.GetIterationNumber(conf["projectId"]);}},
                {"burndownDate",		val => {return val ?? DateTime.Today.ToString("yyyy-MM-dd");}},
                {"hoursPerDay",			val => {return val ?? "8";}},
                {"taskNames",			val => {return val ?? "Administration,Regression,Iteration Planning,Deployment Planning,Environment Issue,User Stories".Split(',').ToJson();}},
                {"emailGreeting",		val => {return val ?? "Hi Boss";}},
                {"emailSignature",		val => {return val ?? "dev team";}},
                {"humanName",			val => {return val ?? ApiWrapper.GetTargetUserHumanName(conf["targetUser"]);}},
                {"statusReportNames",	val => {

                    if(val != null) {
                        return val;}

                    var teams = ApiWrapper.GetTeamMembers(conf["projectId"]);

                    List<string> statusReportNames = new List<string>();
                    foreach(JToken member in teams){
                        statusReportNames.Add(member.Value<string>("DisplayName"));}

                    return statusReportNames.ToArray().ToJson();
                }}
            };

            foreach(string property in requiredProperties) {
                conf[property] = defaults[property](conf[property]);}

            return conf;
        }
Esempio n. 5
0
        private static void CreateEmptyTimeCardForTasks(List<Task> tasks, TaiConfig config)
        {
            var target_date = DateTime.Parse(config["burndownDate"]);
            var week_begin_date = target_date.AddDays(-(int)target_date.DayOfWeek);
            var my_id = ApiWrapper.GetTargetUserObjectId(config["targetUser"]);

            foreach(Task task in tasks) {

                if(!task.weeklyTime.ContainsKey(week_begin_date.Date)) {
                    /* this can and will create an invisible time item for tasks that don't belong to this week. no damage will be done */
                    task.weeklyTime.Add(
                        week_begin_date.Date,
                        ApiWrapper.CreateNewTimeEntryItem(config["projectId"], my_id, task.taskObjectId, week_begin_date)
                    );
                }
            }
        }
Esempio n. 6
0
        private static StringBuilder GenerateGenerictHtmlTabularReport(List<JToken> storys, TaiConfig config)
        {
            config = SetRequiredProperties(config, "emailGreeting", "emailSignature");

            var emailBody = new StringBuilder();

            var headerRow = string.Format("<tr><td><p>{0}</p></td> <td><p>{1}</p></td> <td><p>{2}</p></td> <td><p>{3}</p></td> <td><p>{4}</p></td></tr>",
                "Person",
                "Story",
                "Finished On",
                "Status",
                "Risks"
                );

            emailBody.Append(string.Format("<p>{0},</p><br>Here is the latest status report.<br><br>", config["emailGreeting"]));
            emailBody.Append("<table>");
            emailBody.Append(headerRow);

            foreach (JToken story in storys) {

                var hero = story.Value<string>("HeroOfTime");
                var name = story.Value<string>("Name");
                var risk = story.Value<string>("BlockedReason");
                var status = story.Value<string>("ScheduleState");
                var date = story.Value<string>("AcceptedDate").GetPrettyDate();

                var dataRow = string.Format("<tr><td><p>{0}</p></td> <td><p>{1}</p></td> <td><p>{2}</p></td> <td><p>{3}</p></td> <td><p>{4}</p></td></tr>",
                    hero,
                    name,
                    date,
                    status,
                    risk
                    );

                emailBody.Append(dataRow);
            }
            emailBody.Append("</table>");

            var signature = string.Format("<br><br><p>{0},</p><br><p>-{1}</p>", GetFarewell(), config["emailSignature"]);

            emailBody.Append(signature);

            return emailBody;
        }
Esempio n. 7
0
        internal static void AutomaticallyFillTaskTime(TaiConfig config)
        {
            config = SetRequiredProperties(config, "targetUser", "projectId", "burndownDate", "hoursPerDay", "taskNames");

            var target_date = DateTime.Parse(config["burndownDate"]);
            var week_begin_date = target_date.AddDays(-(int)target_date.DayOfWeek);

            var my_id = ApiWrapper.GetTargetUserObjectId(config["targetUser"]);
            var tasks = ApiWrapper.GetTasks(my_id, week_begin_date);

            CreateEmptyTimeCardForTasks(tasks, config);

            int daily_min = Convert.ToInt32(config["hoursPerDay"]);
            int[] total_hours = SumTaskHoursByWeekStart(tasks, week_begin_date);
            var priority_chart = BuildTaskPriorityChart(tasks, config);
            int required_hours_this_week = (int)target_date.DayOfWeek * daily_min;

            Echo.Out(string.Format("burn down hours from {0} to {1} as role: {2}", week_begin_date.ToString("MMMM dd yyyy"), target_date.ToString("MMMM dd yyyy"), "dev"), 2);
            Echo.Out(my_id + " is my ObjectID", 5);
            Echo.Out(tasks.Count + " tasks since "+week_begin_date.ToString("yyyy MM dd"), 5);
            Echo.Out(". . . . . . . . . . .", 5);
            Echo.TaskReport(tasks, week_begin_date);
            Echo.Out("min hours needed to pass inspection: " + required_hours_this_week, 2);
            Echo.Out("You have " + total_hours.Sum() + " hours", 2);

            if (total_hours.Sum() < required_hours_this_week) {

                var workload = new List<JObject>();

                for(int i = 0; i<total_hours.Length; ++i) {

                    int this_days_time      = total_hours[i];
                    string day_name         = Enum.GetName(typeof(DayOfWeek), i);
                    int daily_time_needed   = daily_min - this_days_time; //this allows for overages since we are adding in increments of 8. should only allow for a maximum of (inspection_amount + increment-1)

                    if(total_hours.Sum() < required_hours_this_week && daily_time_needed > 0 /**/ && i!=0 && i !=6) { //hard code ignore of sunday and saturaday for now

                        Echo.Out(day_name + " FAILED inspection.", 2);

                        int hoursToAdd = (int)Math.Ceiling(((double)daily_time_needed / (double)priority_chart.Count));

                        foreach(KeyValuePair<string, Task> priority in priority_chart) {

                            var task = priority.Value;

                            DateTime post_date = week_begin_date.AddDays(i);

                            JObject newTimeEntryValuePost = new JObject();

                            if(task.weeklyTime[week_begin_date].dailyTime.ContainsKey(post_date)) {

                                newTimeEntryValuePost["Verb"] = "update";
                                newTimeEntryValuePost["ObjectID"] = task.weeklyTime[week_begin_date].dailyTime[post_date].Value<string>("ObjectID");
                                newTimeEntryValuePost["Hours"] = task.weeklyTime[week_begin_date].dailyTime[post_date].Value<int>("Hours") + hoursToAdd;
                            }else{
                                newTimeEntryValuePost["Verb"] = "insert";
                                newTimeEntryValuePost["DateVal"] = post_date.ToString("yyyy-MM-ddTHH:mm:ssZ");
                                newTimeEntryValuePost["TimeEntryItem"] = task.weeklyTime[week_begin_date].timeEntryObjectId;
                                newTimeEntryValuePost["Hours"] = hoursToAdd;
                            }

                            Echo.Out(newTimeEntryValuePost.ToString(), 6);
                            Echo.Out("..........", 6);

                            workload.Add(newTimeEntryValuePost);
                            total_hours[i] += hoursToAdd;
                            daily_time_needed -= hoursToAdd;
                            if(daily_time_needed <= 0){break;}
                        }

                    }else{
                        Echo.Out(day_name + " PASSED inspection", 2);
                    }
                }

                ApiWrapper.SubmitTaskTimeValue(workload);

            }else {
                Echo.Out("Autofilling is not needed since you have already filled out the minimum necessary time", 2);
            }
        }

        internal static void CreateDevLeadBoilerplate(TaiConfig config)
        {
            config = SetRequiredProperties(config, "targetUser", "projectId", "iterationNumber", "taskState");

            List<JToken> iterations = ApiWrapper.GetIteration(config["projectId"]);

            List<JToken> storys = ApiWrapper.GetUserStories(config["projectId"], iterations);

            string[] devTaskNames = new string[] {
                 (new Random(RandomSeed.seed).Next(0, 2) == 0 ? "Code Review" : "Harsh Code Review"),
            };

            foreach(JToken story in storys) {
                foreach(string logicalName in devTaskNames) {
                    JObject newTask = new JObject();
                    newTask["Name"] = logicalName;
                    newTask["Description"] = config["description"] ?? "";
                    newTask["Notes"] = config["notes"] ?? "";
                    newTask["Owner"] = ApiWrapper.GetTargetUserObjectId(config["targetUser"]);
                    newTask["Estimate"] = 1;
                    newTask["State"] = config["taskState"];
                    newTask["TaskIndex"] = 1;
                    newTask["WorkProduct"] = story.Value<string>("ObjectID");

                    JToken createResult = ApiWrapper.CreateNewTask(newTask);

                    Echo.Out(newTask.ToString(Newtonsoft.Json.Formatting.Indented), 3);
                    Echo.Out(createResult.ToString(Newtonsoft.Json.Formatting.None), 9);
                    Echo.Out((string)createResult["CreateResult"]["Object"]["ObjectID"], 1);
                }
            }
        }

        internal static void CreateMyStoryTasks(TaiConfig config)
        {
            config = SetRequiredProperties(config, "targetUser", "storyId", "storySize", "estimateHours", "taskState");

            JToken story = ApiWrapper.GetUserStory(config["storyId"]);

            string[] taskNames = GetLogicalTaskNamesForStory(story.Value<string>("Description"));

            var splitEstimate =  Math.Floor(Convert.ToDecimal(Convert.ToInt32(config["estimateHours"]) / taskNames.Length));

            var targetUserObjectId = ApiWrapper.GetTargetUserObjectId(config["targetUser"]);

            foreach(string logicalName in taskNames) {
                JObject newTask = new JObject();
                newTask["Name"] = logicalName;
                newTask["Description"] = config["description"] ?? "";
                newTask["Notes"] = config["notes"] ?? "";
                newTask["Owner"] = targetUserObjectId;
                newTask["Estimate"] = splitEstimate;
                newTask["State"] = config["taskState"];
                newTask["TaskIndex"] = 1;
                newTask["WorkProduct"] = story.Value<string>("ObjectID");

                JToken createResult = ApiWrapper.CreateNewTask(newTask);

                Echo.Out(newTask.ToString(Newtonsoft.Json.Formatting.Indented), 3);
                Echo.Out(createResult.ToString(Newtonsoft.Json.Formatting.None), 9);
                Echo.Out((string)createResult["CreateResult"]["Object"]["ObjectID"], 1);
            }
        }

        internal static void CreateQABoilerplate(TaiConfig config)
        {
            config = SetRequiredProperties(config, "targetUser", "projectId", "iterationNumber", "taskState");

            List<JToken> iterations = ApiWrapper.GetIteration(config["projectId"]);

            List<JToken> storys = ApiWrapper.GetUserStories(config["projectId"], iterations);

            string[] qaTaskNames = new string[] {
                "QA User Story Analysis",
                "QA Test Case creation",
                "QA Test Case Execution",
            };

            var targetUserObjectId = ApiWrapper.GetTargetUserObjectId(config["targetUser"]);

            foreach(JToken story in storys) {
                foreach(string logicalName in qaTaskNames) {
                    JObject newTask = new JObject();
                    newTask["Name"] = logicalName;
                    newTask["Description"] = config["description"] ?? "";
                    newTask["Notes"] = config["notes"] ?? "";
                    newTask["Owner"] = targetUserObjectId;
                    newTask["Estimate"] = 1; //hardcode 1 on purpose
                    newTask["State"] = config["taskState"];
                    newTask["TaskIndex"] = 1;
                    newTask["WorkProduct"] = story.Value<string>("ObjectID");

                    JToken createResult = ApiWrapper.CreateNewTask(newTask);

                    Echo.Out(newTask.ToString(Newtonsoft.Json.Formatting.Indented), 3);
                    Echo.Out(createResult.ToString(Newtonsoft.Json.Formatting.None), 9);
                    Echo.Out((string)createResult["CreateResult"]["Object"]["ObjectID"], 1);
                }
            }
        }

        internal static void CreateTaskForStory(TaiConfig config)
        {
            config = SetRequiredProperties(config, "targetUser", "storyId", "taskName", "estimateHours", "taskState");

            JObject newTask = new JObject();
            newTask["Name"] = config["taskName"];//just create one for now. later iterate over these and create one for each name
            newTask["Description"] = config["description"] ?? "";
            newTask["Notes"] = config["notes"] ?? "";
            newTask["Owner"] = ApiWrapper.GetTargetUserObjectId(config["targetUser"]);
            newTask["Estimate"] = config["estimateHours"];
            newTask["State"] = config["taskState"];
            newTask["TaskIndex"] = 1;
            newTask["WorkProduct"] = ApiWrapper.GetUserStory(config["storyId"]).Value<string>("ObjectID");

            JToken createResult = ApiWrapper.CreateNewTask(newTask);

            Echo.Out(newTask.ToString(Newtonsoft.Json.Formatting.Indented), 3);
            Echo.Out(createResult.ToString(Newtonsoft.Json.Formatting.None), 9);
            Echo.Out((string)createResult["CreateResult"]["Object"]["ObjectID"], 1);
        }

        internal static void GetCurrentIterationNumber(TaiConfig config)
        {
            config = SetRequiredProperties(config, "targetUser");

            var project_id = ApiWrapper.GetProjectId(config["targetUser"]);

            Echo.Out(ApiWrapper.GetIterationNumber(project_id), 1);
        }

        internal static void GetStorysForUser(TaiConfig config)
        {
            config = SetRequiredProperties(config, "targetUser", "projectId", "humanName");

            var humanName	= config["humanName"];
            var iterations  = ApiWrapper.GetIteration(config["projectId"]);
            var storys      = ApiWrapper.GetUserStories(config["projectId"], iterations);
            var tasks       = ApiWrapper.GetTasks(storys);
            storys          = AssignTaskMastersToStorys(storys, tasks, new string[]{humanName});
            var sorted      = GetStorysSortedByProgrammer(storys);

            if(sorted.ContainsKey(humanName)){

                var sb = new StringBuilder();

                sb.AppendFormat("{0} has {1} stories assigned. \n",
                    humanName,
                    sorted[humanName].Count
                );

                foreach(JToken story in sorted[humanName]) {

                    var cleanedCriteria = story.Value<string>("c_AcceptanceCriteria");
                    cleanedCriteria = Regex.Replace(cleanedCriteria, @"(<br />)", "\n");
                    cleanedCriteria = Regex.Replace(cleanedCriteria, @"(<div>)|(</\w+>)","");
                    cleanedCriteria = Regex.Replace(cleanedCriteria, @"(<ul><li>)|(<ol><li>)|(<li>)", "\n-");

                    sb.AppendFormat(@"
            _________________________________________________________________________
            |{0}:        {1}
            |
            |justification:  {2}
            |
            |criteria:       {3}
            |
            |expert(s):      {4}
            |
            |link:           {5}
            __________________________________________________________________________

            ",
                    story.Value<string>("FormattedID"),
                    story.Value<string>("Name"),
                    story.Value<string>("c_Benefit"),
                    cleanedCriteria,
                    story.Value<string>("c_ResponsibleParty"),
                    story.Value<string>("_ref"));
                }

                Echo.Out(sb.ToString(), 1);
            }
        }

        internal static void GetTeamStoryIds(TaiConfig config)
        {
            config = SetRequiredProperties(config, "targetUser", "projectId", "iterationNumber");

            var iterations  = ApiWrapper.GetIteration(config["projectId"], config["iterationNumber"]);
            var storys      = ApiWrapper.GetUserStories(config["projectId"], iterations);

            var sb = new StringBuilder();
            foreach(JToken story in storys) {
                sb.AppendFormat("{0}{1}", story.Value<string>("FormattedID"), Echo.DELIMITER);}

            Echo.Out(sb.ToString().Substring(0, sb.Length-Echo.DELIMITER.Length), 1);
        }

        internal static void GetTeamStoryUrls(TaiConfig config)
        {
            config = SetRequiredProperties(config, "targetUser", "projectId", "iterationNumber");

            var iterations  = ApiWrapper.GetIteration(config["projectId"], config["iterationNumber"]);
            var storys      = ApiWrapper.GetUserStories(config["projectId"], iterations);

            var sb = new StringBuilder();
            foreach(JToken story in storys) {
                sb.AppendFormat("{0}{1}", story.Value<string>("_ref"), Echo.DELIMITER);}

            Echo.Out(sb.ToString().Substring(0, sb.Length-Echo.DELIMITER.Length), 1);
        }

        internal static void SetStoryBuildId(TaiConfig config)
        {
            if(config.ContainsKey("storyId") && config.ContainsKey("buildId")) {

                Echo.Out("User story ID: " + config["storyId"], 5);
                Echo.Out("Build ID: " + config["buildId"], 5);
                Echo.Out("..........", 5);

                string UserStoryURL = ApiWrapper.GetUserStoryReferenceUrl(config["storyId"]);

                JObject postJson = new JObject();
                postJson["c_BuildID"] = config["buildId"];

                ApiWrapper.UpdateStoryBuildId(postJson, UserStoryURL);
                Echo.Out("success", 1);
            }else{
                Echo.Out("you must have both a storyId and buildId in order to perform action: SetStoryBuildId", 1);
            }
        }

        internal static void WriteStatusReportForAnIteration(TaiConfig config)
        {
            config = SetRequiredProperties(config, "targetUser", "projectId", "iterationNumber", "statusReportNames");

            var team        = ApiWrapper.GetTeamMembers(config["projectId"]);
            var iterations  = ApiWrapper.GetIteration(config["projectId"], config["iterationNumber"]);
            var storys      = ApiWrapper.GetUserStories(config["projectId"], iterations);
            var tasks       = ApiWrapper.GetTasks(storys);
            storys          = AssignTaskMastersToStorys(storys, tasks, JArray.Parse(config["statusReportNames"]).ToArray());
            var emailBody   = GenerateMicrosoftHtmlTabularReport(storys, config);

            System.IO.File.WriteAllText(Grapple.GetThisFolder() + "email_body.txt", emailBody.ToString());

            Echo.IterationReport(team, iterations, storys, tasks);
        }

        private static List<JToken> AssignTaskMastersToStorys(List<JToken> storys, List<JToken> tasks, string[] includeNames)
        {
            var timeTracker = new Dictionary<string, Dictionary<string, int>>(); /* <story guid, <username, time>> */

            foreach(JToken task in tasks) {

                if(task["Owner"].Type == JTokenType.Null){
                    continue;}

                string user_name = task["Owner"].Value<string>("_refObjectName");

                if (!user_name.Contains(includeNames)) {
                    continue;}

                string story_guid = task["WorkProduct"].Value<string>("_refObjectUUID");

                if (!timeTracker.ContainsKey(story_guid)) {
                    timeTracker.Add(story_guid, new Dictionary<string, int>());}

                int estimate = task["Estimate"].Type != JTokenType.Null ? task.Value<int>("Estimate") : 0;

                if (timeTracker[story_guid].ContainsKey(user_name)){

                    timeTracker[story_guid][user_name] += estimate;
                }else {
                    timeTracker[story_guid].Add(user_name, estimate);
                }
            }

            var mutated_storys = new List<JToken>();
            foreach(JToken story in storys) {

                string story_guid = story.Value<string>("ObjectUUID");

                story["HeroOfTime"] = "DreamTeam";//todo:un-hardcode use the discovered team name
                story["HeroValue"] = 0;

                if(timeTracker.ContainsKey(story_guid)) {

                    foreach (KeyValuePair<string, int> dev_time in timeTracker[story_guid]) {

                        if (dev_time.Value > story.Value<int>("HeroValue")) {
                            story["HeroOfTime"] = dev_time.Key;
                            story["HeroValue"] = dev_time.Value;
                        }
                    }
                }

                mutated_storys.Add(story);
            }

            return mutated_storys;
        }
Esempio n. 8
0
        private static Dictionary<string, Task> BuildTaskPriorityChart(List<Task> tasks, TaiConfig config)
        {
            string[] priority_strings = JArray.Parse(config["taskNames"]).ToArray();
            var priority_chart = new Dictionary<string, Task>();

            foreach(Task task in tasks) {

                string priority_name = task.taskName.ContainsMatch(priority_strings);

                if(priority_name != string.Empty) {
                    if(!priority_chart.ContainsKey(priority_name)) {
                        priority_chart.Add(priority_name, task);
                    }
                }
            }

            return priority_chart;
        }
Esempio n. 9
0
        public static bool Initialize(TaiConfig config)
        {
            BASE_URL = config["apiUrl"] ?? BASE_URL;

            try{
                CACHED_AUTH = GetCachedAuthentication(config["username"], config["password"]);

            }catch{
                Console.WriteLine("No Internet Connection");//meah
                Environment.Exit(0);
            }

            return true;
        }
Esempio n. 10
0
 private static TaiConfig PrepareConfigForTaskTimeAutoFill(TaiConfig conf)
 {
     conf["targetUser"] = conf["targetUser"] ?? conf["username"];
     conf["projectId"] = conf["projectId"] ?? ApiWrapper.GetProjectId(conf["targetUser"]);
     conf["burndownDate"] = conf["burndownDate"] ?? DateTime.Today.ToString("yyyy-MM-dd");
     conf["hoursPerDay"] = conf["hoursPerDay"] ?? "8";
     conf["taskNames"] = conf["taskNames"] ?? new string[]{"Administration", "Regression", "Iteration Planning", "Deployment Planning", "Environment Issue", "User Stories"}.ToJson();
     return conf;
 }
Esempio n. 11
0
 private static TaiConfig PrepareConfigForTaskCreation(TaiConfig conf)
 {
     conf["targetUser"] = conf["targetUser"] ?? conf["username"];
     conf["storyId"] = conf["storyId"] ?? "US00000";
     conf["taskNames"] = conf["taskNames"] ?? "new task for" + conf["targetUser"]; //have a flag that auto gens task name based on story requirements. rexgex split a sentence or something
     return conf;
 }
Esempio n. 12
0
 private static TaiConfig FixMissingEmailProperties(TaiConfig conf)
 {
     conf["emailGreeting"] = conf["emailGreeting"] ?? "Hi Boss";
     conf["emailSignature"] = conf["emailSignature"] ?? "-dev team";
     return conf;
 }
Esempio n. 13
0
        internal static void AutomaticallyFillTaskTime(TaiConfig config)
        {
            config = PrepareConfigForTaskTimeAutoFill(config);

            var target_date = DateTime.Parse(config["burndownDate"]);
            var week_begin_date = target_date.AddDays(-(int)target_date.DayOfWeek);

            var my_id = ApiWrapper.GetTargetUserObjectId(config["targetUser"]);
            var tasks = ApiWrapper.GetTasks(my_id, week_begin_date);

            CreateEmptyTimeCardForTasks(tasks, config);

            int daily_min = Convert.ToInt32(config["hoursPerDay"]);
            int[] total_hours = SumTaskHoursByWeekStart(tasks, week_begin_date);
            var priority_chart = BuildTaskPriorityChart(tasks, config);
            int required_hours_this_week = (int)target_date.DayOfWeek * daily_min;

            Echo.Out(string.Format("burn down hours from {0} to {1} as role: {2}", week_begin_date.ToString("MMMM dd yyyy"), target_date.ToString("MMMM dd yyyy"), "dev"), 2);
            Echo.Out(my_id + " is my ObjectID", 5);
            Echo.Out(tasks.Count + " tasks since "+week_begin_date.ToString("yyyy MM dd"), 5);
            Echo.Out(". . . . . . . . . . .", 5);
            Echo.TaskReport(tasks, week_begin_date);
            Echo.Out("min hours needed to pass inspection: " + required_hours_this_week, 2);
            Echo.Out("You have " + total_hours.Sum() + " hours", 2);

            if (total_hours.Sum() < required_hours_this_week) {

                var workload = new List<JObject>();

                for(int i = 0; i<total_hours.Length; ++i) {

                    int this_days_time      = total_hours[i];
                    string day_name         = Enum.GetName(typeof(DayOfWeek), i);
                    int daily_time_needed   = daily_min - this_days_time; //this allows for overages since we are adding in increments of 8. should only allow for a maximum of (inspection_amount + increment-1)

                    if(total_hours.Sum() < required_hours_this_week && daily_time_needed > 0 /**/ && i!=0 && i !=6) { //hard code ignore of sunday and saturaday for now

                        Echo.Out(day_name + " FAILED inspection. Tai says, 'add some time m**********r!'", 5);

                        int hoursToAdd = (int)Math.Ceiling(((double)daily_time_needed / (double)priority_chart.Count));

                        foreach(KeyValuePair<string, Task> priority in priority_chart) {

                            var task = priority.Value;

                            DateTime post_date = week_begin_date.AddDays(i);

                            JObject newTimeEntryValuePost = new JObject();

                            if(task.weeklyTime[week_begin_date].dailyTime.ContainsKey(post_date)) {

                                newTimeEntryValuePost["Verb"] = "update";
                                newTimeEntryValuePost["ObjectID"] = task.weeklyTime[week_begin_date].dailyTime[post_date].Value<string>("ObjectID");
                                newTimeEntryValuePost["Hours"] = task.weeklyTime[week_begin_date].dailyTime[post_date].Value<int>("Hours") + hoursToAdd;
                            }else{
                                newTimeEntryValuePost["Verb"] = "insert";
                                newTimeEntryValuePost["DateVal"] = post_date.ToString("yyyy-MM-ddTHH:mm:ssZ");
                                newTimeEntryValuePost["TimeEntryItem"] = task.weeklyTime[week_begin_date].timeEntryObjectId;
                                newTimeEntryValuePost["Hours"] = hoursToAdd;
                            }

                            Echo.Out(newTimeEntryValuePost.ToString(), 6);
                            Echo.Out("..........", 6);

                            workload.Add(newTimeEntryValuePost);
                            total_hours[i] += hoursToAdd;
                            daily_time_needed -= hoursToAdd;
                            if(daily_time_needed <= 0){break;}
                        }

                    }else{
                        Echo.Out(day_name + " PASSED inspection");
                    }
                }

                ApiWrapper.SubmitTaskTimeValue(workload);

            }else {
                Echo.Out("Autofilling is not needed since you have already filled out the minimum necessary time");
            }
        }

        internal static void CreateTaskForStory(TaiConfig config)
        {
            config = PrepareConfigForTaskCreation(config);

            JObject newTask = new JObject();
            newTask["Name"] = config["taskNames"];//just create one for now. later iterate over these and create one for each name
            newTask["Description"] = "";
            newTask["Owner"] = ApiWrapper.GetTargetUserObjectId(config["targetUser"]);
            newTask["Estimate"] = "10";
            newTask["State"] = "Defined";
            newTask["TaskIndex"] = 1;
            newTask["WorkProduct"] = ApiWrapper.GetUserStory(config["storyId"]).Value<string>("ObjectID");

            Echo.Out(newTask.ToString(Newtonsoft.Json.Formatting.Indented), 1);
            Echo.Out(ApiWrapper.CreateNewTask(newTask).ToString(Newtonsoft.Json.Formatting.Indented));
        }

        internal static void SetStoryBuildId(TaiConfig config)
        {
            if(config.ContainsKey("storyId") && config.ContainsKey("buildId")) {

                Echo.Out("User story ID: " + config["storyId"], 5);
                Echo.Out("Build ID: " + config["buildId"], 5);

                string UserStoryURL = ApiWrapper.GetUserStoryReferenceUrl(config["storyId"]);

                JObject postJson = new JObject();
                postJson["c_BuildID"] = config["buildId"];

                ApiWrapper.UpdateStoryBuildId(postJson, UserStoryURL);
                Echo.Out("..........", 5);
            }else{
                Echo.Out("you must have both a storyId and buildId in order to perform action: SetStoryBuildId", 1);
            }
        }

        internal static void WriteStatusReportForAnIteration(TaiConfig config)
        {
            config["targetUser"] = config["targetUser"] ?? config["username"];
            config["projectId"] = config["projectId"] ?? ApiWrapper.GetProjectId(config["targetUser"]);
            config["iterationNumber"] = config["iterationNumber"] ?? ApiWrapper.GetIterationNumber(config["projectId"]);

            var team        = ApiWrapper.GetTeamMembers(config["projectId"]);
            var iterations  = ApiWrapper.GetIteration(config["projectId"], config["iterationNumber"]);
            var storys      = ApiWrapper.GetUserStories(config["projectId"], iterations);
            var tasks       = ApiWrapper.GetTasks(storys);
            storys          = AssignTaskMastersToStorys(storys, tasks, JArray.Parse(config["statusReportNames"]).ToArray());
            var emailBody   = GenerateMicrosoftHtmlTabularReport(storys, config);

            System.IO.File.WriteAllText(Grapple.GetThisFolder() + "email_body.txt", emailBody.ToString());

            Echo.IterationReport(team, iterations, storys, tasks);
        }

        private static List<JToken> AssignTaskMastersToStorys(List<JToken> storys, List<JToken> tasks, string[] includeNames)
        {
            var timeTracker = new Dictionary<string, Dictionary<string, int>>(); /* <story guid, <username, time>> */

            foreach(JToken task in tasks) {

                if(task["Owner"].Type == JTokenType.Null){
                    continue;}

                string user_name = task["Owner"].Value<string>("_refObjectName");

                if (!user_name.Contains(includeNames)) {
                    continue;}

                string story_guid = task["WorkProduct"].Value<string>("_refObjectUUID");

                if (!timeTracker.ContainsKey(story_guid)) {
                    timeTracker.Add(story_guid, new Dictionary<string, int>());}

                int estimate = task["Estimate"].Type != JTokenType.Null ? task.Value<int>("Estimate") : 0;

                if (timeTracker[story_guid].ContainsKey(user_name)){

                    timeTracker[story_guid][user_name] += estimate;
                }else {
                    timeTracker[story_guid].Add(user_name, estimate);
                }
            }

            var mutated_storys = new List<JToken>();
            foreach(JToken story in storys) {

                string story_guid = story.Value<string>("ObjectUUID");

                story["HeroOfTime"] = "DreamTeam";//todo:un-hardcode use the discovered team name
                story["HeroValue"] = 0;

                if(timeTracker.ContainsKey(story_guid)) {

                    foreach (KeyValuePair<string, int> dev_time in timeTracker[story_guid]) {

                        if (dev_time.Value > story.Value<int>("HeroValue")) {
                            story["HeroOfTime"] = dev_time.Key;
                            story["HeroValue"] = dev_time.Value;
                        }
                    }
                }

                mutated_storys.Add(story);
            }

            return mutated_storys;
        }