/// <summary>
        /// Filters loaded data based on a given criteria set
        /// </summary>
        /// <param name="vm"></param>
        private void FilterStudentsByGrade(TimelineAnalysisViewModel vm)
        {
            //present options to filter by
            List <string> gradingCriteria = vm.GetAllGrades();

            Console.WriteLine("***Grading Categories***");
            for (int i = 0; i < gradingCriteria.Count; i++)
            {
                Console.WriteLine("{0}: {1}", i, gradingCriteria[i]);
            }
            Console.Write("Select category: ");
            string categoryStr = Console.ReadLine();

            //ask for min/max filter
            Console.Write("Enter max score [100%]: ");
            string maxScoreStr = Console.ReadLine();

            Console.Write("Enter min score [0%]: ");
            string minScoreStr = Console.ReadLine();

            //parse strings, send off to VM for processing
            int    category = -1;
            double maxScore = 200;
            double minScore = 0;

            if (Int32.TryParse(categoryStr, out category) && category > -1)
            {
                if (Double.TryParse(maxScoreStr, out maxScore) == false)
                {
                    maxScore = 200;
                }
                if (Double.TryParse(minScoreStr, out minScore) == false)
                {
                    minScore = 0;
                }

                int result = vm.FilterByGrade(gradingCriteria[category], minScore, maxScore);
                Console.WriteLine("{0} entries removed from dataset.", result);
            }
            else
            {
                Console.WriteLine("Error parsing user input.");
            }
        }
        /// <summary>
        /// Creates a matrix of cycle activity for each student
        /// </summary>
        /// <param name="vm"></param>
        private void OrderTransitionsByDate(TimelineAnalysisViewModel vm)
        {
            //step 1: get list of files to process
            List <string> filesToProcess = new List <string>();
            string        fileName       = "a";

            Console.WriteLine("Enter files to process (-1 to stop)");
            while ((fileName = GetFile()).Length > 0)
            {
                filesToProcess.Add(fileName);
            }

            //step 2: setup grade-bands (e.g. A, B, C, etc.)  Hard coded for now as this is just for a
            //single class
            double maxScore = 200;

            double[] gradeRanges = { 90, 78, 69, 60, 0 };
            string[] gradeMap    = { "A", "B", "C", "D", "F" };

            //this produces a lot of files, so create a separate directory for the output
            string outputDirectory = "TransitionsByDate";

            if (Directory.Exists(outputDirectory) == false)
            {
                Directory.CreateDirectory(outputDirectory);
            }

            //finally, begin processing
            //reset max score for A students
            maxScore = 200;

            //based on currently existing code, it is easier to reopen the file for
            //each grade range
            for (int i = 0; i < gradeRanges.Length; i++)
            {
                double bound = gradeRanges[i];

                //reload the files
                LoadFile(filesToProcess[0]);
                for (int j = 1; j < filesToProcess.Count; j++)
                {
                    vm.AppendTimeline(filesToProcess[j]);
                }

                //get grade data
                vm.AttachGrades();

                //filter based on grade data
                vm.FilterByGrade("Assignment AVG", bound, maxScore);

                //get transitions for this grade level
                var result = vm.OrderTransitionsByDate();
                var byDate = result.Item1;

                //update scores for next grade boundary
                maxScore = bound - 0.01;

                //hold all day keys for easier access
                int[] keys = byDate.Keys.OrderBy(k => k).ToArray();

                //figure out all transitions
                Dictionary <string, string> transitionsDict = new Dictionary <string, string>();
                foreach (int key in keys)
                {
                    foreach (string transition in byDate[key].Keys)
                    {
                        transitionsDict[transition] = transition;
                    }
                }
                string[] transitions = transitionsDict.Keys.ToArray();

                //write aggregate information to a file
                CsvWriter writer = new CsvWriter();

                //blank line for transitions
                writer.AddToCurrentLine("Transition");

                //add in header row
                foreach (int key in keys)
                {
                    writer.AddToCurrentLine(key);
                }
                writer.CreateNewRow();

                //add in data
                foreach (string transition in transitions)
                {
                    //data for given transition
                    writer.AddToCurrentLine("T: " + transition);
                    foreach (int key in keys)
                    {
                        if (byDate[key].ContainsKey(transition))
                        {
                            //add in data for given transition
                            writer.AddToCurrentLine(byDate[key][transition].Count);
                        }
                        else
                        {
                            //no data, add a 0
                            writer.AddToCurrentLine(0);
                        }
                    }
                    writer.CreateNewRow();
                }

                //aggregate class results
                using (TextWriter tw = File.CreateText(string.Format("{0}/aggregate_{1}.csv", outputDirectory, gradeMap[i])))
                {
                    tw.Write(writer.ToString());
                    Console.WriteLine("Created file aggregate_{0}.csv", gradeMap[i]);
                }

                //write individual student information to file
                writer = new CsvWriter();

                writer.AddToCurrentLine("UserID");

                //blank line for transitions
                writer.AddToCurrentLine("Transition");

                //add in header row
                foreach (int key in keys)
                {
                    writer.AddToCurrentLine(key);
                }
                writer.CreateNewRow();

                //add in data
                var userData = result.Item2;
                foreach (int userId in userData.Keys)
                {
                    foreach (string transition in transitions)
                    {
                        //user id
                        writer.AddToCurrentLine(userId);

                        //data for given transition
                        writer.AddToCurrentLine("T: " + transition);
                        foreach (int key in keys)
                        {
                            if (userData[userId][key].ContainsKey(transition))
                            {
                                //add in data for given transition
                                writer.AddToCurrentLine(userData[userId][key][transition].Count);
                            }
                            else
                            {
                                //no data, add a 0
                                writer.AddToCurrentLine(0);
                            }
                        }
                        writer.CreateNewRow();
                    }
                }

                using (TextWriter tw = File.CreateText(string.Format("{0}/students_{1}.csv", outputDirectory, gradeMap[i])))
                {
                    tw.Write(writer.ToString());
                    Console.WriteLine("Created file students_{0}.csv", gradeMap[i]);
                }
            }
        }
        /*
         * What I want to do:
         *  For each assignment:
         *      figure out common sequences of length m to n
         *      For each student, for each grade band (A-F), again determine frequences of length m to n
         *      Build a frequency distribution for each grade band by sequence
         * */
        private void BuildTransitionFrequencyCounts(TimelineAnalysisViewModel vm)
        {
            //step 1: get list of files to process
            List <string> filesToProcess = new List <string>();
            string        fileName       = "a";

            Console.WriteLine("Enter files to process (-1 to stop)");
            while ((fileName = GetFile()).Length > 0)
            {
                filesToProcess.Add(fileName);
            }

            //step 2: setup grade-bands (e.g. A, B, C, etc.)  Hard coded for now as this is just for a
            //single class
            double maxScore = 200;

            double[] gradeRanges = { 90, 78, 69, 60, 0 };
            string[] gradeMap    = { "A", "B", "C", "D", "F" };

            //step 3: get sequence boundaries.  Again, hard coded for now
            int startingSequenceLength = 2;
            int endingSequenceLength   = 25;

            //step 4: get assignments.
            string[] assignments = { "Assignment #1",
                                     "Assignment #2",
                                     "Assignment #3",
                                     "Assignment #4",
                                     "Assignment #5",
                                     "Assignment #6",
                                     "Assignment #7" };
            int      assignmentCounter = 0;

            //this produces a lot of files, so create a separate directory for the output
            string outputDirectory = "TransitionFrequencyCounts";

            if (Directory.Exists(outputDirectory) == false)
            {
                Directory.CreateDirectory(outputDirectory);
            }


            //finally, begin processing
            foreach (string fileToProcess in filesToProcess)
            {
                string folderName = fileToProcess.Replace("#", "");
                string outputPath = Path.Combine(outputDirectory, folderName);
                if (Directory.Exists(outputPath) == false)
                {
                    Directory.CreateDirectory(outputPath);
                }
                for (int sequenceLength = startingSequenceLength; sequenceLength <= endingSequenceLength; sequenceLength++)
                {
                    //reset max score for A students
                    maxScore = 200;

                    //based on currently existing code, it is easier to reopen the file for
                    //each grade range
                    for (int i = 0; i < gradeRanges.Length; i++)
                    {
                        double bound = gradeRanges[i];

                        //reload the file
                        LoadFile(fileToProcess);

                        //get grade data
                        vm.AttachGrades();

                        //filter based on grade data
                        vm.FilterByGrade(assignments[assignmentCounter], bound, maxScore);

                        //update scores for next grade boundary
                        maxScore = bound - 0.01;

                        //build markov transitions
                        vm.BuildDefaultMarkovStates();

                        //figure out sequence distribution for entire data set and for individual students
                        Dictionary <string, int> transitions = vm.GetAllTransitionCombinations(sequenceLength);

                        //interesting transitions are those in which we have at least 5 occurrances
                        var interestingTransitions = transitions.Where(t => t.Value > 5).OrderBy(t => t.Value).ToList();

                        //write this information to a file
                        CsvWriter writer = new CsvWriter();

                        //aggregate class results
                        Console.WriteLine("Processing transition sequences of length {0}...", sequenceLength);
                        foreach (KeyValuePair <string, int> kvp in interestingTransitions)
                        {
                            writer.AddToCurrentLine(kvp.Key);
                            writer.AddToCurrentLine(kvp.Value.ToString());
                            writer.CreateNewRow();
                        }
                        using (TextWriter tw = File.CreateText(string.Format("{0}/aggregate_{1}_{2}.csv", outputPath, sequenceLength, gradeMap[i])))
                        {
                            tw.Write(writer.ToString());
                        }

                        //individual students
                        //add header data
                        writer = new CsvWriter();
                        writer.AddToCurrentLine("UserId");
                        writer.AddToCurrentLine("Grade");
                        foreach (var kvp in interestingTransitions)
                        {
                            writer.AddToCurrentLine(kvp.Key);
                        }
                        writer.CreateNewRow();

                        foreach (var user in vm.Timeline.Values)
                        {
                            //first row for users is raw values
                            writer.AddToCurrentLine(user.OsbideId);
                            writer.AddToCurrentLine(gradeMap[i]);

                            //only use the interesting states as columns as identified in the aggregate analysis
                            foreach (KeyValuePair <string, int> kvp in interestingTransitions)
                            {
                                if (user.TransitionCounts.ContainsKey(kvp.Key) == true)
                                {
                                    writer.AddToCurrentLine(user.TransitionCounts[kvp.Key]);
                                }
                                else
                                {
                                    writer.AddToCurrentLine("0");
                                }
                            }
                            writer.CreateNewRow();

                            //2nd row contains normalized values
                            writer.AddToCurrentLine(user.OsbideId);
                            writer.AddToCurrentLine(gradeMap[i]);
                            int totalTransitions = user.TransitionCounts.Values.Sum();

                            //only use the interesting states as columns as identified in the aggregate analysis
                            foreach (KeyValuePair <string, int> kvp in interestingTransitions)
                            {
                                if (user.TransitionCounts.ContainsKey(kvp.Key) == true)
                                {
                                    writer.AddToCurrentLine(user.TransitionCounts[kvp.Key] / (double)totalTransitions);
                                }
                                else
                                {
                                    writer.AddToCurrentLine("0");
                                }
                            }
                            writer.CreateNewRow();
                        }
                        using (TextWriter tw = File.CreateText(string.Format("{0}/individual_{1}_{2}.csv", outputPath, sequenceLength, gradeMap[i])))
                        {
                            tw.Write(writer.ToString());
                        }
                    }
                }

                //move to the next assignment
                assignmentCounter++;
            }
        }