Wednesday 17 April 2013

The easy way to make reports (part 2)

Previously

Previously, when presented with a heap of data like this:

image

with a little html coding I turned it into this:

image

So the question is how?

How NOT to do it?

My first attempt had heaps of HTML code scattered inside my program. It worked, but it was so messy to try to maintain. Every time I wanted to change something, I needed to edit syntax, recompile & test.

Second attempt, was reading the C# report generation libraries. <YUK!>

Third attempt, was looking into XSLT and XML transforms. <Bigger YUK!>

Generally I found that I needed to learn a whole new library/language just to write a little HTML.
I needed a simpler solution.

 

Which was?

Lets look at some simple HTML, something like this

<html>
<body>
<h1>Name: [add name here]</h1>
etc,etc,etc
</body>
</html>



Simple enough right?
So we save this HTML off as our ‘template’, then the main code looks like:



  1. Load the template in
  2. Edit the template with a set of copy-paste actions
  3. Save the completed report

Quite straight forward right?


Show me the code!


Ok:



void makeReport()
{
  StringBuilder buffer = new StringBuilder();
  // read the template file
  buffer.append(File.ReadAllText("template/report.html"));
  // make changes
  buffer.replace("[add name here]", "mark gossage");
  // save the final copy
  File.WriteAllText("result/report.html",buffer.ToString()) ;
}



That’s it, kind of trivial isn’t it? 4 lines of code. But it works!!!!
I coded this example in C#, but you could have used python, perl, C++ or any other language you are familiar with.


What about a more complex example?


Life does get a bit more complex when you want a multiline table, but its still not that hard.
Starting with an html template like this:



<html>
<body>
<h1>Some Table Data</h1>
<table>
<tr><td>Name</td><td>Date</td></tr>
<!-- begin row-->
<tr>
  <td>[add name here]</td>
  <td>[add data here]</td>
</tr>
<!-- end row-->
</table>
</body>
</html>



You can do the same thing, you just need to duplicate the data between the ‘begin row’ and ‘end row’ as many times as needed.
The code looks generally like this:



void makeReport()
{
  // assuming buffer holds the template
  // find the section
  string data = buffer.ToString();
  int startIdx = data.IndexOf("<!-- begin row-->");
  int endIdx = data.IndexOf("<!-- end row-->");
  startIdx+=start.Length; // startIdx is at end of the 'start' string
  // get the 'row'
  string row=data.Substring(startIdx, endIdx - startIdx);            
  // temp buffer for working with
  StringBuilder temp=new StringBuilder();
  // for each item needed
  for(...)
  {
    temp.append(row); // add the row in
    // make changes
    temp.replace("[add name here]", ...);
    temp.replace("[add date here]",...);
  }
  // replace the 'row' with the data
  buffer.replace(row,temp.ToString());
  // save the stuff
}

This works perfectly, but is a little untidy, as it leaves the ‘begin row’, ‘end row’ tags in the file. But its only a couple of lines of code to fix that issue.


 


Conclusion


Another short & simple post.
No complex frameworks, no clever use of the language.
Just some simple common sense & problem solving.
You could write some kind of helper functions/class to do this job for you (I did).
Now my reports just need a little work in some HTML and a lot of search-replace.


Happy Coding,


Mark

No comments:

Post a Comment