Wednesday, March 23, 2016

Working with Microsoft Excel using C#

Library

To programmatically access MS Excel, you will need Microsoft Excel Extension library usually comes with Visual Studio: Microsoft.Office.Interop.Excel. I usually include it in header like this:

    using Excel = Microsoft.Office.Interop.Excel;

Open an Excel file
 
You can code like this:
  
  var app = new Excel.Application();
  var book = app.Workbooks.Open(fileName, 0, true, 5, "", "", true, Microsoft.Office.Interop.Excel.XlPlatform.xlWindows, "\t", false, false, 0, true, 1, 0);

Or simply like this:

     var book = app.Workbooks.Open(@"c:\test\data.xlsx");

Access a sheet, Range and cell

To access a sheet, say the first sheet (please note sheet index is 1-based rather than 0-based):

  var sheets = book.Worksheets as Excel.Sheets;
  var sheet = (Excel.Worksheet)sheets.get_Item(1);

To reach to a cell, you will do it through excel object Range, like this:

  var range = sheet.UsedRange;

Or for multiple cells:

  var range  = (Excel.Range) sheet.get_Range(sheet.Cells[1, 1], sheet.Cells[3,3]);

Or for single cell:

  var range = (Excel.Range) sheet.Cells[1, 1];

You got it, they are all 1-based. To access a cell:

 var range = sheet.UsedRange;
 for (int i = 1; i <= range.Rows.Count; i++)
 {
    for(int j=1; j <= range.Columns.Count; j++)
    var s = (string) (range.Cells[i, j] as Excel.Range).Value2;
               // ProcessString(s);
 }

Clean up


Make sure to clean up after use. Here's how and don't forget to add try catch around it.


 book.Close(false, Type.Missing, Type.Missing);
 System.Runtime.InteropServices.Marshal.FinalReleaseComObject(book);
 System.Runtime.InteropServices.Marshal.FinalReleaseComObject(app);

Wednesday, March 16, 2016

Setup CocoaPods for Swift project

Cocoa Pods provides a standard way to manage dependent libraries. It only takes a few steps to set it up in a Swift project:
  • Assume you've already have a normal Xcode project
  • Open terminal window, $ cd to your project directory
  • Create a Podfile by runing $ pod init
  • Edit the Podfile to add any liberties you want to install within certain target
  • Make sure to uncomment use_frameworks if it's a Swift project
  • Run $ pod install
  • An xcworkspace project should be created, please use it to load your project moving forwards

Friday, March 11, 2016

Define Google Analytics Custom Dimension

Google Analytics' Custom Dimension feature provides a powerful way to combine custom data with  Analytics data. Here are a few steps to do it:

Create a GA account
  • Go to Admin UI, click ACCOUNT dropdown, select 'Create new account
  • Fill in account create form and click 'Get Tracking ID' 
Create environment specify property
  • Under PROPERTY section, select 'Create new property'
  • Fill in New Property form. You can use App Name for specific environment, like Staging, Dev etc.
  • Click 'Get Tracking ID' for specified environment
Sample code to track traffic:

<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');

ga('create', 'UA-74922222-4', 'auto');
ga('send', 'pageview');
</script>


Create Custom Dimension
  • In Admin UI, click 'Custom Definitions' under PROPERTY section
  • Select 'Custom Dimensions', '+New Custom Dimension' button
  • Give a name and specify scope, check active and hit 'Create'
  • Follow the example codes to collect custom data
Sample code using javascript:

var dimensionValue = 'SOME_DIMENSION_VALUE';
ga('set', 'dimension1', dimensionValue);

View Result

  • In Report UI, select Audience>Custom>User Defined
  • From 'Secondary dimension' dropdown, select Custom Dimensions and your dimension name
  • You should be able to see traffics group by each value of your custom dimension.

Friday, March 4, 2016

Steps to upload iOS app to Apple Store


  • In XCode project General tab, make necessary updates to Version and Build number
  • Set Build Only Device to Generic iOS Device
  • Run Product>Archive
  • Upon successful build and archive, click 'Upload to App Store' button
  • Chose proper team for the submission
  • Click 'Upload' to send the app to Apple
  • Wait... until you get upload completed confirmation.
New build won't be available for TestFlight right away. To check status, login to iTunes Connect. Select your app>TestFight tab. Check iOS under TESTFLIGHT BUILDS section, click 'View All Builds' like, you may see your newly updated build is still in processing.

Tuesday, February 23, 2016

Git commands: get older version, set user email

From time to time there may be a need to play with a snapshot of an early version of code. Some Git/GitHub UI tool can help to do this, but it also just takes a few lines of command to do so. I use Mac as an example:

Check out older snapshot:
- Find the last commit you want to go back to. Copy the commit Id. It usually looks like this: 9d4f59ecf2eef3b8a46c5ba4a47a4efc27bf57b4
-  Open Terminal, change to the directory where you code base were checked out.
-  Enter: git checkout {Commit Id}
- You may see message like this:
You are in 'detached HEAD' state. You can look around, make experimental changes and commit them, and you can discard any commits you make in this state without impacting any branches by performing another checkout.

Return to latest version of master branch:
-  Enter: git checkout master

Setup user email for local Git repository:
- Enter: git config --global user.email "{user email}"

To verify user email:
- Enter: git config --global user.email

Monday, February 23, 2015

How to generate report with data from different databases

From time to time we need to generate reports referencing data from other database. In SSRS, One simple way is to utilize its Lookup function.  

All you need to do is create multiple datasets pointing to different databases (data sources). Then use Lookup function following this syntax:
       Lookup (src_expression, target_expression, target_result_expression, target_dataset)

For example, you have application user dataset, say AppUsers(Uid) and membership dataset, say MembershipUsers(Mid, Name). To lookup application user name from membership, the expression will be something like this:
    Lookup(Field!Uid.Value, Field!Mid.Value, Field!Name, “MembershipUsers”)

Notice, Lookup function is good for 1-to-1 source/target field value match. To handle 1-to-many matches, use LookupSet instead. 

Tuesday, November 4, 2014

Simple way to compress Web API Response

There are many ways to do Web API Response compression. I particularly like Radenko Zec's solution mostly because it's simple and efficient.

A few steps I did in my project:
  •  Pick your favorite compression tool. I used DotNetZip as Radenko recommended. It gives me close to 85% compression rate for JSON. A CompressionHelper is used as well so that I can easily change to a different compression tool if I want.
  • Implement DeflateCompressionAttribute. Mostly Radenko's code:
    public class DeflateCompressionAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuted(HttpActionExecutedContext actContext)
        {
            var content = actContext.Response.Content;
            var bytes = content == null ? null : content.ReadAsByteArrayAsync().Result;
            var compressedContent = bytes == null ? new byte[0] : CompressionHelper.Compress(bytes);
            actContext.Response.Content = new ByteArrayContent(compressedContent);
            actContext.Response.Content.Headers.Remove("Content-Type");
            actContext.Response.Content.Headers.Add("Content-encoding", "deflate");
            actContext.Response.Content.Headers.Add("Content-Type", "application/json");
            base.OnActionExecuted(actContext);
        }
    }
  • Add [DeflateCompression] attribute to any WebAPI method needs compression
  • Decompress using standard HttpClientHandler. As our Web API client is expecting objects, we configure HttpClientHandler to decompress response:
            using (var client = new HttpClient(new HttpClientHandler(){AutomaticDecompression = DecompressionMethods.Deflate|DecompressionMethods.GZip}))
            {
                var para = new ServiceParams();
                client.BaseAddress = new Uri(_serviceUrl);
                client.DefaultRequestHeaders.AcceptEncoding.Add(new StringWithQualityHeaderValue("gzip"));
                client.DefaultRequestHeaders.AcceptEncoding.Add(new StringWithQualityHeaderValue("deflate"));
                HttpResponseMessage response =
                    client.PostAsync(_servicePath, para, new JsonMediaTypeFormatter()).Result;
                r = response.Content.ReadAsAsync<ServiceResponse>().Result;
            }