Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Added actual library

  • Loading branch information...
commit eacdd4c8d5298abef86ade987fcc1c71b8a0ecfc 1 parent 491607c
@alexandernyquist authored
Showing with 984 additions and 0 deletions.
  1. +42 −0 AnalyticsApi.sln
  2. BIN  AnalyticsApi.suo
  3. +69 −0 AnalyticsApi/AnalyticsApi.csproj
  4. +6 −0 AnalyticsApi/AnalyticsApi.csproj.user
  5. +38 −0 AnalyticsApi/AnalyticsDataProvider.cs
  6. +239 −0 AnalyticsApi/AnalyticsService.cs
  7. +42 −0 AnalyticsApi/ApiMap.cs
  8. +8 −0 AnalyticsApi/ElementLevel.cs
  9. +13 −0 AnalyticsApi/Exceptions/InvalidCredentialsException.cs
  10. +20 −0 AnalyticsApi/Extensions/EnumerableExtensions.cs
  11. +104 −0 AnalyticsApi/HttpWrapper.cs
  12. +23 −0 AnalyticsApi/Interfaces/IAnalyticsDataProvider.cs
  13. +10 −0 AnalyticsApi/Interfaces/IHttpWrapper.cs
  14. +19 −0 AnalyticsApi/Maps/AnalyticsAccountRequest.cs
  15. +73 −0 AnalyticsApi/Maps/DashboardRequest.cs
  16. +15 −0 AnalyticsApi/Maps/GoalCompletionsRequest.cs
  17. +7 −0 AnalyticsApi/Maps/GoalRequest.cs
  18. +111 −0 AnalyticsApi/Parsers/AnalyticsGoalParser.cs
  19. +94 −0 AnalyticsApi/Parsers/AnalyticsXmlParser.cs
  20. +36 −0 AnalyticsApi/Properties/AssemblyInfo.cs
  21. BIN  AnalyticsApi/bin/Debug/AnalyticsApi.dll
  22. BIN  AnalyticsApi/bin/Debug/AnalyticsApi.pdb
  23. BIN  AnalyticsApi/bin/Release/AnalyticsApi.dll
  24. BIN  AnalyticsApi/bin/Release/AnalyticsApi.pdb
  25. +10 −0 AnalyticsApi/obj/Debug/AnalyticsApi.csproj.FileListAbsolute.txt
  26. BIN  AnalyticsApi/obj/Debug/AnalyticsApi.dll
  27. BIN  AnalyticsApi/obj/Debug/AnalyticsApi.pdb
  28. BIN  AnalyticsApi/obj/Debug/DesignTimeResolveAssemblyReferencesInput.cache
  29. +5 −0 AnalyticsApi/obj/Release/AnalyticsApi.csproj.FileListAbsolute.txt
  30. BIN  AnalyticsApi/obj/Release/AnalyticsApi.dll
  31. BIN  AnalyticsApi/obj/Release/AnalyticsApi.pdb
  32. BIN  AnalyticsApi/obj/Release/DesignTimeResolveAssemblyReferencesInput.cache
View
42 AnalyticsApi.sln
@@ -0,0 +1,42 @@
+
+Microsoft Visual Studio Solution File, Format Version 11.00
+# Visual Studio 2010
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AnalyticsApi", "AnalyticsApi\AnalyticsApi.csproj", "{FCA9EC50-0470-4993-A68A-FB8AFC07EE9C}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AnalyticsApi.Test", "AnalyticsApi.Test\AnalyticsApi.Test.csproj", "{2EF90DA4-8A44-4371-BA34-357F3643FE09}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Debug|Mixed Platforms = Debug|Mixed Platforms
+ Debug|x86 = Debug|x86
+ Release|Any CPU = Release|Any CPU
+ Release|Mixed Platforms = Release|Mixed Platforms
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {FCA9EC50-0470-4993-A68A-FB8AFC07EE9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {FCA9EC50-0470-4993-A68A-FB8AFC07EE9C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {FCA9EC50-0470-4993-A68A-FB8AFC07EE9C}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {FCA9EC50-0470-4993-A68A-FB8AFC07EE9C}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {FCA9EC50-0470-4993-A68A-FB8AFC07EE9C}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {FCA9EC50-0470-4993-A68A-FB8AFC07EE9C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {FCA9EC50-0470-4993-A68A-FB8AFC07EE9C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {FCA9EC50-0470-4993-A68A-FB8AFC07EE9C}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {FCA9EC50-0470-4993-A68A-FB8AFC07EE9C}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {FCA9EC50-0470-4993-A68A-FB8AFC07EE9C}.Release|x86.ActiveCfg = Release|Any CPU
+ {2EF90DA4-8A44-4371-BA34-357F3643FE09}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2EF90DA4-8A44-4371-BA34-357F3643FE09}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2EF90DA4-8A44-4371-BA34-357F3643FE09}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {2EF90DA4-8A44-4371-BA34-357F3643FE09}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {2EF90DA4-8A44-4371-BA34-357F3643FE09}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {2EF90DA4-8A44-4371-BA34-357F3643FE09}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2EF90DA4-8A44-4371-BA34-357F3643FE09}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2EF90DA4-8A44-4371-BA34-357F3643FE09}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {2EF90DA4-8A44-4371-BA34-357F3643FE09}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {2EF90DA4-8A44-4371-BA34-357F3643FE09}.Release|x86.ActiveCfg = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
View
BIN  AnalyticsApi.suo
Binary file not shown
View
69 AnalyticsApi/AnalyticsApi.csproj
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProductVersion>8.0.30703</ProductVersion>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{FCA9EC50-0470-4993-A68A-FB8AFC07EE9C}</ProjectGuid>
+ <OutputType>Library</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>AnalyticsApi</RootNamespace>
+ <AssemblyName>AnalyticsApi</AssemblyName>
+ <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ <TargetFrameworkProfile />
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="System" />
+ <Reference Include="System.Core" />
+ <Reference Include="System.Xml.Linq" />
+ <Reference Include="System.Data.DataSetExtensions" />
+ <Reference Include="Microsoft.CSharp" />
+ <Reference Include="System.Data" />
+ <Reference Include="System.Xml" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="ElementLevel.cs" />
+ <Compile Include="Exceptions\InvalidCredentialsException.cs" />
+ <Compile Include="Maps\DashboardRequest.cs" />
+ <Compile Include="Maps\AnalyticsAccountRequest.cs" />
+ <Compile Include="AnalyticsDataProvider.cs" />
+ <Compile Include="AnalyticsService.cs" />
+ <Compile Include="ApiMap.cs" />
+ <Compile Include="Extensions\EnumerableExtensions.cs" />
+ <Compile Include="HttpWrapper.cs" />
+ <Compile Include="Interfaces\IAnalyticsDataProvider.cs" />
+ <Compile Include="Interfaces\IHttpWrapper.cs" />
+ <Compile Include="Maps\GoalCompletionsRequest.cs" />
+ <Compile Include="Maps\GoalRequest.cs" />
+ <Compile Include="Parsers\AnalyticsGoalParser.cs" />
+ <Compile Include="Parsers\AnalyticsXmlParser.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ </ItemGroup>
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Microsoft.Common.targets.
+ <Target Name="BeforeBuild">
+ </Target>
+ <Target Name="AfterBuild">
+ </Target>
+ -->
+</Project>
View
6 AnalyticsApi/AnalyticsApi.csproj.user
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <ProjectView>ProjectFiles</ProjectView>
+ </PropertyGroup>
+</Project>
View
38 AnalyticsApi/AnalyticsDataProvider.cs
@@ -0,0 +1,38 @@
+using System.Collections.Generic;
+
+namespace AnalyticsApi
+{
+ public class AnalyticsDataProvider : IAnalyticsDataProvider
+ {
+ private readonly string _applicationName;
+ private readonly IHttpWrapper _httpWrapper;
+
+ public AnalyticsDataProvider(string applicationName, IHttpWrapper httpWrapper)
+ {
+ _applicationName = applicationName;
+ _httpWrapper = httpWrapper;
+ }
+
+ public string RequestLoginToken(string username, string password)
+ {
+ var parameters = new Dictionary<string, string>
+ {
+ {"Email", username},
+ {"Passwd", password},
+ {"accountType", "GOOGLE"},
+ {"service", "analytics"},
+ {"source", _applicationName}
+ };
+
+ var response = _httpWrapper.Post("https://www.google.com/accounts/ClientLogin", parameters);
+
+ var token = response.Remove(0, response.IndexOf("Auth=")).Replace("Auth=", string.Empty);
+ return token;
+ }
+
+ public string SendRequest(string token, string resource)
+ {
+ return _httpWrapper.Get(resource, new Dictionary<string, string> { { "Authorization", "GoogleLogin auth=" + token } });
+ }
+ }
+}
View
239 AnalyticsApi/AnalyticsService.cs
@@ -0,0 +1,239 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+
+namespace AnalyticsApi
+{
+ public class AnalyticsService
+ {
+ private readonly IAnalyticsDataProvider _dataProvider;
+
+ protected string _username;
+ protected string _password;
+ protected int? _profile;
+ protected string _token;
+ private int? _account;
+
+ public AnalyticsService(IAnalyticsDataProvider dataProvider)
+ {
+ _dataProvider = dataProvider;
+ }
+
+ /// <summary>
+ /// Check if we have a token or not
+ /// </summary>
+ public bool IsLogedIn
+ {
+ get { return _token != string.Empty; }
+ }
+
+ /// <summary>
+ /// Set username for current context
+ /// </summary>
+ /// <param name="username"></param>
+ /// <returns></returns>
+ public AnalyticsService Username(string username)
+ {
+ _username = username;
+ return this;
+ }
+
+ /// <summary>
+ /// Set password for current context
+ /// </summary>
+ /// <param name="password"></param>
+ /// <returns></returns>
+ public AnalyticsService Password(string password)
+ {
+ _password = password;
+ return this;
+ }
+
+ /// <summary>
+ /// Set profile id for current context
+ /// </summary>
+ /// <param name="id"></param>
+ /// <returns></returns>
+ public AnalyticsService Profile(int id)
+ {
+ _profile = id;
+ return this;
+ }
+
+ /// <summary>
+ /// Set account id for current context
+ /// </summary>
+ /// <param name="accountId"></param>
+ public AnalyticsService Account(int accountId)
+ {
+ _account = accountId;
+ return this;
+ }
+
+ /// <summary>
+ /// Get current username
+ /// </summary>
+ /// <returns></returns>
+ public string GetUsername()
+ {
+ return _username;
+ }
+
+ /// <summary>
+ /// Get current password
+ /// </summary>
+ /// <returns></returns>
+ public string GetPassword()
+ {
+ return _password;
+ }
+
+ /// <summary>
+ /// Get current profile id
+ /// </summary>
+ /// <returns></returns>
+ public int? GetProfile()
+ {
+ return _profile;
+ }
+
+ /// <summary>
+ /// Get current token
+ /// </summary>
+ /// <returns></returns>
+ public string GetToken()
+ {
+ return _token;
+ }
+
+ /// <summary>
+ /// Login to service
+ /// </summary>
+ public AnalyticsService Logon()
+ {
+ // Do not login if we have a token
+ if (string.IsNullOrEmpty(_token) == false) return this;
+
+ _token = _dataProvider.RequestLoginToken(_username, _password);
+ return this;
+ }
+
+ /// <summary>
+ /// Get all accounts for a specific user
+ /// </summary>
+ /// <returns></returns>
+ public IEnumerable<AnalyticsAccountRequest> GetAccounts()
+ {
+ if (string.IsNullOrEmpty(_token))
+ throw new Exception("No token found. Did you forget to login?");
+
+ var data = _dataProvider.SendRequest(_token, "https://www.google.com/analytics/feeds/accounts/default");
+ var parser = new AnalyticsXmlParser();
+ return parser.Parse(data, new AnalyticsAccountApiMap());
+ }
+
+ /// <summary>
+ /// Get dashboard summary for a given profile
+ /// </summary>
+ /// <returns></returns>
+ public DashboardRequest GetDashboard(string start, string end)
+ {
+ if (string.IsNullOrEmpty(_token))
+ throw new Exception("No token found. Did you forget to login?");
+
+ if(_profile == null)
+ throw new Exception("No profile set. Use .Profile() to set.");
+
+ var data = _dataProvider.SendRequest(_token, "https://www.google.com/analytics/feeds/data?ids=ga%3A" + _profile.Value + "&metrics=ga%3Avisits,ga%3Apageviews,ga%3Abounces,ga%3Aentrances,ga%3AtimeOnSite,ga%3AnewVisits&start-date=" + start + "&end-date=" + end + "&max-results=50");
+
+ var parser = new AnalyticsXmlParser();
+ return parser.Parse(data, new DashboardApiMap()).First();
+ }
+
+ public IEnumerable<Goal> GetGoals()
+ {
+ var data = _dataProvider.SendRequest(_token, "https://www.google.com/analytics/feeds/datasources/ga/accounts/~all/webproperties/~all/profiles/~all/goals");
+ Debug.WriteLine(data);
+
+ var parser = new AnalyticsGoalParser();
+ var goals = parser.ParseGoals(data);
+
+ return goals;
+ }
+
+ public IEnumerable<Goal> GetGoalCompletions(IEnumerable<Goal> goals, string start, string end)
+ {
+ var metrics = "ga%3Avisits,";
+ foreach(var goal in goals)
+ {
+ metrics += "ga%3Agoal" + goal.Number + "Completions,";
+ }
+
+ metrics = metrics.TrimEnd(',');
+
+ var data = _dataProvider.SendRequest(_token, "https://www.google.com/analytics/feeds/data?ids=ga%3A" + _profile.Value + "&metrics=" + metrics + "&start-date=" + start + "&end-date=" + end + "&max-results=50");
+
+ var parser = new AnalyticsGoalParser();
+ var visits = parser.ParseVisits(data);
+
+ var completions = parser.ParseCompletions(data);
+
+ foreach(var completion in completions)
+ {
+ var goal = goals.FirstOrDefault(x => x.Number == completion.Key);
+ goal.Completions = completion.Value;
+ goal.ConversionRate = Math.Round((completion.Value / (double)visits) * 100, 2);
+
+ yield return goal;
+ }
+ }
+
+ public void GetTopTrafficSources(string start, string end)
+ {
+ if (string.IsNullOrEmpty(_token))
+ throw new Exception("No token found. Did you forget to login?");
+
+ if (_profile == null)
+ throw new Exception("No profile set. Use .Profile() to set.");
+
+ var url = "https://www.google.com/analytics/feeds/data?ids=ga%3A" + _profile.Value +
+ "&metrics=ga%3Avisits&dimensions=ga%3Akeyword&start-date=" + start + "&end-date=" + end +
+ "&max-results=50" +
+ "&filters=ga%3Akeyword!=(not set)0&sort=-ga%3Avisits";
+
+ var data = _dataProvider.SendRequest(_token, url);
+ Debug.Write(data);
+ }
+
+ public Goal GetGoalCompletions(Goal goal, string end)
+ {
+ var metrics = string.Format("ga%3Avisits,ga%3Agoal{0}Completions", goal.Number);
+ var data = _dataProvider.SendRequest(_token, "https://www.google.com/analytics/feeds/data?ids=ga%3A" + _profile.Value + "&metrics=" + metrics + "&start-date=" + goal.Updated.ToShortDateString() + "&end-date=" + end + "&max-results=50");
+
+ var parser = new AnalyticsGoalParser();
+
+ var visits = parser.ParseVisits(data);
+ var completions = parser.ParseCompletions(data);
+
+ foreach(var completion in completions)
+ {
+ goal.Completions = completion.Value;
+ goal.ConversionRate = Math.Round((completion.Value / (double)visits) * 100, 2);
+ }
+
+ return goal;
+ }
+ }
+
+ public class Goal
+ {
+ public string Name { get; set; }
+ public int Number { get; set; }
+ public bool Active { get; set; }
+ public int Completions { get; set; }
+ public double ConversionRate { get; set; }
+ public decimal Value { get; set; }
+ public DateTime Updated { get; set; }
+ }
+}
View
42 AnalyticsApi/ApiMap.cs
@@ -0,0 +1,42 @@
+using System;
+using System.Collections.Generic;
+using System.Linq.Expressions;
+
+namespace AnalyticsApi
+{
+ internal abstract class ApiMap<T>
+ {
+ public IList<ApiParseableProperty<T>> ParseMap = new List<ApiParseableProperty<T>>();
+
+ public void Map(Expression<Func<T, string>> exp, string path, string attribute = "value")
+ {
+ Map(exp, path, ElementLevel.EntryLevel);
+ }
+
+ public void Map(Expression<Func<T, string>> exp, string path, ElementLevel level, string attribute = "value")
+ {
+ ParseMap.Add(new ApiParseableProperty<T>(exp, level, path, attribute));
+ }
+ }
+
+ internal class ApiParseableProperty<T>
+ {
+ public Expression<Func<T, string>> Expr { get; set; }
+ public ElementLevel Level { get; set; }
+ public string AttributeName { get; set; }
+ public string Path { get; set; }
+
+ public ApiParseableProperty(Expression<Func<T, string>> expr, ElementLevel level, string path, string attribute)
+ {
+ Expr = expr;
+ Path = path;
+ Level = level;
+ AttributeName = attribute;
+ }
+
+ public ApiParseableProperty(Expression<Func<T, string>> expr, string path, string attribute)
+ : this(expr, ElementLevel.FeedLevel, path, attribute)
+ {
+ }
+ }
+}
View
8 AnalyticsApi/ElementLevel.cs
@@ -0,0 +1,8 @@
+namespace AnalyticsApi
+{
+ internal enum ElementLevel
+ {
+ EntryLevel = 1,
+ FeedLevel = 2
+ }
+}
View
13 AnalyticsApi/Exceptions/InvalidCredentialsException.cs
@@ -0,0 +1,13 @@
+using System;
+
+namespace AnalyticsApi
+{
+ public class InvalidCredentialsException : Exception
+ {
+ public InvalidCredentialsException(string message)
+ : base(message)
+ {
+
+ }
+ }
+}
View
20 AnalyticsApi/Extensions/EnumerableExtensions.cs
@@ -0,0 +1,20 @@
+using System.Collections.Generic;
+using System.Collections.Specialized;
+
+namespace AnalyticsApi
+{
+ internal static class EnumerableExtensions
+ {
+ public static NameValueCollection ToNameValueCollection<T>(this Dictionary<T, T> dictionary) where T : class
+ {
+ var collection = new NameValueCollection();
+
+ foreach (var item in dictionary)
+ {
+ collection.Add(item.Key.ToString(), item.Value.ToString());
+ }
+
+ return collection;
+ }
+ }
+}
View
104 AnalyticsApi/HttpWrapper.cs
@@ -0,0 +1,104 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Net;
+using System.Text;
+
+namespace AnalyticsApi
+{
+ public class HttpWrapper : IHttpWrapper
+ {
+ public static HttpWrapper Standard
+ {
+ get { return new HttpWrapper(); }
+ }
+
+ public string Get(string url, Dictionary<string, string> headers)
+ {
+ var req = (HttpWebRequest)WebRequest.Create(url);
+ req.Headers.Add(headers.ToNameValueCollection());
+
+ Debug.WriteLine(url);
+
+ using(var response = (HttpWebResponse)req.GetResponse())
+ using(var responseStream = response.GetResponseStream())
+ using(var sr = new StreamReader(responseStream))
+ {
+ return sr.ReadToEnd().Trim();
+ }
+ }
+
+ public string Post(string url, Dictionary<string, string> parameters)
+ {
+ if (url == null) throw new ArgumentNullException("url");
+ if (parameters == null) throw new ArgumentNullException("parameters");
+
+ var strParameters = GetParameters(parameters);
+
+ var webRequest = WebRequest.Create(url);
+ webRequest.ContentType = "application/x-www-form-urlencoded";
+ webRequest.Method = "POST";
+
+ var bytes = Encoding.ASCII.GetBytes(strParameters);
+
+ Stream stream = null;
+
+ try
+ {
+ webRequest.ContentLength = bytes.Length;
+ stream = webRequest.GetRequestStream();
+ stream.Write(bytes, 0, bytes.Length);
+ }
+ catch (WebException ex)
+ {
+ throw new WebException("Error", ex);
+ }
+ finally
+ {
+ if (stream != null)
+ stream.Close();
+ }
+
+ try
+ {
+ var webResponse = webRequest.GetResponse();
+ if (webResponse == null)
+ return null;
+
+ var streamReader = new StreamReader(webResponse.GetResponseStream());
+ return streamReader.ReadToEnd().Trim();
+ }
+ catch (WebException ex)
+ {
+ if(ex.Message.Contains("403"))
+ {
+ throw new InvalidCredentialsException("Invalid credentials");
+ }
+
+ throw new WebException("Post exception", ex);
+ }
+ }
+
+ protected string GetParameters(Dictionary<string, string> values)
+ {
+ if (values == null) throw new ArgumentNullException("values");
+
+ string parameters = string.Empty;
+
+ bool first = true;
+
+ foreach (var val in values)
+ {
+ if (!first)
+ parameters += "&";
+
+ parameters += string.Concat(val.Key, "=", val.Value);
+
+ first = false;
+ }
+
+ return parameters;
+ }
+ }
+}
View
23 AnalyticsApi/Interfaces/IAnalyticsDataProvider.cs
@@ -0,0 +1,23 @@
+using System.Collections.Generic;
+
+namespace AnalyticsApi
+{
+ public interface IAnalyticsDataProvider
+ {
+ /// <summary>
+ /// Try fetching login token from api
+ /// </summary>
+ /// <param name="username"></param>
+ /// <param name="password"></param>
+ /// <returns></returns>
+ string RequestLoginToken(string username, string password);
+
+ /// <summary>
+ /// Request specified resource from api
+ /// </summary>
+ /// <param name="token">Authorization token</param>
+ /// <param name="resource"></param>
+ /// <returns></returns>
+ string SendRequest(string token, string resource);
+ }
+}
View
10 AnalyticsApi/Interfaces/IHttpWrapper.cs
@@ -0,0 +1,10 @@
+using System.Collections.Generic;
+
+namespace AnalyticsApi
+{
+ public interface IHttpWrapper
+ {
+ string Get(string url, Dictionary<string, string> dictionary);
+ string Post(string url, Dictionary<string, string> parameters);
+ }
+}
View
19 AnalyticsApi/Maps/AnalyticsAccountRequest.cs
@@ -0,0 +1,19 @@
+namespace AnalyticsApi
+{
+ public class AnalyticsAccountRequest
+ {
+ public string AccountId { get; set; }
+ public string AccountName { get; set; }
+ public string ProfileId { get; set; }
+ }
+
+ internal class AnalyticsAccountApiMap : ApiMap<AnalyticsAccountRequest>
+ {
+ public AnalyticsAccountApiMap()
+ {
+ Map(x => x.AccountId, "ga:accountId");
+ Map(x => x.AccountName, "ga:accountName");
+ Map(x => x.ProfileId, "ga:profileId");
+ }
+ }
+}
View
73 AnalyticsApi/Maps/DashboardRequest.cs
@@ -0,0 +1,73 @@
+using System;
+using System.Diagnostics;
+using System.Globalization;
+
+namespace AnalyticsApi
+{
+ public class DashboardRequest
+ {
+ public string Visits { get; set; }
+ public string PageViews { get; set; }
+ public double PagesPerVisit
+ {
+ get
+ {
+ return Math.Round(double.Parse(PageViews, NumberStyles.Any) / double.Parse(Visits, NumberStyles.Any), 2);
+ }
+ }
+
+ /* Calculate bouncerate */
+ public string Bounces { get; set; }
+ public string Entrances { get; set; }
+ public double Bouncrate
+ {
+ get
+ {
+ return Math.Round((double.Parse(Bounces, NumberStyles.Any) / double.Parse(Entrances, NumberStyles.Any)) * 100, 2);
+ }
+ }
+
+ /* Calculate average time on site */
+ public string TimeOnSite { get; set; }
+ public double AverageTime
+ {
+ get
+ {
+ var timePerVisit = float.Parse(TimeOnSite.Replace(".", ",")) / float.Parse(Visits.Replace(".", ","), NumberStyles.Any);
+
+ var minutes = Math.Floor(timePerVisit / 60);
+ var seconds = Math.Round(timePerVisit % 60);
+ return double.Parse(minutes + "," + seconds);
+ }
+ }
+
+ /* Calculate number of new visits */
+ public double NewVisitsPercent
+ {
+ get
+ {
+ return Math.Round((double.Parse(NewVisits, NumberStyles.Any) / double.Parse(Visits, NumberStyles.Any) * 100), 2);
+ }
+ }
+ public string NewVisits { get; set; }
+
+ public string StartDate { get; set; }
+ public string EndDate { get; set; }
+ }
+
+ internal class DashboardApiMap : ApiMap<DashboardRequest>
+ {
+ public DashboardApiMap()
+ {
+ Map(x => x.Visits, "ga:visits");
+ Map(x => x.PageViews, "ga:pageviews");
+ Map(x => x.Bounces, "ga:bounces");
+ Map(x => x.Entrances, "ga:entrances");
+ Map(x => x.TimeOnSite, "ga:timeonsite");
+ Map(x => x.NewVisits, "ga:newvisits");
+
+ Map(x => x.StartDate, "startDate", ElementLevel.FeedLevel);
+ Map(x => x.EndDate, "endDate", ElementLevel.FeedLevel);
+ }
+ }
+}
View
15 AnalyticsApi/Maps/GoalCompletionsRequest.cs
@@ -0,0 +1,15 @@
+namespace AnalyticsApi
+{
+ public class GoalCompletionsRequest
+ {
+ public string Completions { get; set; }
+ }
+
+ internal class GoalCompletionsRequestApiMap : ApiMap<GoalCompletionsRequest>
+ {
+ public GoalCompletionsRequestApiMap(int goalNumber)
+ {
+ Map(x => x.Completions, "ga:goal" + goalNumber + "Completions");
+ }
+ }
+}
View
7 AnalyticsApi/Maps/GoalRequest.cs
@@ -0,0 +1,7 @@
+namespace AnalyticsApi
+{
+ public class GoalRequest
+ {
+ public string Name { get; set; }
+ }
+}
View
111 AnalyticsApi/Parsers/AnalyticsGoalParser.cs
@@ -0,0 +1,111 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Globalization;
+using System.Linq;
+using System.Xml.Linq;
+
+namespace AnalyticsApi
+{
+ internal class AnalyticsGoalParser
+ {
+ public IEnumerable<Goal> ParseGoals(string xml)
+ {
+ var doc = XDocument.Parse(xml);
+ var ns = doc.Root.GetDefaultNamespace();
+ var gaPrefix = doc.Root.GetNamespaceOfPrefix("ga");
+
+ var nodes = doc.Root.Descendants(ns + "entry");
+ var goals = new List<Goal>();
+
+ if (nodes.Any())
+ {
+ foreach (var node in nodes)
+ {
+ var goal = node.Descendants(gaPrefix + "goal");
+ if(goal.Any())
+ {
+ var number = int.Parse(goal.First().Attribute("number").Value);
+ var name = goal.First().Attribute("name").Value;
+ var active = bool.Parse(goal.First().Attribute("active").Value);
+ var value = decimal.Parse(goal.First().Attribute("value").Value.Replace(".", ","));
+ var updated = DateTime.Parse(node.Element(ns + "updated").Value);
+
+ goals.Add(new Goal {Number = number, Name = name, Active = active, Value = value, Updated = updated});
+ }
+ }
+ }
+
+ return goals;
+ }
+
+ public Dictionary<int, int> ParseCompletions(string xml)
+ {
+ var dictionary = new Dictionary<int, int>();
+
+ var doc = XDocument.Parse(xml);
+ var ns = doc.Root.GetDefaultNamespace();
+ var prefix = doc.Root.GetNamespaceOfPrefix("dxp");
+
+ var nodes = doc.Root.Descendants(ns + "entry");
+
+ if(nodes.Any())
+ {
+ var node = nodes.First();
+ var metrics = node.Descendants(prefix + "metric");
+
+ if(metrics.Any())
+ {
+ foreach(var metric in metrics)
+ {
+ var number = metric.Attribute("name").Value.Replace("ga:goal", string.Empty).Replace("Completions", string.Empty);
+
+ if (number == "ga:visits")
+ continue;
+
+ var completions = metric.Attribute("value").Value;
+
+ try
+ {
+ dictionary.Add(int.Parse(number), int.Parse(completions));
+ }
+ catch(ArgumentException ex)
+ {
+ // Swallow
+ }
+ }
+ }
+ }
+
+ return dictionary;
+ }
+
+ public int ParseVisits(string xml)
+ {
+ //<dxp:metric confidenceInterval='0.0' name='ga:visits' type='integer' value='3733'/>
+ var visits = 0;
+
+ var doc = XDocument.Parse(xml);
+ var ns = doc.Root.GetDefaultNamespace();
+ var prefix = doc.Root.GetNamespaceOfPrefix("dxp");
+
+ var nodes = doc.Root.Descendants(ns + "entry");
+
+ if (nodes.Any())
+ {
+ var node = nodes.First();
+ var metrics = node.Descendants(prefix + "metric");
+ foreach(var metric in metrics)
+ {
+ if(metric.Attribute("name").Value == "ga:visits")
+ {
+ var value = metric.Attribute("value").Value;
+ visits = int.Parse(value);
+ }
+ }
+ }
+
+ return visits;
+ }
+ }
+}
View
94 AnalyticsApi/Parsers/AnalyticsXmlParser.cs
@@ -0,0 +1,94 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Xml.Linq;
+
+namespace AnalyticsApi
+{
+ internal class AnalyticsXmlParser
+ {
+ public IEnumerable<TReturn> Parse<TReturn>(string xml, ApiMap<TReturn> map)
+ where TReturn : new()
+ {
+ var result = new List<TReturn>();
+ var doc = XDocument.Parse(xml);
+ var ns = doc.Root.GetDefaultNamespace();
+ var nodes = doc.Root.Descendants(ns + "entry"); // Default path is entry
+
+ foreach (var node in nodes)
+ {
+ var item = new TReturn();
+ var type = item.GetType();
+
+ foreach (var prop in map.ParseMap)
+ {
+ var key = prop.Expr;
+ var propertyName = (key.Body as MemberExpression).Member.Name;
+
+ var property = type.GetProperty(propertyName);
+
+ var value = GetXmlValue(doc, prop.Level, prop.Path, prop.AttributeName);
+
+ property.SetValue(item, value, null);
+ }
+
+ result.Add(item);
+ }
+
+ return result;
+ }
+
+ public string GetXmlValue(XDocument doc, ElementLevel level, string path, string attributeName)
+ {
+ // Get namespaces
+ var ns = doc.Root.GetDefaultNamespace();
+ var prefix = doc.Root.GetNamespaceOfPrefix("dxp");
+ var gaPrefix = doc.Root.GetNamespaceOfPrefix("ga");
+
+ if (level == ElementLevel.FeedLevel)
+ {
+ return GetXmlTopValue(doc, path);
+ }
+
+ // Elementlevel is entry level
+ var nodes = doc.Root.Descendants(ns + "entry");
+
+ var value = "";
+
+ foreach (var node in nodes)
+ {
+ // Try finding properties first
+ var xmlNode = node.Elements(prefix + "property")
+ .Where(x => x.Attribute("name").Value.ToLower() == path.ToLower());
+
+ // Then metrics
+ if (xmlNode.Any() == false)
+ xmlNode = node.Elements(prefix + "metric")
+ .Where(x => x.Attribute("name").Value.ToLower() == path.ToLower());
+
+ // Then dimensions
+ if (xmlNode.Any() == false)
+ xmlNode = node.Elements(prefix + "dimension")
+ .Where(x => x.Attribute("name").Value.ToLower() == path.ToLower());
+
+ if (xmlNode.Any() == false)
+ return string.Empty;
+
+ value = xmlNode.First().Attribute(attributeName).Value;
+ }
+
+ return value;
+ }
+
+ private string GetXmlTopValue(XDocument doc,string path)
+ {
+ if (doc.Root == null) return string.Empty;
+
+ var prefix = doc.Root.GetNamespaceOfPrefix("dxp");
+
+ var node = doc.Descendants(prefix + path);
+ return node.Any() == false ? string.Empty : doc.Descendants(prefix + path).First().Value;
+ }
+ }
+}
View
36 AnalyticsApi/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("AnalyticsApi")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Microsoft")]
+[assembly: AssemblyProduct("AnalyticsApi")]
+[assembly: AssemblyCopyright("Copyright © Microsoft 2010")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("2d59967d-5163-4074-b7b2-ac4cc52a7a58")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
View
BIN  AnalyticsApi/bin/Debug/AnalyticsApi.dll
Binary file not shown
View
BIN  AnalyticsApi/bin/Debug/AnalyticsApi.pdb
Binary file not shown
View
BIN  AnalyticsApi/bin/Release/AnalyticsApi.dll
Binary file not shown
View
BIN  AnalyticsApi/bin/Release/AnalyticsApi.pdb
Binary file not shown
View
10 AnalyticsApi/obj/Debug/AnalyticsApi.csproj.FileListAbsolute.txt
@@ -0,0 +1,10 @@
+P:\Experiments\AnalyticsApi\AnalyticsApi\bin\Debug\AnalyticsApi.dll
+P:\Experiments\AnalyticsApi\AnalyticsApi\bin\Debug\AnalyticsApi.pdb
+P:\Experiments\AnalyticsApi\AnalyticsApi\obj\Debug\ResolveAssemblyReference.cache
+P:\Experiments\AnalyticsApi\AnalyticsApi\obj\Debug\AnalyticsApi.dll
+P:\Experiments\AnalyticsApi\AnalyticsApi\obj\Debug\AnalyticsApi.pdb
+P:\Private\AnalyticsApi\AnalyticsApi\bin\Debug\AnalyticsApi.dll
+P:\Private\AnalyticsApi\AnalyticsApi\bin\Debug\AnalyticsApi.pdb
+P:\Private\AnalyticsApi\AnalyticsApi\obj\Debug\ResolveAssemblyReference.cache
+P:\Private\AnalyticsApi\AnalyticsApi\obj\Debug\AnalyticsApi.dll
+P:\Private\AnalyticsApi\AnalyticsApi\obj\Debug\AnalyticsApi.pdb
View
BIN  AnalyticsApi/obj/Debug/AnalyticsApi.dll
Binary file not shown
View
BIN  AnalyticsApi/obj/Debug/AnalyticsApi.pdb
Binary file not shown
View
BIN  AnalyticsApi/obj/Debug/DesignTimeResolveAssemblyReferencesInput.cache
Binary file not shown
View
5 AnalyticsApi/obj/Release/AnalyticsApi.csproj.FileListAbsolute.txt
@@ -0,0 +1,5 @@
+P:\Experiments\AnalyticsApi\AnalyticsApi\bin\Release\AnalyticsApi.dll
+P:\Experiments\AnalyticsApi\AnalyticsApi\bin\Release\AnalyticsApi.pdb
+P:\Experiments\AnalyticsApi\AnalyticsApi\obj\Release\ResolveAssemblyReference.cache
+P:\Experiments\AnalyticsApi\AnalyticsApi\obj\Release\AnalyticsApi.dll
+P:\Experiments\AnalyticsApi\AnalyticsApi\obj\Release\AnalyticsApi.pdb
View
BIN  AnalyticsApi/obj/Release/AnalyticsApi.dll
Binary file not shown
View
BIN  AnalyticsApi/obj/Release/AnalyticsApi.pdb
Binary file not shown
View
BIN  AnalyticsApi/obj/Release/DesignTimeResolveAssemblyReferencesInput.cache
Binary file not shown
Please sign in to comment.
Something went wrong with that request. Please try again.