if (week_total_hours < required_hours && time_needed > 0 /**/ && i!=0 && i !=6) { //hard code ignore of sunday and saturaday for now Console.WriteLine(day_name + " FAILED inspection. Tai says, 'add some time m**********r!'"); while (time_needed > 0) { //muaha! now create the new time items using the api //remember we are looking into the payload manipulation via c# foreach(string priority in priority_strings) { //since days only get iterated over once, this will save a max of 6 hours in single day. either iterate over the days while week_time_needed > 0 OR add more priorty strings. the prior is superior because if tere is at least 1 priority, then it will get filled out to the max instead of each priority only getting filled out then closing var post_date = week_begin_date.AddDays(i); string existing_object_id = priority_chart[priority].getTimeValueObjectIdForThisDate(post_date); int existing_hours = priority_chart[priority].getTimeValueHoursForThisDate(post_date); JObject postJson = new JObject(); postJson["Verb"] = existing_object_id != string.Empty ? "update" : "insert"; postJson["ObjectID"] = existing_object_id; postJson["DateVal"] = post_date.ToString("yyyy-MM-ddTHH:mm:ssZ"); postJson["Hours"] = (existing_hours+1); postJson["TimeEntryItem"] = priority_chart[priority].timeEntryObjectId; Console.WriteLine(postJson); Console.WriteLine(".........."); db.PostNewTimeEntryValue(postJson); //if post was a success then add time to mem day_hours[i] += (existing_hours+1); time_needed -= (int)postJson["Hours"]; if(time_needed <= 0){ break;} } } }else{ Console.WriteLine(day_name + " PASSED inspection"); } } }else { Console.WriteLine("Autofilling is not needed since you have already filled out the minimum necessary time"); } } private static StringBuilder GenerateCLIReport(List<JToken> fullTeam, List<JToken> iterations, List<JToken> storys, List<JToken> tasks) { var report = new StringBuilder(); foreach (JToken iteration in iterations){ report.AppendLine((string)iteration["Name"]);} report.AppendLine("==========================\n\n"); report.AppendLine("Team Members"); foreach (JToken person in fullTeam){ report.AppendLine((string)person["DisplayName"]);} report.AppendLine("==========================\n\n"); foreach (JToken story in storys) { var hero = (string)story["HeroOfTime"]; var name = (string)story["Name"]; var risk = (string)story["BlockedReason"]; var status = (string)story["ScheduleState"]; var date = ((string)story["AcceptedDate"]).GetPrettyDate(); var line = string.Format("{0}\n{1}\n{2}\n{3}\n{4}", hero, name, date, status, risk); report.AppendLine(line); report.AppendLine("-----------------------\n"); } return report; } private static StringBuilder GenerateGenerictHtmlTabularReport(List<JToken> storys, RallyBotConfig config) { 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 = (string)story["HeroOfTime"]; var name = (string)story["Name"]; var risk = (string)story["BlockedReason"]; var status = (string)story["ScheduleState"]; var date = ((string)story["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; } private static StringBuilder GenerateMicrosoftHtmlTabularReport(List<JToken> storys, RallyBotConfig config) { 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 = (string)story["HeroOfTime"]; var name = (string)story["Name"]; var risk = (string)story["BlockedReason"]; var status = (string)story["ScheduleState"]; var date = ((string)story["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; } private static StringBuilder GetErrorReport(string[] badInput) { var err_report = new StringBuilder(); foreach(string misunderstood_word in badInput) { err_report.AppendLine("No switch available for: " + misunderstood_word);} return err_report; } private static string GetFarewell() { var farewell = new string[]{ "thinking of you", "your majesty", "with smugness", "smugly yours", "sincerely", "with great jubilation", "fearfully yours", "lurking behind you", "good day", "with regrets", "My Best", "My best to you", "Best", "All Best", "All the best", "Best Wishes", "Bests", "Best Regards", "Regards", "Warm Regards", "Warmest Regards", "Warmest", "Warmly", "Take care", "Many thanks", "Thanks for your consideration", "Hope this helps", "Looking forward", "Rushing", "In haste", "Be well", "Peace", "Yours Truly", "Yours", "Very Truly Yours", "Sincerely", "Sincerely Yours", "Cheers!" }; return farewell[new Random().Next(0, farewell.Length)]; } private static string GetIterationNumberFromTerminal(string someQuestion) { //todo: input validation Console.WriteLine("\n"+someQuestion+"\n"); return Console.ReadLine(); } private static string GetPasswordFromTerminal() { //TODO: actually hide password Console.Write("enter rally password\n\nyour hidden password: "******"Your Rally ProjectId is associated with your specific team; such as dream team, seal team 6, or team impact. In order to get your ProjectId: 1. goto https://rally1.rallydev.com/ 2. login using your credentials 3. In the top left corner, be sure that your team name is selected 4. Once you have selected your team name, the ProjectId will be located in the url after the hashtag(#) symbol 5. be sure to exclude the last letter 'd' What is your ProjectId? "); return Console.ReadLine(); } private static string[] GetTeamMemberFirstNamesFromTerminal() { Console.Write("enter first name(s) of your dev team members seperated by a single space \nex: ross derrick antonio \n\nfirst names: "); return Console.ReadLine().Split(' '); } private static string GetThisFolder() { var path_bits = System.Reflection.Assembly.GetEntryAssembly().Location.Split('\\'); string simple_path_i_have_to_build_because_you_suck_microsoft = ""; for(int i=0; i<(path_bits.Length-1); ++i) { simple_path_i_have_to_build_because_you_suck_microsoft += path_bits[i]+'\\';} return simple_path_i_have_to_build_because_you_suck_microsoft; } private static string GetUsernameFromTerminal() { //todo: validate email format regex ? /w.*\@/w.*\.com Console.Write("enter rally username\nex: [email protected]\n\nyour username: "******"rallybot.conf") { //todo: check if the file exists, if it does not, then create one //todo: encode the password or some other method. ask ross (if there is a plaintext password then erase it and write the back the encoded version as a different serialized property name) RallyBotConfig conf = new RallyBotConfig(@fileLocation); if(!conf.isValidConfiguration) { conf.username = GetUsernameFromTerminal(); conf.password = GetPasswordFromTerminal(); conf.emailGreeting = "Hey Boss"; conf.emailSignature = "dev team"; conf.includeNames = GetTeamMemberFirstNamesFromTerminal(); conf.projectId = GetProjectIdFromTerminal(); SaveNewConfig(@fileLocation, conf); } return conf; } private static string OffensiveGesture() { return @"┌∩┐(>_<)┌∩┐"; } private static bool SaveNewConfig(string newFileLocation, RallyBotConfig config) { //delete any potential existing file at location //create the new file and save the serialized version into it config.ToString() override // if there is a failure, just tell the user. dont try to solve any issues yet return false; } /**M: Sets a story's buildID and Unit Test Config flag (to true by default)**/ private static void SetBuildId(RallyBotConfig config, string buildId) { var buildid = buildId.Length > 0 ? buildId : GetIterationNumberFromTerminal("What BuildId number do want to use ?"); var userStoryId = GetIterationNumberFromTerminal("Please Enter the User story ID (ex: US12345) : "); //sry using your GetIterationNumberFromTerminal() for general console input :P //should prolly do input validation.. Console.WriteLine("User story ID: " + userStoryId); Console.WriteLine("Build ID: " + buildid); var db = new RallyDatabase(config); string UserStoryURL= db.GetUserStoryRef(userStoryId); JObject postJson = new JObject(); postJson["c_BuildID"] = buildid; db.PostNewBuildId(postJson, UserStoryURL); Console.WriteLine(".........."); }
private static void WriteRallyStatusReportForAnIteration(RallyBotConfig config, string iterationArg = "", Delegate emailBuilder = null) { var db = new RallyDatabase(config); var iter_num = iterationArg.Length > 0 ? iterationArg : GetIterationNumberFromTerminal("What Iteration number do you want a report on ?"); var team = db.GetTeamMembers(config.projectId); var iterations = db.GetIteration(config.projectId, iter_num); var storys = db.GetUserStories(config.projectId, iterations); var tasks = db.GetRallyTasks(storys); storys = AssignTaskMastersToStorys(storys, tasks, config.includeNames); var emailBody = GenerateMicrosoftHtmlTabularReport(storys, config); System.IO.File.WriteAllText(@THIS_FOLDER + "email_body.txt", emailBody.ToString()); Console.WriteLine(GenerateCLIReport(team, iterations, storys, tasks)); //todo: offer non microsoft styled tabular data structure. (gmail or pure text) //todo: potentially use outlook api to send email instead of relying on powershell to do the command }
//todo: refactor this savagery. viewers dont h8 private static void AutomaticallySetRallyTime_Alpha(RallyBotConfig config, DateTime targetDate) { //required: must only autofill one week up to the target date (as to not appear suspicious under investigating) //required: support target date in order to backfill easily var week_begin_date = targetDate.AddDays(-(int)targetDate.DayOfWeek); var goal_concept = string.Format("burn down hours from {0} to {1} as role: {2}", week_begin_date.ToString("MMMM dd yyyy"), targetDate.ToString("MMMM dd yyyy"), "dev"); Console.WriteLine(goal_concept); //1. get tasks relating to this.user current task list on rally var db = new RallyDatabase(config); var this_team = db.GetTeamMembers(config.projectId); var my_id = db.GetSpecificTeamMemberObjectId(this_team, config.username); var time_items = db.GetTimeEntryItemsForOneTeamMember(my_id, week_begin_date); //todo: maybe class AWeekAtWork{list<timeitems>, computed int[] dailyHours, computed totalHours, method set priorities (orders list by given name list)} int daily_min = config.hoursPerDay != 0 ? config.hoursPerDay : 8; int[] day_hours = new int[] { 0, 0, 0, 0, 0, 0, 0 }; var priority_chart = new Dictionary<string, TimeEntryItem>(); var priority_strings = new string[]{"Administration", "Regression", "Iteration Planning", "Deployment Planning", "Environment Issue", "User Stories"}; Console.WriteLine(my_id + " is my object_id"); Console.WriteLine(time_items.Count + " time entrys"); Console.WriteLine("# # # # # # # # # # #"); //this loop does 3 very different things. it fills the day_hours array, it establishes priorities, and generates a cli report foreach(TimeEntryItem item in time_items) { Console.WriteLine("Story Name: "+ item.self["WorkProductDisplayString"].ToString()); Console.WriteLine("Task Name: " + item.self["TaskDisplayString"].ToString()); Console.WriteLine("Time Values:\n"); foreach(JToken time_value in item.timeEntryValues) { var t_val_date = Convert.ToDateTime(time_value["DateVal"].ToString()); day_hours[(int)t_val_date.DayOfWeek] += (int)time_value["Hours"]; Console.WriteLine("DateVal: " + ((string)time_value["DateVal"]).GetPrettyDate()); Console.WriteLine("Hours: " + time_value["Hours"]); Console.WriteLine("Last Updated: " + time_value["LastUpdated"]); Console.WriteLine("----------"); } //needs to be after time calcs string priority_name = item.taskName.Contains(priority_strings, true); if(priority_name != string.Empty) { if(!priority_chart.ContainsKey(priority_name)) { priority_chart.Add(priority_name, item); } } Console.WriteLine("==================================================\n\n\n"); } //2. make sure that all the results are returned, should be about 77 for me //(ok good!) //3. find a formula that will determine how many hours i should have for the entire week up until the point/day i desire int required_hours = (int)targetDate.DayOfWeek * daily_min; //this will not support sunday work. maybe support weekend autofilling as a bool switch next version. also allow config of the 8 hours to more or less Console.WriteLine("min hours needed to pass inspection: " + required_hours); Console.WriteLine("You have " + day_hours.Sum() + " hours"); // if you do not match or exceed the min hours, then we will perform some autofilling //then add up the time for all days that week. if the total is less than sun-today * 8, then add new time entry values foreach day where total time < 8 (not in this method fyi) //consider while here while() TODO: instead of doing the submints in this loop. just generate a workload to perform later if (day_hours.Sum() < required_hours) { for(int i = 0; i<day_hours.Length; ++i) { int this_days_time = day_hours[i]; string day_name = Enum.GetName(typeof(DayOfWeek), i); int 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) int week_total_hours= day_hours.Sum();//important to refresh each loop if (week_total_hours < required_hours && time_needed > 0 /**/ && i!=0 && i !=6) { //hard code ignore of sunday and saturaday for now