Efficient method to increase "code" by 1 - HtmlAgilityPack - c#

I'm working on an app that extracts content from a game page (example), displays it to the user in a textbox and if the user wishes to do so, he/she can save it as a .txt file or .xsl (excel spreadsheet format).
But the main problem I'm facing right now is that you have to manually change the code to "extract" data about another in-game unit.
If you open the link you'll see that I'm currently extracting the "Weapons", "Used", "Survived" and "Casualties" from the Defender side (as for now), but only 1 type of unit (more like only 1 row of that table) is being "extracted", I'm looking for a way to search "tr[1]/td[2]/span[1]" through "tr[45]/td[2]/span[1]" (even if the example page only goes until tr[16]), or maybe a way to automate it to search until it finds no data (nothing) then it would stop.
Sorry for any text mistakes, I'm not a native speaker
private void btnStart_Click(object sender, RoutedEventArgs e)
{
HtmlDocument brPage = new HtmlWeb().Load("http://us.desert-operations.com/world2/battleReport.php?code=f8d77b1328c8ce09ec398a78505fc465");
HtmlNodeCollection nodes = brPage.DocumentNode.SelectNodes("/html[1]/body[1]/div[1]/div[1]/div[3]/div[1]/div[1]/div[1]/div[2]/table[2]");
string result = "";
List<brContentSaver> ContentList = new List<brContentSaver>();
foreach (var item in nodes)
{
brContentSaver cL = new brContentSaver();
/* Here comes the junk handler, replaces all junk for nothing, essentially deleting it
I wish I knew a way to do this efficiently */
cL.Weapons = item.SelectSingleNode("tr[16]/td[1]").InnerText
.Replace(" * ", " ")
.Replace("&nbsp ; *&nbsp ;", " ");
cL.Used = item.SelectSingleNode("tr[16]/td[2]/span[1]").InnerText
.Replace(" * ", " ")
.Replace("&nbsp ; *&nbsp ;", " ");
cL.Survived = item.SelectSingleNode("tr[16]/td[3]").InnerText
.Replace(" * ", " ")
.Replace("&nbsp ; *&nbsp ;", " ");
if (cL.Survived == "0")
{
cL.Casualties = cL.Used;
} else
{
/* int Casualties = int.Parse(cL.Casualties);
* int Used = int.Parse(cL.Used);
* int Survived = int.Parse(cL.Survived);
* Casualties = Used - Survived; */
cL.Casualties = item.SelectSingleNode("tr[16]/td[4]").InnerText
.Replace(" * ", " ")
.Replace("&nbsp ; *&nbsp ;", " ");
}
ContentList.Add(cL);
}
foreach (var item in ContentList)
{
result += item.Weapons + " " + item.Used + " " + item.Survived + " " + item.Casualties + Environment.NewLine;
}
brContent.Text = result;
}
Sorry if this sounds silly, but I'm new to programming, especially in C#.
Edit 1: I noticed that "if (cL.Survived == "0")", I was just testing stuff some stuff way earlier and I forgot to change it, but hey, it works
Edit 2: If you are wondering I'm also using this:
public class brContentSaver
{
public string Weapons
{
get;
set;
}
public string Used
{
get;
set;
}
public string Survived
{
get;
set;
}
public string Casualties
{
get;
set;
}
}

I don't have much time to write this but hope it will help if you still need. I find Linq is more handy:
private static void Run()
{
HtmlDocument brPage = new HtmlWeb().Load("http://us.desert-operations.com/world2/battleReport.php?code=f8d77b1328c8ce09ec398a78505fc465");
var nodes = brPage.DocumentNode.Descendants("table").Where(_ => _.Attributes["class"] != null && _.Attributes["class"].Value != null && _.Attributes["class"].Value.Contains("battleReport"));
string result = "";
List<brContentSaver> ContentList = new List<brContentSaver>();
foreach (var item in nodes)
{
if (item.Descendants("th").Any(_ => _.InnerText.Equals("Weapons")))
{
//get all tr nodes except first one (header)
var trNodes = item.Descendants("tr").Skip(1);
foreach (var node in trNodes)
{
brContentSaver cL = new brContentSaver();
var tds = node.Descendants("td").ToArray();
/* Here comes the junk handler, replaces all junk for nothing, essentially deleting it
I wish I knew a way to do this efficiently */
cL.Weapons = tds[0].InnerText
.Replace(" * ", " ")
.Replace("&nbsp ; *&nbsp ;", " ");
cL.Used = tds[1].Descendants("span").FirstOrDefault()?.InnerText
.Replace(" * ", " ")
.Replace("&nbsp ; *&nbsp ;", " ");
if (string.IsNullOrEmpty(cL.Used))
{
cL.Used = tds[1].InnerText;
}
cL.Survived = tds[2].Descendants("span").FirstOrDefault()?.InnerText
.Replace(" * ", " ")
.Replace("&nbsp ; *&nbsp ;", " ");
if (string.IsNullOrEmpty(cL.Survived))
{
cL.Casualties = cL.Used;
}
else
{
/* int Casualties = int.Parse(cL.Casualties);
* int Used = int.Parse(cL.Used);
* int Survived = int.Parse(cL.Survived);
* Casualties = Used - Survived; */
cL.Casualties = tds[3].Descendants("span").FirstOrDefault()?.InnerText
.Replace(" * ", " ")
.Replace("&nbsp ; *&nbsp ;", " ");
if (string.IsNullOrEmpty(cL.Casualties))
{
cL.Casualties = tds[3].InnerText;
}
}
ContentList.Add(cL);
}
}
}
foreach (var item in ContentList)
{
result += item.Weapons + " " + item.Used + " " + item.Survived + " " + item.Casualties + Environment.NewLine;
}
var text = result;
}

Related

Implement User Log in a Centralized Way

I'm currently working on finding a way to Log UserActions/Requests. I'm inclined towards logging the details to a text file. The LOG details are organized in a tree-like (hierarchical) structure so that it is readable and shows method names in a step-by-step manner. (if a request went through several methods)
I have a sample app which works fine but it is not the way how it should be. Consider the following classes.
Node class which is the template to make a tree-like (hierarchical) structure. It has attributes such as name (method name), Time and a List type para named Children.
public class Node
{
public string Name; // method name
public DateTime Time; // time when accessed
public List<Node> Children;
public static void PrintTree(Node tree)
{
string temp = "";
List<Node> firstStack = new List<Node>();
firstStack.Add(tree);
List<List<Node>> childListStack = new List<List<Node>>();
childListStack.Add(firstStack);
while (childListStack.Count > 0)
{
List<Node> childStack = childListStack[childListStack.Count - 1];
if (childStack.Count == 0)
{
childListStack.RemoveAt(childListStack.Count - 1);
}
else
{
tree = childStack[0];
childStack.RemoveAt(0);
string indent = "";
for (int i = 0; i < childListStack.Count - 1; i++)
{
indent += (childListStack[i].Count > 0) ? "| " : " ";
}
temp = indent + "+- " + tree.Name + " (" + tree.Time + ")";
Console.WriteLine(indent + "+- " + tree.Name + " (" + tree.Time + ")");
File.AppendAllText(#"C:\Users\aimalkhan\Desktop\Log Work\Log.txt", temp + Environment.NewLine);
if (tree.Children != null)
{
if (tree.Children.Count > 0)
{
childListStack.Add(new List<Node>(tree.Children));
}
}
}
}
File.AppendAllText(#"C:\Users\aimalkhan\Desktop\Log Work\Log.txt", Environment.NewLine + "*************************************************************" + Environment.NewLine);
}
}
My sample Employee class with a sample method SetEmployeeName() having AN EXTRA parameter of NODE type for Logging purposes.
public class Employee
{
private string FirstName { get; set; }
private string LastName { get; set; }
public string SetEmployeeName(string firstName, string lastName, Node node)
{
node.Name = "Class Name: " +this.GetType().Name + ", Calling method Name: setEmployeeName()";
node.Time = DateTime.Now;
this.FirstName = firstName;
this.LastName = lastName;
return this.FirstName + " " + this.LastName;
}
public void CompleteTask(string empName, string taskName)
{
Console.WriteLine("Employee: " + empName + " is completing the task: " + taskName);
}
}
and finally this is how i'm using the aforementioned sample of Codes.
Node root = new Node();
root.Name = "ClassName: Main";
root.Time = DateTime.Now;
root.Children = new List<Node>();
Node child = new Node();
Employee emp = new Employee();
emp.SetEmployeeName("John", "D", child);
root.Children.Add(child);
Node.PrintTree(root);
This is how the output looks
Now my question is that it would really be a headache for me to pass a NODE type para every time i need a child info log. Could this be some how made centralized in any possible way? Is there a better way than this one? A little guidance would really be appreciated.

C# print list issue

I'm doing a WCF service with GUI as client, however I have a problem with printing list of current items added. I have a code to add new entries to the list:
public bool Add_Data(Data sample)
{
container.Add(sample);
Console.WriteLine("New record added!");
return true;
}
And it's working, however when I'm trying to view added records with first try it works, however if I want to view it again list is adding same element. To show you how it works:
I'm adding new entry and I "print" list:
IMAGE CLICK [works how it should]
However I want to see it again, so I'm pressing same button in my form, and here is what happens:IMAGE CLICK as you can see, we have our list + additional same record, if I will press button again, I will have 3 same records.
Here is my "show records" code:
public string Show_Data()
{
Console.WriteLine("Printing records");
foreach (Data record in container)
{
string final_result = ("\nID: "+ + record.ID + " " + "product: " + record.product + " " + "category: " + record.category + " " + "price: " + record.price + " " + "quantity: " + record.quantity + " " + "\n ");
result += final_result;
}
return result;
}
Let me know if you know how to solve it.
You need to look into variable scope. You have result declared outside of the Show_Data() method. Each time the method is called you are doing result += final_result; which is adding to result. Try the code below and you will get different results.
public string Show_Data()
{
Console.WriteLine("Printing records");
var output = string.Empty;
foreach (Data record in container)
{
string final_result = ("\nID: "+ + record.ID + " " + "product: " + record.product + " " + "category: " + record.category + " " + "price: " + record.price + " " + "quantity: " + record.quantity + " " + "\n ");
output += final_result;
}
return output;
}
Also, I would consider using a string builder and string format as well.
public string Show_Data()
{
Console.WriteLine("Printing records");
var output = new StringBuilder();
foreach (Data record in container)
{
string final_result = string.Format("ID: {0} product: {1} category: {2} price: {3} quantity: {4}", record.ID, record.product, record.category, record.price, record.quantity);
// if using C# 6
// string final_result = string.Format($"ID: {record.ID} product: {record.product} category: {record.category} price: {record.price} quantity: {record.quantity)}";
output.AppendLine(final_result);
}
return output.ToString();
}

C# How to check a Listbox for a string + object?

I'm trying to search for a certain number(object) in a listbox which comes together with a string in order to highlight it. In the following bit of code i override a ToString() method to contain all my objects.
public override string ToString()
{
string reservatiestring;
reservatiestring = "Kamer: " + roomNumber + "" + " Op datum: " + datum + " Aantal personen: " + personen.Count + " Naam: " + reservatienaam;
return reservatiestring;
}
Following this I add it to my listbox in the following bit of code:
listBox1.Items.Add(reservatie.ToString());
I now want to search for all the items in my listbox containing the same roomNumber object. To do this i tried the Contains() method with the text before it: "Kamer: " and the object which I'm looking for +comboBox1.SelectedItem. This however always fails and my code goes to the else option giving me the error message.
private void buttonSearch_Click(object sender, EventArgs e)
{
listBox1.SelectionMode = SelectionMode.MultiExtended;
Reservations reservaties = new Reservations();
reservaties.roomnumberstring = "Kamer: " + comboBox1.SelectedValue;
for (int i = listBox1.Items.Count - 1; i >= 0; i--)
{
if (listBox1.Items[i].ToString().ToLower().Contains(("Kamer: " + comboBox1.SelectedValue)))
{
listBox1.SetSelected(i, true);
}
else
{
MessageBox.Show("error");
}
Please note: All my roomNumber objects are stored in the combobox, so whenever i select for example roomNumber 3 in my combobox and hit search all the items in the listbox containing "Kamer: 3" should be selected.
The roomnumberstring is a option I tried which did not work unfortunately.
reservaties.roomnumberstring = "Kamer: " + comboBox1.SelectedValue;
Your override of the ToString method is wrong and won't modify anything. Try this :
public override string ToString(this string reservatiestring)
{
reservatiestring = "Kamer: " + roomNumber + "" + " Op datum: " + datum + " Aantal personen: " + personen.Count + " Naam: " + reservatienaam;
return reservatiestring;
}
I can see one thing that might make your code fail. you are comparing
.ToLower()
with "Kamer", where the "K" isnĀ“t in lowercase

Pass multiple data from one function to label in C#

I have a function that retrieves multiple lines of data and I want to display them in a label. My function is as shown below.
public static string GetItemByQuery(IAmazonSimpleDB simpleDBClient, string domainName)
{
SelectResponse response = simpleDBClient.Select(new SelectRequest()
{
SelectExpression = "Select * from " + domainName
});
String res = domainName + " has: ";
foreach (Item item in response.Items)
{
res = item.Name + ": ";
foreach (Amazon.SimpleDB.Model.Attribute attribute in item.Attributes)
{
res += "{" + attribute.Name + ", " + attribute.Value + "}, ";
}
res = res.Remove(res.Length - 2);
}
return res;
}
So far I can only return a string which is the last line of the retrieved data. How can I retrieve all the records? I tries arraylist, but it seems that the AWS web application doesn't allow me to use arraylist. Can anyone please help me to solve this??
Return it as as a Enumberable,
List<String> Results ;
Your method would be
public static List<String> GetItemByQuery(IAmazonSimpleDB simpleDBClient, string domainName)
{
List<String> Results = null;
SelectResponse response = simpleDBClient.Select(new SelectRequest()
{
SelectExpression = "Select * from " + domainName
});
String res = domainName + " has: ";
foreach (Item item in response.Items)
{
Results = new List<String>();
res = item.Name + ": ";
foreach (Amazon.SimpleDB.Model.Attribute attribute in item.Attributes)
{
res += "{" + attribute.Name + ", " + attribute.Value + "}, ";
}
res = res.Remove(res.Length - 2);
Results.Add(res);
}
return Results;
}

How to remove missing tags from HTML source in Asp.net

Sometime it has been seen that the HTML source that we receive from some website are not having proper tag ending, and that affect our UI.
So, like
<br /><p>hello the para start here </p> <p>some text and no ending tag
And there is no ending tag .
I want to retain the HTML format and want this like
<br /><p>hello the para start here </p> some text and no ending tag
One more thing is that sometimes we get the end tag at the start that should also be resolved by the algorithm.
Hey guys I thought for a long time and finally I have code for my problem, i am posting it here so that other can get benefit from this ....
public static string RemoveIncompleteTags(string source, string tag)
{
source = source.Replace(" ", " ");
source = source.Replace("/n", string.Empty).Replace("/r", string.Empty).Replace("/t", string.Empty);
source = source.Replace("<" + tag + "></" + tag + ">", string.Empty);
source = source.Replace("<" + tag + "> </" + tag + ">", string.Empty);
source = source.Replace("<" + tag + "> </" + tag + ">", string.Empty);
Dictionary<int, string> oDict = new Dictionary<int, string>();
string[] souceList;
Dictionary<int, string> final = new Dictionary<int, string>();
bool opening = false;
bool operate = false;
source = source.Replace(" ", " ");
source = source.Replace(">", "> ").Replace("<", " <");
source = source.Replace(" >", ">").Replace("< ", "<");
source = source.Replace(" ", " ").Replace(" ", " ");
souceList = source.Split(' ');
for (int i = 0; i < souceList.Length; i++)
{
string word = souceList[i];
if (word.ToLower() == "<" + tag.ToLower() + ">")
{
opening = true;
operate = true;
}
else if (word.ToLower() == "</" + tag.ToLower() + ">")
{
opening = false;
operate = true;
}
if (operate)
{
if (opening)
{
oDict.Add(i, word);
final.Add(i, word);
}
else
{
if (oDict.Count != 0)
{
oDict.Remove(oDict.Last().Key);//.ToList().RemoveAt(oDict.Count - 1);
final.Add(i, word);
}
else
{
// need not to add to the output string
// code if you want to log
}
}
operate = false;
opening = false;
}
else
{
final.Add(i, word);
}
}
if (final.Count > 0)
{
if (oDict.Count > 0)
{
foreach (var key in oDict.Keys)
{
final.Remove(key);
}
}
StringBuilder fText = new StringBuilder();
final.ToList().ForEach(wd =>
{
if (wd.Value.Trim().Length > 0)
fText.Append(wd.Value.Trim() + " ");
});
return fText.ToString().Trim();
}
else
{
return string.Empty;
}
}
thanks...

Categories

Resources