public static Calendar CreateCourseAppointment(Course course, IIdentity currentUser) { TimeZoneInfo tzi = course.Department.Institution.TimeZone; var currentCal = CreateCal(course); // Create the event, and add it to the iCalendar // Event courseEvt = new Event { Class = "PRIVATE", Created = new CalDateTime(course.CreatedUtc), LastModified = new CalDateTime(course.CourseDatesLastModified), Sequence = course.EmailSequence, Transparency = TransparencyType.Opaque, Status = course.Cancelled ? EventStatus.Cancelled : EventStatus.Confirmed, Uid = "course" + course.Id.ToString(), Priority = 5, Location = course.Room.ShortDescription, Attendees = course.CourseParticipants.Select(MapCourseParticipantToAttendee).ToList(), Summary = course.Department.Abbreviation + " " + course.CourseFormat.CourseType.Abbreviation, IsAllDay = false, Description = course.Department.Name + " " + course.CourseFormat.CourseType.Description, GeographicLocation = GetGeoLocation(course), Organizer = GetOrganizer(course), //DtStamp = - this is probably being inserted - check }; System.Diagnostics.Debug.WriteLine(courseEvt.Organizer); foreach (var cd in course.AllDays().Take(course.CourseFormat.DaysDuration)) { var dayEvt = courseEvt.Copy<Event>(); dayEvt.Start = new CalDateTime(course.StartLocal, tzi.Id); dayEvt.Description += " - " + course.StartLocal.ToString("g"); dayEvt.Duration = TimeSpan.FromMinutes(cd.DurationMins); if (course.CourseFormat.DaysDuration > 1) { string dayNo = $" (day {cd.Day})"; dayEvt.Summary += dayNo; dayEvt.Description += dayNo; } currentCal.Events.Add(dayEvt); } // Create a serialization context and serializer factory. // These will be used to build the serializer for our object. //set alarm Alarm alarm = new Alarm { Action = AlarmAction.Display, Summary = course.Department.Abbreviation + ' ' + course.CourseFormat.CourseType.Abbreviation, Trigger = new Trigger(TimeSpan.FromHours(-1)) }; // Add the alarm to the event courseEvt.Alarms.Add(alarm); return currentCal; }
public static void AppendFacultyMeetingEvent(Calendar cal, Course course) { if (!course.FacultyMeetingUtc.HasValue) { return; } //CalendarMethods.Request is only valid for single event per calendar cal.Method = course.Cancelled ? CalendarMethods.Cancel : CalendarMethods.Publish; var courseEvent = cal.Events.First(); var facultyMeet = CreateFacultyMeetingEvent(course); cal.Events.Add(facultyMeet); }
/// <summary> /// /// </summary> /// <param name="course"></param> /// <param name="user"></param> /// <returns>a lookup true = successfully emailed, false = email failed</returns> public static async Task<ILookup<bool, CourseParticipant>> SendEmail(Course course, IPrincipal user) { using (var cal = Appointment.CreateCourseAppointment(course, user.Identity)) { using (var appt = new AppointmentStream(cal)) { var map = ((Expression<Func<CourseParticipant, CourseParticipantDto>>)new CourseParticipantMaps().MapToDto).Compile(); var faculty = course.CourseParticipants.Where(cp => !map(cp).IsEmailed) .ToLookup(cp => cp.IsFaculty); IEnumerable<Attachment> attachments = new Attachment[0]; using (var client = new SmtpClient()) { var mailMessages = new List<ParticipantMail>(); var sendMail = new Action<CourseParticipant>(cp => { var mail = new MailMessage(); mail.To.AddParticipants(cp.Participant); var confirmEmail = new CourseInvite { CourseParticipant = cp }; mail.CreateHtmlBody(confirmEmail); appt.AddAppointmentsTo(mail); foreach (var a in attachments) { a.ContentStream.Position = 0; mail.Attachments.Add(a); } mailMessages.Add(new ParticipantMail { Message = mail, SendTask = client.SendMailAsync(mail), CourseParticipant = cp }); }); foreach (var cp in faculty[false]) { sendMail(cp); } if (faculty[true].Any()) { if (course.FacultyMeetingUtc.HasValue) { using (var fm = Appointment.CreateFacultyCalendar(course)) { appt.Add(fm); } } attachments = GetFilePaths(course).Select(fp => new Attachment(fp.Value, System.Net.Mime.MediaTypeNames.Application.Zip) { Name = fp.Key }) .Concat(new[] { new Attachment(CreateDocxTimetable.CreateTimetableDocx(course, WebApiConfig.DefaultTimetableTemplatePath), OpenXmlDocxExtensions.DocxMimeType) { Name = CreateDocxTimetable.TimetableName(course)} }); foreach (var cp in faculty[true]) { sendMail(cp); } } await Task.WhenAll(mailMessages.Select(mm => mm.SendTask)); mailMessages.ForEach(mm => mm.Message.Dispose()); return mailMessages.ToLookup(k => k.SendTask.Status == TaskStatus.RanToCompletion, v => v.CourseParticipant); } } } }
private static IEnumerable<KeyValuePair<string, string>> GetFilePaths(Course course) { return (from s in course.CourseSlotActivities let sr = s.Scenario.ScenarioResources.FirstOrDefault(ssr => ssr.FileName != null) where sr != null select new KeyValuePair<string, string>(s.Scenario.BriefDescription, sr.GetServerPath())) .Concat(from ctr in course.CourseSlotActivities let atr = ctr.Activity where atr.FileName != null select new KeyValuePair<string, string>(atr.Description, atr.GetServerPath())); }
static IList<TimetableRow> GetTimeTableRows(Course course) { var start = course.StartLocal; int scenarioCount = 0; var csps = course.CourseSlotPresenters.ToLookup(c=>c.CourseSlotId); //var csfrs = course.CourseScenarioFacultyRoles.ToLookup(c => c.CourseSlotId); var csas = course.CourseSlotActivities.ToDictionary(c => c.CourseSlotId); var emptyStringArray = new string[0]; var returnVar = course.CourseFormat.CourseSlots.Where(cs=>cs.IsActive) .OrderBy(cs=>cs.Order).Select(cs=> { var ttr = new TimetableRow { LocalStart = start, }; CourseSlotActivity activity; csas.TryGetValue(cs.Id, out activity); if (cs.ActivityId.HasValue) { ttr.IsScenario = false; ttr.SlotName = cs.Activity.Name; ttr.SlotActivity = activity?.Activity?.Description; ttr.Faculty = csps[cs.Id]?.Select(csp => csp.Participant.FullName) ?? emptyStringArray; } else { ttr.IsScenario = false; ttr.SlotName = "Scenario " + (++scenarioCount).ToString(); ttr.SlotActivity = activity?.Scenario?.BriefDescription; ttr.Faculty = emptyStringArray;//csfrs[cs.Id]?.Select(csfr => csfr.Participant.FullName) // ?? emptyStringArray; } start += TimeSpan.FromMinutes(cs.MinutesDuration); return ttr; }).ToList(); returnVar.Add(new TimetableRow { LocalStart = start, SlotName = "Finish", Faculty=new string[0]}); return returnVar; }
static void UpdateMetadata(WordprocessingDocument doc, Course course) { //doc.AddCoreProperty(); throw new NotImplementedException(); }
public static string TimetableName(Course course) { return CourseNameWithoutExt(course) + "Timetable.docx"; }
internal static string CourseNameWithoutExt(Course course) { string dateString = course.StartLocal.ToString("d", course.Department.Institution.Culture.CultureInfo).Replace('/', '-'); return $"{course.Department.Abbreviation} {course.CourseFormat.CourseType.Abbreviation} - {dateString}"; }
private static void AddScenarios(MainDocumentPart doc, Course course) { var firstParaWithSectionBreak = doc.RootElement.Descendants<Paragraph>() .First(p => p.Descendants<SectionProperties>().Any()); var allScenarioEls = firstParaWithSectionBreak.Parent.ChildElements .SkipWhile(c => c != firstParaWithSectionBreak) .TakeWhile(c => c.GetType() != typeof(SectionProperties)) .ToList(); var csas = course.CourseSlotActivities .Where(ca => ca.ScenarioId != null) .ToDictionary(ca => ca.CourseSlotId); var manikins = course.CourseSlotManikins .ToLookup(m => m.CourseSlotId); var roles = course.CourseScenarioFacultyRoles .ToLookup(k => k.CourseSlotId); int i = 0; var csss = (from cs in course.CourseFormat.CourseSlots.OrderBy(c=>c.Order) let scenarioNo = ++i where cs.IsActive && (csas.ContainsKey(cs.Id) || manikins[cs.Id].Any() || roles[cs.Id].Any()) select new { cs, scenarioNo}).ToList(); var roleFacultyEls = new List<ScenarioRoleEl>(); allScenarioEls.CloneElements(csss, (mergeFieldName, css, elements) => { switch (mergeFieldName) { case "ScenarioRole": case "ScenarioRoleFaculty": if (roleFacultyEls.Count == 0 || roleFacultyEls[roleFacultyEls.Count - 1].SlotId != css.cs.Id) { roleFacultyEls.Add(new ScenarioRoleEl { Row = elements.First().FindFirstAncestor<TableRow>(), SlotId = css.cs.Id }); } return null; case "ScenarioNo": return "Scenario " + css.scenarioNo.ToString(); case "ScenarioName": case "ScenarioBriefDescription": CourseSlotActivity csab; if (csas.TryGetValue(css.cs.Id, out csab)) { return csab.Scenario?.BriefDescription ?? string.Empty; } return string.Empty; case "ScenarioFullDescription": CourseSlotActivity csaf; if (csas.TryGetValue(css.cs.Id, out csaf)) { return csaf.Scenario?.FullDescription ?? string.Empty; } return string.Empty; case "Manikins": return string.Join("\t",manikins[css.cs.Id].Select(m=>m.Manikin.Description)); default: return $"[Value Not Found - \'{ mergeFieldName }\']"; } }); foreach (var sre in roleFacultyEls) { var scenarioRoles = roles[sre.SlotId].OrderBy(csfr => csfr.FacultyScenarioRole.Order) .ToLookup(csfr => csfr.FacultyScenarioRole); if (scenarioRoles.Count == 0) { sre.Row.FindFirstAncestor<Table>().Remove(); } else { sre.Row.CloneElement(scenarioRoles, (mergeFieldName, role) => { switch (mergeFieldName) { case "ScenarioRole": return role.Key.Description; case "ScenarioRoleFaculty": return string.Join("\n", role.Select(r => r.Participant.FullName)); default: return $"[Value Not Found - \'{ mergeFieldName }\']"; } }); } } }
public static IEnumerable<KeyValuePair<Guid, string>> GetBookedManikins(MedSimDbContext context, Course course) { var refStart = course.StartUtc; var refFinish = course.FinishCourseUtc(); return (from csm in context.CourseSlotManikins let c = csm.Course let lastDay = c.CourseDays.FirstOrDefault(cd => cd.Day == c.CourseFormat.DaysDuration) let cFinish = lastDay == null ? DbFunctions.AddMinutes(c.StartUtc, c.DurationMins) : DbFunctions.AddMinutes(lastDay.StartUtc, lastDay.DurationMins) where c.Id != course.Id && c.StartUtc < refFinish && refStart < cFinish select new { csm.ManikinId, c.CourseFormat.Description, c.Department.Abbreviation }) .ToKeyValuePairList(a => a.ManikinId, a => a.Abbreviation + '-' + a.Description); }
public static string CertificateName(Course course) { return CreateDocxTimetable.CourseNameWithoutExt(course) + " Certificates.pptx"; }
private static Calendar CreateCal(Course course) { var currentCal = new Calendar { Method = course.Cancelled ? CalendarMethods.Cancel : CalendarMethods.Request, }; var timezone = VTimeZone.FromSystemTimeZone(course.Department.Institution.TimeZone); currentCal.AddTimeZone(timezone); return currentCal; }
public static Calendar CreateFacultyCalendar(Course course) { var currentCal = CreateCal(course); var facultyMeet = CreateFacultyMeetingEvent(course); currentCal.Events.Add(facultyMeet); return currentCal; }
private static Organizer GetOrganizer(Course course) { const string SimPlannerInfo = mailto + "*****@*****.**"; //ToDo read from web.config mail settings return new Organizer() { Value = new Uri(SimPlannerInfo), CommonName = "sim-planner.com" }; }
private static GeographicLocation GetGeoLocation(Course course) { if (course.Department.Institution.Latitude.HasValue && course.Department.Institution.Longitude.HasValue) { return new GeographicLocation(course.Department.Institution.Latitude.Value, course.Department.Institution.Longitude.Value); } return null; }
public static MemoryStream CreatePptxCertificates(Course course, string sourceFile) { const string fullNameTxt = "FullName"; byte[] byteArray = File.ReadAllBytes(sourceFile); MemoryStream stream = new MemoryStream(); stream.Write(byteArray, 0, byteArray.Length); using (PresentationDocument document = PresentationDocument.Open(stream, true)) { var master1LayoutPart = document.PresentationPart.SlideMasterParts.First() .SlideLayoutParts.First(); #region MasterSlide var formattedDate = course.StartLocal.ToString("dddd, MMMM dd, yyyy", course.Department.Institution.Culture.CultureInfo); var organisers = course.CourseParticipants.Where(cp => cp.IsOrganiser).ToList(); var organiserCells = new List<OrganiserXml>(organisers.Count); organiserCells.Add(new OrganiserXml()); Draw.Text fullName = null; foreach (var t in master1LayoutPart.SlideLayout.Descendants<Draw.Text>()) { t.Text = t.Text .Replace("CourseTypeDescription", course.CourseFormat.CourseType.Description) .Replace("CourseTypeAbbreviation", course.CourseFormat.CourseType.Abbreviation) .Replace("CourseFormatDescription", course.CourseFormat.Description) .Replace("DepartmentName", course.Department.Name) .Replace("DepartmentAbbreviation", course.Department.Abbreviation) .Replace("InstitutionName", course.Department.Institution.Name) .Replace("InstitutionAbbreviation", course.Department.Institution.Abbreviation) .Replace("StartDate", formattedDate); if (t.Text.Contains(organiserName)) { organiserCells[0].NameCell = t.FindFirstAncestor<Draw.TableCell>(); } if (t.Text.Contains(organiserRole)) { organiserCells[0].RoleCell = t.FindFirstAncestor<Draw.TableCell>(); } if (t.Text.Contains(fullNameTxt)) { fullName = t; } } if (organisers.Count > 0) { var parentTable = (organiserCells[0].NameCell ?? organiserCells[0].RoleCell).FindFirstAncestor<Draw.Table>(); var grid = parentTable.ChildElements<Draw.TableGrid>().First(); var gridCols = grid.ChildElements<Draw.GridColumn>().ToList(); var tableWidth = gridCols.Sum(gc => gc.Width); var requiredColWidths = (tableWidth / organisers.Count); //assuming for now table with 1 col for (int i = 1; i < organisers.Count; i++) { grid.AppendChild(new Draw.GridColumn() { Width = requiredColWidths }); organiserCells.Add(organiserCells[0].Clone()); } for (int i = 0; i < organisers.Count; i++) { organiserCells[i].SetText(organisers[i].Participant.FullName, organisers[i].Participant.ProfessionalRole.Description); } }; #endregion //MasterSlide #region certificates var participants = (from cp in course.CourseParticipants where !cp.IsFaculty orderby cp.Participant.FullName select cp.Participant.FullName).ToList(); //find equivalent id in first slide document.PresentationPart.DeleteParts(document.PresentationPart.SlideParts); SlideIdList slideIdList = document.PresentationPart.Presentation.SlideIdList; foreach (var c in slideIdList.ChildElements) { c.Remove(); } document.PresentationPart.Presentation.Save(); foreach (var part in participants) { IEnumerable<Shape> placeholderShapes; AppendNewSlide(document.PresentationPart, master1LayoutPart, out placeholderShapes); foreach (var r in (from phs in placeholderShapes from dr in phs.Descendants<Draw.Run>() where dr.Text.Text.IndexOf(fullNameTxt, StringComparison.OrdinalIgnoreCase) > -1 select dr)) { //should probably be using innerText and xml powertools regex replace r.Text.Text = r.Text.Text.Replace(fullNameTxt, part); } } document.PresentationPart.Presentation.Save(); //find equivalent id in 1st slide #endregion //certificates } return stream; }
public static MemoryStream CreateTimetableDocx(Course course, string sourceFile) { byte[] byteArray = File.ReadAllBytes(sourceFile); MemoryStream stream = new MemoryStream(); stream.Write(byteArray, 0, byteArray.Length); using (WordprocessingDocument document = WordprocessingDocument.Open(stream, true)) { IFormatProvider prov = course.Department.Institution.Culture.CultureInfo; // If your sourceFile is a different type (e.g., .DOTX), you will need to change the target type like so: document.ChangeDocumentType(WordprocessingDocumentType.Document); // Get the MainPart of the document MainDocumentPart mainPart = document.MainDocumentPart; var mergeFields = mainPart.HeaderParts.Cast<OpenXmlPart>() .Concat(mainPart.FooterParts.Cast<OpenXmlPart>()) .Concat(new OpenXmlPart[] { mainPart }) .Select(x => x.RootElement) .GetMergeFieldDict(); var mergeClassDict = Enum.GetValues(typeof(MergeClassification)) .Cast<MergeClassification>() .ToDictionary(k => k.ToString()); var names = mergeClassDict.Keys.OrderByDescending(k => k).ToList(); //descending so that longer (more precise) names come first var defaultType = MergeClassification.General.ToString(); names.Remove(defaultType); var classifiedMergeFields = mergeFields .ToLookup(fc => mergeClassDict[names.FirstOrDefault(n => fc.Key.StartsWith(n)) ?? defaultType]); var faculty = course.CourseParticipants.ToLookup(cp => cp.IsFaculty); foreach (var mf in classifiedMergeFields[MergeClassification.General]) { string replaceVal; switch (mf.Key.Replace(".",string.Empty).Replace(" ", string.Empty)) { case "CourseFormatDescription": replaceVal = course.CourseFormat.Description ; break; case "CourseStart": replaceVal = course.StartLocal.ToString("D", prov) ; break; case "CourseTypeAbbreviation": replaceVal = course.CourseFormat.CourseType.Abbreviation ; break; case "CourseTypeDescription": replaceVal = course.CourseFormat.CourseType.Description ; break; case "Department": case "DepartmentAbbreviation": replaceVal = course.Department.Abbreviation ; break; case "DepartmentName": replaceVal = course.Department.Name ; break; case "Faculty": replaceVal = string.Join("\t",faculty[true].Select(cp => cp.Participant.FullName)); break; case "Institution": case "InstitutionAbbreviation": replaceVal = course.Department.Institution.Abbreviation ; break; case "InstitutionName": replaceVal = course.Department.Institution.Name ; break; case "Participants": replaceVal = string.Join("\t",faculty[false].Select(cp => cp.Participant.FullName)); break; default: replaceVal = $"[Value Not Found - \'{ mf.Key }\']" ; break; } foreach (var m in mf) { m.InsertMergeFieldText(replaceVal); } } //the first first() will be any of the elements starting with the name slot //the second first is assuming each slot, eg slotStart only occurs once in the doc //could at a later date come up with some fancy ancestors() to find matching subgroup TableRow slotRow = classifiedMergeFields[MergeClassification.Slot].First().First().FindFirstAncestor<TableRow>(); var ttrs = GetTimeTableRows(course); slotRow.CloneElement(ttrs, (mergeFieldName, ttr) => { switch (mergeFieldName) { case "SlotStart": return ttr.LocalStart.ToString("t", prov); case "SlotActivity": return ttr.SlotActivity ?? string.Empty; case "SlotName": return ttr.SlotName; case "SlotFaculty": return string.Join("\n", ttr.Faculty); default: return $"[Value Not Found - \'{ mergeFieldName }\']"; } }); AddScenarios(mainPart, course); return stream; } }
public static Event CreateFacultyMeetingEvent(Course course) { Event meeting = new Event { Class = "PRIVATE", Created = new CalDateTime(course.CreatedUtc), LastModified = new CalDateTime(course.FacultyMeetingDatesLastModified), Sequence = course.EmailSequence, Uid = "planning" + course.Id.ToString(), Priority = 5, Organizer = GetOrganizer(course), Transparency = TransparencyType.Opaque, Status = course.Cancelled ? EventStatus.Cancelled : EventStatus.Confirmed, Description = "planning meeting for " + course.Department.Name + " " + course.CourseFormat.CourseType.Description + " - " + course.StartLocal.ToString("g"), Summary = course.Department.Abbreviation + " " + course.CourseFormat.CourseType.Abbreviation + " planning meeting for " + course.StartLocal.ToString("d"), Attendees = course.CourseParticipants.Where(cp => cp.IsFaculty).Select(MapCourseParticipantToAttendee).ToList(), Start = new CalDateTime(course.FacultyMeetingLocal.Value, course.Department.Institution.StandardTimeZone), GeographicLocation = GetGeoLocation(course) }; // Set information about the event if (course.FacultyMeetingDuration.HasValue) meeting.Duration = TimeSpan.FromMinutes(course.FacultyMeetingDuration.Value); //evt.Name = course.Department.Abbreviation + " " + course.CourseFormat.CourseType.Abbreviation; //evt.Description = course.Department.Name + " " + course.CourseFormat.CourseType.Description; if (course.FacultyMeetingRoom != null) meeting.Location = course.FacultyMeetingRoom.ShortDescription; Alarm alarm = new Alarm { Action = AlarmAction.Display, Summary = meeting.Summary, Trigger = new Trigger(TimeSpan.FromHours(-1)) }; // Add the alarm to the event meeting.Alarms.Add(alarm); return meeting; }