/// <summary> /// Recursively through all fields in a form to find fields of type ExternalMediaField and /// checks whether the media sources referred by them exist. Returns the list of URLs of media /// sources that does not exist. /// </summary> /// <param name="model">The form or field.</param> /// <returns></returns> public List <string> CheckMedia(CFXmlModel model) { List <string> errorneousMedia = new List <string>(); if (typeof(Form).IsAssignableFrom(model.GetType())) { Form form = model as Form; foreach (var child in form.Fields) { errorneousMedia.AddRange(CheckMedia(child)); } } else if (typeof(ExternalMediaField).IsAssignableFrom(model.GetType())) { if (!CheckMedia((model as ExternalMediaField).Source)) { errorneousMedia.Add((model as ExternalMediaField).Source); } } else if (typeof(CompositeFormField).IsAssignableFrom(model.GetType())) { CompositeFormField cfield = model as CompositeFormField; foreach (var child in cfield.Header) { errorneousMedia.AddRange(CheckMedia(child)); } foreach (var child in cfield.Fields) { errorneousMedia.AddRange(CheckMedia(child)); } foreach (var child in cfield.Footer) { errorneousMedia.AddRange(CheckMedia(child)); } } return(errorneousMedia); }
public Form CreateForm(FormIngestionViewModel model) { try { model.ColumnHeadings = GetColumnHeadings(model.DataSheet); string lastColName = GetColumnName(model.ColumnHeadings.Count - 1); string dataRange = string.Format("'{0}'!A2:{1}", model.DataSheet, lastColName); var request = SheetsService.Spreadsheets.Values.Get(Spreadsheet.SpreadsheetId, dataRange); var result = request.Execute(); if (string.IsNullOrEmpty(model.ListIdColumn)) { throw new Exception("Please identify the List ID column"); } int listIdCol = model.ColumnHeadings.IndexOf(model.ListIdColumn); if (string.IsNullOrEmpty(model.BlockIdColumn)) { throw new Exception("Please identify the Block ID column"); } int blockIdCol = model.ColumnHeadings.IndexOf(model.BlockIdColumn); List <int> preContextColIndicies = model.PreContextColumns.Select(s => model.ColumnHeadings.IndexOf(s)).ToList(); if (string.IsNullOrEmpty(model.QuestionColumn)) { throw new Exception("Please idenfify the Question column"); } int questionCol = model.ColumnHeadings.IndexOf(model.QuestionColumn); if (string.IsNullOrEmpty(model.AnswerTypeColumn)) { throw new Exception("Please idenfify the Answer Type column"); } int answerTypeCol = model.ColumnHeadings.IndexOf(model.AnswerTypeColumn); if (string.IsNullOrEmpty(model.AnswerOptionsColumn)) { throw new Exception("Please idenfify the Answer Options column"); } int answerOptionsCol = model.ColumnHeadings.IndexOf(model.AnswerOptionsColumn); if (string.IsNullOrEmpty(model.QuestionIdColumn)) { throw new Exception("Please idenfify the Question ID column"); } int questionIdCol = model.ColumnHeadings.IndexOf(model.QuestionIdColumn); if (string.IsNullOrEmpty(model.IsRequiredIndicatorColumn)) { throw new Exception("Please idenfify the Required Question Indicator column"); } int isRequiredCol = model.ColumnHeadings.IndexOf(model.IsRequiredIndicatorColumn); if (string.IsNullOrEmpty(model.AudioFileColumn)) { throw new Exception("Please idenfify the Audio File column"); } int audioFileCol = model.ColumnHeadings.IndexOf(model.AudioFileColumn); string urlBase = string.IsNullOrEmpty(model.MediaFolderUrl) ? "/media/" : model.MediaFolderUrl.TrimEnd(new char[] { '/' }) + "/"; //Iterating through all the data and creating numbered lists and list of blocks, list of headers and list of footers //The key of each dictionary element represents the list where each block, header or foter belongs to. List <CompositeFormField> listFields = new List <CompositeFormField>(); Dictionary <int, List <CompositeFormField> > blockFieldSets = new Dictionary <int, List <CompositeFormField> >(); Dictionary <int, CompositeFormField> headers = new Dictionary <int, CompositeFormField>(); Dictionary <int, CompositeFormField> footers = new Dictionary <int, CompositeFormField>(); var uniqueListIds = result.Values.Select(x => x[listIdCol] as string).Distinct().ToList(); foreach (var listId in uniqueListIds.Where(x => x != "*").Select(x => int.Parse(x))) { listFields.Add(new CompositeFormField() { Name = "Page " + listId, Page = listId }); blockFieldSets.Add(listId, new List <CompositeFormField>()); headers.Add(listId, new CompositeFormField() { Name = "Footer" }); footers.Add(listId, new CompositeFormField() { Name = "Header" }); } //Iterating through all the data and creating blocks, headers, footers and questions for (int index = 0; index < result.Values.Count; ++index) { var row = result.Values[index]; List <string> values = row.Select(s => s.ToString().Trim()).ToList(); //Creating the question List <string> preContexts = preContextColIndicies.Select(i => values[i]).ToList(); string questionText = values[questionCol]; string questionId = values[questionIdCol]; string answerType = values[answerTypeCol]; string answerOptions = answerOptionsCol < values.Count ? values[answerOptionsCol] : ""; if (string.IsNullOrEmpty(answerType)) { if (string.IsNullOrEmpty(answerOptions)) { answerType = "ShortText"; } else { answerType = "RadioButtonSet"; } } /// /// Each precontext and question are represented by a composite field inside the selected block. /// CompositeFormField surveyItem = new CompositeFormField() { Name = "Question " + questionId }; foreach (string pc in preContexts) { if (!string.IsNullOrEmpty(pc.Trim())) { HtmlField html = new HtmlField() { Name = "Description" }; html.SetDescription(pc); surveyItem.InsertChildElement("./fields", html.Data); } } //Adding the question text HtmlField questionTextField = new HtmlField() { Name = "Text" }; questionTextField.SetDescription(questionText); surveyItem.InsertChildElement("./fields", questionTextField.Data); //Adding the media file string mediaFile = audioFileCol < values.Count ? values[audioFileCol].TrimStart(new char[] { '/' }) : null; if (!string.IsNullOrEmpty(mediaFile)) { ExternalMediaField mf = new ExternalMediaField() { Name = "Media", Source = urlBase + mediaFile, MediaType = Models.Data.CFDataFile.MimeType.Audio, PlayOnce = true, PlayOnShow = true }; surveyItem.InsertChildElement("./fields", mf.Data); } FormField question = null; if (answerType == "ShortText") { question = new TextField(); } else if (answerType == "Number") { question = new NumberField(); } else if (answerType == "Scale") { List <string> options = answerOptions.Split(new char[] { '|' }).Select(s => s.Trim()).ToList(); string[] begin = options[0].Split(new char[] { ':' }); string[] end = options[1].Split(new char[] { ':' }); question = new SliderField() { Min = begin.Length > 0 ? int.Parse(begin[0]) : 0, Max = end.Length > 0 ? int.Parse(end[0]) : 1, MinLabel = begin.Length > 1 ? begin[1] : "", MaxLabel = end.Length > 1 ? end[1] : "" }; } else if (answerType == "RadioButtonSet" || answerType == "DropDown" || answerType == "CheckBoxSet") { List <string> optionStrings = answerOptions.Split(new char[] { '|' }).Select(s => s.Trim()).ToList(); List <Option> options = new List <Option>(); foreach (var optVal in optionStrings) { Option opt = new Option(); opt.Value = new List <TextValue>() { new TextValue() { Value = optVal, LanguageCode = "en" } }; options.Add(opt); } if (answerType == "RadioButtonSet") { question = new RadioButtonSet() { Options = options } } ; else if (answerType == "CheckBoxSet") { question = new CheckBoxSet() { Options = options } } ; else { question = new DropDownMenu() { Options = options } }; } else if (answerType == "TextArea") { question = new TextArea(); } else if (answerType == "Attachment") { question = new Attachment(); } ////else if(answerType == "DropDown") ////{ //// List<string> optionStrings = answerOptions.Split(new char[] { '|' }).Select(s => s.Trim()).ToList(); //// List<Option> options = new List<Option>(); //// foreach (var optVal in optionStrings) //// { //// Option opt = new Option(); //// opt.Value = new List<TextValue>() { new TextValue() { Value = optVal, LanguageCode = "en" } }; //// options.Add(opt); //// } //// question = new DropDownMenu() { Options = options }; ////} else { throw new Exception(string.Format("Answer type \"{0}\" is not implemented in survey form ingestion.", answerType)); } question.Name = "Answer"; question.ReferenceLabel = questionId; question.IsRequired = values[isRequiredCol] == "1"; question.SetAttribute("question-id", values[questionIdCol].ToString()); surveyItem.InsertChildElement("./fields", question.Data); //Adding the question to the header, footer or the appropriate block of the //selected list (or all the lists, if list number is *) var listIdStr = values[listIdCol]; string blockIdStr = values[blockIdCol].ToUpper(); var applicableLists = listIdStr == "*" ? listFields : listFields.Where(x => x.Page == int.Parse(listIdStr)); foreach (var list in applicableLists) { if (blockIdStr == "H") { headers[list.Page].InsertChildElement("./fields", surveyItem.Data); } else if (blockIdStr == "F") { footers[list.Page].InsertChildElement("./fields", surveyItem.Data); } else { var blockSet = blockFieldSets[list.Page]; var block = blockSet.Where(x => x.Page == int.Parse(blockIdStr)).FirstOrDefault(); if (block == null) { block = new CompositeFormField() { Name = "Block " + blockIdStr, Page = int.Parse(blockIdStr) }; blockSet.Add(block); } block.InsertChildElement("./fields", surveyItem.Data); } } }//END: foreach (var row in result.Values) /// /// By this point, we have all "lists" in the listFields array and all "blocks" corresponding to /// each of those lists in the blockFieldSets dictionary. /// //Inserting all blocks into each list entry foreach (var list in listFields) { List <CompositeFormField> blocks = blockFieldSets[list.Page]; foreach (var block in blocks) { list.InsertChildElement("./fields", block.Data); } if (headers[list.Page].Fields.Any()) { list.InsertChildElement("./header", headers[list.Page].Data); } if (footers[list.Page].Fields.Any()) { list.InsertChildElement("./footer", footers[list.Page].Data); } } Form form = new Form(); form.SetName(model.FormName); form.SetDescription(model.FormDescription); foreach (var field in listFields) { form.InsertChildElement("./fields", field.Data); } form.Serialize(); return(form); ////// //If the block number is H or F, the question goes to the header or the footer section of a block. ////// if (blockIdCol.HasValue && values[blockIdCol.Value].ToUpper() == "H") ////// { ////// string lsitNum = listIdCol.HasValue ? values[listIdCol.Value] : "0"; ////// if (!headers.ContainsKey(lsitNum)) ////// headers.Add(lsitNum, new List<CompositeFormField>()); ////// } ////// //Otherwise, this is a regular question. ////// string listNum = listIdCol.HasValue ? values[listIdCol.Value] : "0"; ////// string blockNum = blockIdCol.HasValue ? values[blockIdCol.Value] : "0"; ////// List<string> preContexts = preContextColIndicies.Select(i => values[i]).ToList(); ////// string questionText = values[questionCol]; ////// string answerType = answerOptionsCol.HasValue ? values[answerOptionsCol.Value] : ""; ////// string answerOptions = answerOptionsCol.HasValue ? values[answerOptionsCol.Value] : ""; ////// if (string.IsNullOrEmpty(answerType)) ////// { ////// if (string.IsNullOrEmpty(answerOptions)) ////// answerType = "TextField"; ////// else ////// answerType = "RadioButtonSet"; ////// } ////// /// ////// /// Each list is represented by a composite field at the top level of the form. ////// ///The list number is stored in the "page" property of the field. ////// ///Get the composite field representing the given list number, or create a new one if it doesn't exist ////// /// ////// CompositeFormField list = listFields.Where(field => field.Page == int.Parse(listNum)).FirstOrDefault(); ////// if(list == null) ////// { ////// list = new CompositeFormField() { Page = int.Parse(listNum) }; ////// listFields.Add(list); ////// blockFieldSets.Add(listNum, new List<CompositeFormField>()); //Placehoder for blocks of this list. ////// } ////// /// ////// /// Each block is represented by a composite field in the "list". ////// ///The block number is stored in the "page" propoerty of this composite field. ////// ///Get the composite field representinhg the give block number from the selected list. pr create a new one if it doesn't exist ////// /// ////// List<CompositeFormField> blocks = blockFieldSets[listNum]; ////// CompositeFormField block = blocks.Where(field => field.Page == int.Parse(blockNum)).FirstOrDefault(); ////// if(block == null) ////// { ////// block = new CompositeFormField() { Page = int.Parse(blockNum) }; ////// blocks.Add(block); ////// } ////// /// ////// /// Each precontext and question are represented by a composite field inside the selected block. ////// /// ////// CompositeFormField surveyItem = new CompositeFormField(); ////// foreach(string pc in preContexts) ////// { ////// if(!string.IsNullOrEmpty(pc.Trim())) ////// { ////// HtmlField html = new HtmlField(); ////// html.SetDescription(pc); ////// surveyItem.InsertChildElement("./fields", html.Data); ////// } ////// } ////// FormField question = null; ////// if (answerType == "TextField") ////// question = new TextField(); ////// else if (answerOptions == "RadioButtonSet") ////// { ////// List<string> optionStrings = answerOptions.Split(new char[] { '\n' }).Select(s => s.Trim()).ToList(); ////// List<Option> options = new List<Option>(); ////// foreach (var optVal in optionStrings) ////// { ////// Option opt = new Option(); ////// opt.Value = new List<TextValue>() { new TextValue() { Value = optVal } }; ////// options.Add(opt); ////// } ////// question = new RadioButtonSet() ////// { ////// Options = options ////// }; ////// } ////// else ////// throw new Exception(string.Format("Answer type \"{0}\" is not implemented in survey form ingestion.")); ////// question.SetName(questionText); ////// surveyItem.InsertChildElement("./fields", question.Data); ////// block.InsertChildElement("./fields", surveyItem.Data); //////} //////// /// //////// /// By this point, we have all "lists" in the listFields array and all "blocks" corresponding to //////// /// each of those lists in the blockFieldSets dictionary. //////// /// //////// //Inserting all blocks into each list entry //////// foreach(var list in listFields) //////// { //////// List<CompositeFormField> blocks = blockFieldSets[list.Page.ToString()]; //////// foreach (var block in blocks) //////// list.InsertChildElement("./fields", block.Data); //////// } //////// Form form = new Form(); //////// form.SetName(model.FormName); //////// form.SetDescription(model.FormDescription); //////// foreach (var field in listFields) //////// form.InsertChildElement("./fields", field.Data); //////// form.Serialize(); //////// return form; } catch (Exception ex) { model.Error = ex.Message; } return(null); }
////private void EvaluateCompositeField(IEnumerable<FormField> fields) ////{ //// foreach(var field in fields) //// { //// if(field is CompositeFormField) //// { //// if (((CompositeFormField)field).Shuffle) //// { //// //TODO: Shuffle //// } //// EvaluateCompositeField(((CompositeFormField)field).Fields); //// } //// } ////} /// <summary> /// Create a form submission based on a specified form template. /// </summary> /// <param name="formTemplateId">The template to create the submission on.</param> /// <returns>The newly created submission.</returns> public Form CreateSubmissionForm(int formTemplateId, bool enforceLists, bool shuffleBlocks, bool shuffleQuestions, CompositeFormField.eStepState questionStepOption, CompositeFormField.eStepState questionPartsStepOption) { //Obtaining the template Form template = Db.FormTemplates.Where(m => m.Id == formTemplateId).FirstOrDefault(); //Creating a clone of the template and returning it. We don't want to return the template //itself to avoid saving user data into the template. Form submission = new Form() { Data = template.Data }; submission.Id = template.Id; Random rand = new Random(); if (enforceLists) { //Assumes the top-level CompositeFormFields represents of the form sublists of fields in the form and selects only one of them //for the submission var listSet = submission.Fields.Where(field => field is CompositeFormField); var listCount = listSet.Count(); int selectedIndex = rand.Next(0, listCount); CompositeFormField selectedList = listSet.Skip(selectedIndex).FirstOrDefault() as CompositeFormField; //replacing the fields of the submission with composite field which represents the elected list submission.Fields = submission.Fields.Where(field => field.Guid == selectedList.Guid || !(field is CompositeFormField)).ToList(); } ////EvaluateCompositeField(submission.Fields); if (shuffleBlocks) { //Assumes that the top-level CompositeFormFields of the form represents sublists of felds and CompositeFormFields in each of those //sublists represent blocks, and shuffles those blocks var listSet = submission.Fields.Where(field => field is CompositeFormField).Select(field => field as CompositeFormField); foreach (var list in listSet) { List <CompositeFormField> blockSet = (list as CompositeFormField).Fields .Where(b => b is CompositeFormField) .Select(b => b as CompositeFormField) .ToList(); List <CompositeFormField> shuffledBlockSet = new List <CompositeFormField>(); int n = blockSet.Count; while (n > 0) { var selectedIndex = rand.Next(0, n); shuffledBlockSet.Add(blockSet[selectedIndex]); blockSet.RemoveAt(selectedIndex); --n; } //Replacing the fields with the shuffeled block set list.Fields = shuffledBlockSet; } } if (shuffleQuestions) { //Assumes that the top-level CompositeFormFields of the form represents sublists of felds and CompositeFormFields in each of those //sublists represent blocks, and each block to contain quesitons. This code section shudffles questions inside those blocks. var listSet = submission.Fields.Where(field => field is CompositeFormField).Select(field => field as CompositeFormField); foreach (var list in listSet) { List <CompositeFormField> blockSet = (list as CompositeFormField).Fields .Where(b => b is CompositeFormField) .Select(b => b as CompositeFormField) .ToList(); foreach (var block in blockSet) { List <FormField> sourceFieldSet = block.Fields.ToList(); List <FormField> shuffledFieldSet = new List <FormField>(); int n = sourceFieldSet.Count; while (n > 0) { var selectedIndex = rand.Next(0, n); shuffledFieldSet.Add(sourceFieldSet[selectedIndex]); sourceFieldSet.RemoveAt(selectedIndex); --n; } //Replacing the fields with the shuffeled block set block.Fields = shuffledFieldSet; } } } //Activating stepping through if (questionStepOption == CompositeFormField.eStepState.None && questionPartsStepOption != CompositeFormField.eStepState.None) { questionStepOption = CompositeFormField.eStepState.StepThrough; } if (questionStepOption != CompositeFormField.eStepState.None) { //Assumes that the top-level CompositeFormFields of the form represents sublists of felds and CompositeFormFields in each of those //sublists represent blocks, and each block to contain quesitons. This code initializes the step-through-children property of //the blocks and the questions in order to enable shuffling questions and question-parts, respectively. foreach (var list in submission.Fields.Where(field => field is CompositeFormField).Select(field => field as CompositeFormField)) { foreach (var headerItem in list.Header.Where(h => h is CompositeFormField).Select(h => h as CompositeFormField)) { headerItem.StepState = questionStepOption; if (questionPartsStepOption != CompositeFormField.eStepState.None) { foreach (var question in headerItem.Fields.Where(field => field is CompositeFormField).Select(field => field as CompositeFormField)) { question.StepState = questionPartsStepOption; } } } foreach (var block in list.Fields.Where(field => field is CompositeFormField).Select(field => field as CompositeFormField)) { block.StepState = questionStepOption; if (questionPartsStepOption != CompositeFormField.eStepState.None) { foreach (var question in block.Fields.Where(field => field is CompositeFormField).Select(field => field as CompositeFormField)) { question.StepState = questionPartsStepOption; } } } foreach (var footerItem in list.Footer.Where(h => h is CompositeFormField).Select(h => h as CompositeFormField)) { footerItem.StepState = questionStepOption; if (questionPartsStepOption != CompositeFormField.eStepState.None) { foreach (var question in footerItem.Fields.Where(field => field is CompositeFormField).Select(field => field as CompositeFormField)) { question.StepState = questionPartsStepOption; } } } } //throw new NotImplementedException("Handle stepping through footer questions here ..."); } //Removing the audit trail from the created form since the current trail contains info //from the form template creation. XElement audit = submission.Data.Element("audit"); if (audit != null) { audit.Remove(); } return(submission); }
/// <summary> /// Creates a basic form to be used for testing. /// </summary> /// <param name="title">The name of the form to be using.</param> /// <param name="sectionCount">how many sections will be shown.</param> /// <param name="questionTypesPerSection">The types of questions to be shown in each section.</param> /// <param name="sectionTitleFunc">How to define the title of each section.</param> /// <param name="questionTitleFunc">How to define the title of each question.</param> /// <param name="setSectionDetailsFunc">Adds any details to the section.</param> /// <param name="setQuestionDetailsFunc">Adds any details to the question.</param> /// <returns></returns> static Form GenerateTestForm(string name, int sectionCount, IEnumerable <eQuestionType> questionTypesPerSection, Func <int, string> sectionTitleFunc, Func <int, int, eQuestionType, string> questionTitleFunc, Action <int, CompositeFormField> setSectionDetailsFunc = null, Action <int, int, FormField> setQuestionDetailsFunc = null) { List <Form> sections = new List <Form>(); for (int i = 0; i < sectionCount; ++i) { Form form = new Form(); List <FormField> fields = new List <FormField>(); int questionId = 0; foreach (eQuestionType type in questionTypesPerSection) { ++questionId; FormField field; switch (type) { default: field = new TextField(); break; } field.Name = questionTitleFunc(i, questionId, type); if (setQuestionDetailsFunc != null) { setQuestionDetailsFunc(i, questionId, field); } fields.Add(field); } form.Fields = fields.AsReadOnly(); sections.Add(form); } if (sections.Count == 1) { sections[0].Name = name; return(sections[0]); } Form result = new Form(); List <FormField> resultFields = new List <FormField>(); for (int i = 0; i < sections.Count; ++i) { Form section = sections[i]; CompositeFormField field = new CompositeFormField(); field.Fields = section.Fields; field.Name = sectionTitleFunc(i); if (setSectionDetailsFunc != null) { setSectionDetailsFunc(i, field); } resultFields.Add(field); } result.Name = name; result.Fields = resultFields.AsReadOnly(); return(result); }