Managing Records from XML File – One Punch Man Training App

Managing Records from Xml File
Share this to your friends

Remember we created an instance of our Progress class in our XmlLoader Load method and filled it with the data we gathered from our xml saved file. To be able to get that object we added a getter and setter accessor in the end of our XmlLoader class. We put the code below in our XmlLoader.cs file.


public Progress GetProgress {
	get {
       	return progress;
       } set {
       	this.progress = value;
       }
}

To access it in our Subject Form. We simply added this code in our SubjectForm.cs file, inside our BeginLoadingSavedData method:


Progress progress = loader.GetProgress;

In the code above we created a new Progress class object. We also assign the data we retrieved from our getter and setter accessors as its value. By creating a copy of that object in our SubjectForm, all the controls in our form has gained access to that object. We can now display record, but before that we would just perform basic null checks in our newly created object and its properties.

Checking for Null Value


if(progress == null) {
	MessageBox.Show("Problem retrieving progress data.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
       return;
}

This is just a simple check to see if our Progress object isn’t empty. You can also individually check its properties if you like. Remember in our Progress class we added four properties.

  • StartOfTime
  • Days
  • Rank
  • GetExercises

Gather Data for Date and Time Check

We expect our xml file to have its record everyday. From the day the app started(also the creation of the xml saved file) it must have the same record(exercises data) count as the current progress day. To make it simple we won’t allow the app to continue if our progress day is not equal to our exercises record count.
For this simple check we gathered:


DateTime startOfTime = progress.StartOfTime;
List currentExercises = progress.GetExercises;
int lastTrainingDay = currentExercises.Count;

The startOfTime logs the date and time the app started/created the first record in the xml file. Variable lastTrainingDay obviously assigns the total number of exercise records as its value. We assume that its total count would also be the last training day of the subject. We will used this data to check for inconsistencies in our xml file records.

For this tutorial every time we detect inconsistencies in the record we will just recreate the xml file, completely restarting the progress again. Just to make things simpler.

Update or Delete Invalid Xml File


while(progress.Days != lastTrainingDay) {
	if (progress.Days > lastTrainingDay) {
	       MessageBox.Show("Saved file is out of date.\nUpdating and restarting application...", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
       	loader.Edit(SavedDataPath, progress.Days, lastTrainingDay);
	}

       if (progress.Days < lastTrainingDay) {
	       MessageBox.Show("Invalid saved file.\nDeleting and restarting application...", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
       	loader.Delete(SavedDataPath);
	}
       BeginLoadingSavedData();
}

The code above updates or delete xml saved file. The Progress.Days(see Progress class to check the property) gets the total number of days when the app started unto the current date and time. It simply updates the file if the record is less than the progress day. This happens when the user forgot to add a record. The loader.Edit method will simply record exercises with empty or 0 repetitions.
In cases the record is more that the days count, which happens when the xml file is modified simply by adding exercises records manually. The loader.Delete method is triggered.
The same BeginloadingSavedData method is called to recheck and refresh changes in xml file.

Note: We still don't have the loader.Edit and loader.Delete method in our XmlLoader class. We will ignore this for now by simply commenting the call for the method. Comment loader.Edit and loader.Delete in the code you added.

Gather Remaining Data from the Object

We will just retrieve all the data needed by the form. After getting the record for our date and time check. Let's retrieve the subject's ranking status.


Person person = new Person() {
	GetRank = progress.Rank
};

This copies the data gathered from the xml file to a newly created object of the Person class. The Person class is a separate class created to store and calculate data specific to the subject's ranking. We'll also add the simple code below to check for empty value.


if (person == null) {
	MessageBox.Show("Problem retrieving subject's data.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
       return;
}

Also a separate Exercises class is created to handle all record concerning exercises. To retrieve all exercises record we created a new object of Exercises and fill the new list with the exercises we collected:


Exercises exercises = new Exercises();
exercises.ListExercises(currentExercises);
var currentExercisesList = exercises.GetExercises;
currentExercisesList.Reverse();
if (currentExercisesList == null) {
	MessageBox.Show("Problem retrieving exercise's record.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
       return;
}

The Person Class

This class only holds properties that are specific to rank and its calculation. I decided to combine Rank and Person class for simplicity(or I might be approaching object oriented programming the wrong way). Updates to this code will be posted to a separate article. For now let's stick to this one. This is also not the complete Person class code.


using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;

namespace OnePunchManTrainingAppLibrary {
	public class Person : IHero {
		private string rank;

		enum Rank {
			Normal,
            		C,
            		B,
            		A,
            		S,
            		SS,
            		SSS
        	};
	
		public void CalculateRanking(List exercises) {}

        	public string GetRank {
			get { return rank; }
            		set { rank = value;}
		}
	}
}

Notice that in our Person class, we implements the IHero interface.


public class Person : IHero { }

We won't discussed interfaces in this tutorial. But because we implement IHero in our class, we also need to define all IHero method in our Person class. Let's see the code of our IHero interface.

The IHero Interface


namespace OnePunchManTrainingAppLibrary {
    public interface Ihero {
        void CalculateRanking(List exercises);        
    }
}

Notice the CalculateRanking method that accept a parameter List, we need to define that method in our Person class. And you can see that under the enum declaration in our class.

The Exercises Class


namespace OnePunchManTrainingAppLibrary {
    public class Exercises {
        private List exercises = new List();
        public string Name { get; set; }
        public double Repetition { get; set; }

        public Exercises() {
            
        }

        public void ListExercises(List progress) {
            if (progress == null) {
                MessageBox.Show("Problem retrieving records.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }

            try {
                var lastIndexOfProgress = progress.Last();
                foreach(var e in lastIndexOfProgress.Descendants()) {
                    if (e.Name == "PushUps") {
                        TotalPushUps = int.Parse(e.Value);
                    }
                    if (e.Name == "Squats") {
                        TotalSquats = int.Parse(e.Value);
                    }
                    if (e.Name == "Jog") {
                        TotalJog = int.Parse(e.Value);
                    }
                    if (e.Name == "SitUps") {
                        TotalSitUps = int.Parse(e.Value);
                    }
                }

                foreach (var e in progress.Descendants()) {
                    int repetition = 0;
                    repetition = progress.Descendants(e.Name).Select(x => int.Parse(x.Value)).Sum();                    
                    double repetitionPercentage = GetPercentage(e.Name.ToString(), repetition);

                    if (!exercises.Any(x => x.Name == e.Name)) {
                        exercises.Add(new Exercises() { Name = e.Name.ToString(), Repetition = repetitionPercentage });
                    }
                }

                foreach (var e in exercises) {
                    switch (e.Name) {
                        case "PushUps":
                            e.Name = "Upper Strength";
                            break;
                        case "Squats":
                            e.Name = "Lower Strength";
                            break;
                        case "Jog":
                            e.Name = "Stamina";
                            break;
                        case "SitUps":
                            e.Name = "Durability";
                            break;
                    }
                }
            } catch(Exception e) {
                MessageBox.Show(e.Message);
            }
        }
	private Double GetPercentage(string name, int value) {
		int max = 0;
            	switch (name) {
			case "Jog":
                    		max = MAXIMUM_KILOMETER;
                    	break;
                	default:
                    		max = MAXIMUM_REPETITION;
                    	break;
            	}
            	max *= TOTAL_TRAINING_DAY;

	       return value > 0 ? Convert.ToDouble(100 / (max / value)) : 0;
	}

        public List GetExercises {
            get { return exercises; }
            set
            {
                exercises = value;
            }
        }

        public int TotalPushUps { get; set; }
        public int TotalSquats { get; set; }
        public int TotalJog { get; set; }
        public int TotalSitUps { get; set; }
    }
}

The Exercises class consist of two important method, the ListExercises and the GetExercises method. The ListExercises method accepts the parameter List progress. Below are samples of what this method is accepting.


<Progress Day="1">
    <PushUps>100</PushUps>
    <SitUps>100</SitUps>
    <Squats>100</Squats>
    <Jog>10</Jog>
</Progress>
<Progress Day="2">
    <PushUps>0</PushUps>
    <SitUps>0</SitUps>
    <Squats>0</Squats>
    <Jog>0</Jog>
</Progress>

The ListExercises method simply separates all element in the list. It gets all the total repetitions of each exercise. In our example above the total repetition of exercise PushUps from Day 1 to the current Day 2 is 100. After getting the total repetition of an exercise, the method pass it to another method to convert it to percentage. That is the GetPercentage method in the Exercises class.

An accessor(GetExercises) is also added so that we can retrieve this modified data back to our Subject form.

Explanations of each method will be explain later in the series. We will just explore each classes to see what method is being called when we want to display data in the form.

That's it for now. Please read Adding Chart - One Punch Man Training App if you haven't read it.

Leave a Reply