Skip to content
This repository
Browse code

Adding package download statistics to Home page

  • Loading branch information...
commit da9fee5ac12c13129c2dcfd666942f0c8c40fd9d 1 parent 764a1f2
Pranav K pranavkm authored
4 Website/App_Start/ContainerBindings.cs
@@ -207,6 +207,10 @@ public override void Load()
207 207 Bind<IUserByUsernameQuery>()
208 208 .To<UserByUsernameQuery>()
209 209 .InRequestScope();
  210 +
  211 + Bind<IAggregateStatsService>()
  212 + .To<AggregateStatsService>()
  213 + .InRequestScope();
210 214 Bind<IPackageIdsQuery>()
211 215 .To<PackageIdsQuery>()
212 216 .InRequestScope();
5 Website/App_Start/Routes.cs
@@ -14,6 +14,11 @@ public static void RegisterRoutes(RouteCollection routes)
14 14 "",
15 15 MVC.Pages.Home());
16 16
  17 + routes.MapRoute(
  18 + RouteName.Stats,
  19 + "stats",
  20 + MVC.Pages.Stats());
  21 +
17 22 routes.Add(new JsonRoute("json/{controller}"));
18 23
19 24 routes.MapRoute(
29 Website/Content/Site.css
@@ -390,7 +390,34 @@ header.main {
390 390 border-color: #4585aa;
391 391 text-decoration: none;
392 392 }
393   -
  393 +
  394 + /* Aggregate Stats */
  395 + .aggstats {
  396 + background: #555;
  397 + display: none;
  398 + }
  399 +
  400 + .aggstats .stat {
  401 + display: inline-block;
  402 + *zoom: 1;
  403 + color: #bbb;
  404 + font-weight: bold;
  405 + text-align: center;
  406 + padding: 0.5em 0;
  407 + width: 33%;
  408 + margin: 0;
  409 + }
  410 + .aggstats .num {
  411 + font-size: 4em;
  412 + color: #f9f9f9;
  413 + font-family: monospace;
  414 + display: block;
  415 + line-height: 0.9em;
  416 + }
  417 +
  418 + .aggstats .num > span {
  419 + position: relative;
  420 + }
394 421
395 422 /* Account/Actions */
396 423 #actions h1 {
21 Website/Controllers/PagesController.cs
... ... @@ -1,9 +1,17 @@
1 1 using System.Web.Mvc;
  2 +using System.Web.UI;
2 3
3 4 namespace NuGetGallery
4 5 {
5 6 public partial class PagesController : Controller
6 7 {
  8 + private readonly IAggregateStatsService statsSvc;
  9 +
  10 + public PagesController(IAggregateStatsService statsSvc)
  11 + {
  12 + this.statsSvc = statsSvc;
  13 + }
  14 +
7 15 public virtual ActionResult Home()
8 16 {
9 17 return View();
@@ -18,5 +26,18 @@ public virtual ActionResult Privacy()
18 26 {
19 27 return View();
20 28 }
  29 +
  30 + [HttpGet]
  31 + [OutputCache(VaryByParam = "None", Duration = 120, Location = OutputCacheLocation.Server)]
  32 + public virtual JsonResult Stats()
  33 + {
  34 + var stats = statsSvc.GetAggregateStats();
  35 + return Json(new
  36 + {
  37 + Downloads = stats.Downloads.ToString("#,#"),
  38 + UniquePackages = stats.UniquePackages.ToString("#,#"),
  39 + TotalPackages = stats.TotalPackages.ToString("#,#")
  40 + }, JsonRequestBehavior.AllowGet);
  41 + }
21 42 }
22 43 }
9 Website/PagesController.generated.cs
@@ -23,9 +23,6 @@
23 23 namespace NuGetGallery {
24 24 public partial class PagesController {
25 25 [GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode]
26   - public PagesController() { }
27   -
28   - [GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode]
29 26 protected PagesController(Dummy d) { }
30 27
31 28 [GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode]
@@ -50,6 +47,7 @@ public class ActionNamesClass {
50 47 public readonly string Home = "Home";
51 48 public readonly string Terms = "Terms";
52 49 public readonly string Privacy = "Privacy";
  50 + public readonly string Stats = "Stats";
53 51 }
54 52
55 53
@@ -83,6 +81,11 @@ public class T4MVC_PagesController: NuGetGallery.PagesController {
83 81 return callInfo;
84 82 }
85 83
  84 + public override System.Web.Mvc.JsonResult Stats() {
  85 + var callInfo = new T4MVC_JsonResult(Area, Name, ActionNames.Stats);
  86 + return callInfo;
  87 + }
  88 +
86 89 }
87 90 }
88 91
1  Website/RouteNames.cs
@@ -12,6 +12,7 @@ public static class RouteName
12 12 public const string DownloadPackage = "DownloadPackage";
13 13 public const string DownloadNuGetExe = "DownloadNuGetExe";
14 14 public const string Home = "Home";
  15 + public const string Stats = "Stats";
15 16 public const string Policies = "Policies";
16 17 public const string ListPackages = "ListPackages";
17 18 public const string Authentication = "SignIn";
53 Website/Scripts/stats.js
... ... @@ -0,0 +1,53 @@
  1 +function getStats() {
  2 + $.get('/Stats', function(data) {
  3 + var section = $('section.aggstats');
  4 + section.show();
  5 + update(data, 'UniquePackages');
  6 + update(data, 'Downloads');
  7 + update(data, 'TotalPackages');
  8 + })
  9 + setTimeout(getStats, 30000);
  10 +}
  11 +
  12 +function update(data, key) {
  13 + var value = data[key].toString();
  14 + var self = $('#' + key);
  15 + var currentValue = $.trim(self.text().replace(/\s/g, ''));
  16 + if (currentValue != value) {
  17 + var length = value.length;
  18 + var currLength = currentValue.length;
  19 + var items = self.children('span');
  20 +
  21 + if (currLength > length) {
  22 + while (items.length > length) {
  23 + items.first().remove();
  24 + items = self.children('span');
  25 + }
  26 + }
  27 + else if (currLength < length) {
  28 + var i;
  29 + for (i = currLength; i < length; i++) {
  30 + self.prepend('<span>' + value.charAt(length - i - 1) + '</span>');
  31 + }
  32 + items = self.children('span');
  33 + currentValue = $.trim(self.text().replace(/\s/g, ''));
  34 + currLength = currentValue.length;
  35 + }
  36 +
  37 + $.each(value.split('').reverse(), function (i, e) {
  38 + var c = (i < currLength) ? currentValue.charAt(currLength - i - 1) : '';
  39 + if (c != e) {
  40 + var el = $(items[length - i - 1]);
  41 + animateEl(el, e);
  42 + }
  43 + });
  44 + }
  45 +
  46 + function animateEl(el, v) {
  47 + v = v || '';
  48 + var parent = e.parent();
  49 + el.animate({ top: 0.3 * parseInt(parent.height()) }, 350, 'linear', function () {
  50 + $(this).html(v).css({ top: -0.8 * parseInt(parent.height()) }).animate({ top: 0 }, 350, 'linear')
  51 + });
  52 + }
  53 +}
39 Website/Services/AggregateStatsService.cs
... ... @@ -0,0 +1,39 @@
  1 +using System.Data;
  2 +
  3 +namespace NuGetGallery
  4 +{
  5 + public class AggregateStatsService : IAggregateStatsService
  6 + {
  7 + public AggregateStats GetAggregateStats()
  8 + {
  9 + using (var dbContext = new EntitiesContext())
  10 + {
  11 + var database = dbContext.Database;
  12 + using (var command = database.Connection.CreateCommand())
  13 + {
  14 + command.CommandText = @"Select
  15 + (Select Count([Key]) from PackageRegistrations) as UniquePackages,
  16 + (Select Count([Key]) from Packages where Listed = 1) as TotalPackages,
  17 + (
  18 + (Select Sum(DownloadCount) from Packages) +
  19 + (Select Count([Key]) from PackageStatistics where [Key] >
  20 + (Select DownloadStatsLastAggregatedId FROM GallerySettings)
  21 + )
  22 + ) as DownloadCount";
  23 +
  24 + database.Connection.Open();
  25 + using (var reader = command.ExecuteReader(CommandBehavior.CloseConnection | CommandBehavior.SingleRow))
  26 + {
  27 + reader.Read();
  28 + return new AggregateStats
  29 + {
  30 + UniquePackages = reader.GetInt32(0),
  31 + TotalPackages = reader.GetInt32(1),
  32 + Downloads = reader.GetInt32(2),
  33 + };
  34 + }
  35 + }
  36 + }
  37 + }
  38 + }
  39 +}
8 Website/Services/IAggregateStatsService.cs
... ... @@ -0,0 +1,8 @@
  1 +
  2 +namespace NuGetGallery
  3 +{
  4 + public interface IAggregateStatsService
  5 + {
  6 + AggregateStats GetAggregateStats();
  7 + }
  8 +}
19 Website/T4MVC.cs
@@ -253,7 +253,8 @@ public static class T4Extensions {
253 253 result.RouteValueDictionary.Add("Action", action);
254 254 }
255 255
256   - public static bool FileExists(string virtualPath) {
  256 + public static bool FileExists(string virtualPath)
  257 + {
257 258 if (!HostingEnvironment.IsHosted) return false;
258 259 string filePath = HostingEnvironment.MapPath(virtualPath);
259 260 return System.IO.File.Exists(filePath);
@@ -302,7 +303,19 @@ public class T4MVC_ActionResult : System.Web.Mvc.ActionResult, IT4MVCActionResul
302 303 public string Action { get; set; }
303 304 public RouteValueDictionary RouteValueDictionary { get; set; }
304 305 }
  306 +[GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode]
  307 +public class T4MVC_JsonResult : System.Web.Mvc.JsonResult, IT4MVCActionResult
  308 +{
  309 + public T4MVC_JsonResult(string area, string controller, string action)
  310 + : base()
  311 + {
  312 + this.InitMVCT4Result(area, controller, action);
  313 + }
305 314
  315 + public string Controller { get; set; }
  316 + public string Action { get; set; }
  317 + public RouteValueDictionary RouteValueDictionary { get; set; }
  318 +}
306 319
307 320
308 321 namespace Links {
@@ -311,6 +324,8 @@ public static class Scripts {
311 324 private const string URLPATH = "~/Scripts";
312 325 public static string Url() { return T4MVCHelpers.ProcessVirtualPath(URLPATH); }
313 326 public static string Url(string fileName) { return T4MVCHelpers.ProcessVirtualPath(URLPATH + "/" + fileName); }
  327 + public static readonly string Home_js = T4MVCHelpers.IsProduction() && T4Extensions.FileExists(URLPATH + "/Home.min.js") ? Url("Home.min.js") : Url("Home.js");
  328 +
314 329 public static readonly string jquery_1_6_2_vsdoc_js = T4MVCHelpers.IsProduction() && T4Extensions.FileExists(URLPATH + "/jquery-1.6.2-vsdoc.min.js") ? Url("jquery-1.6.2-vsdoc.min.js") : Url("jquery-1.6.2-vsdoc.js");
315 330
316 331 public static readonly string jquery_1_6_2_js = T4MVCHelpers.IsProduction() && T4Extensions.FileExists(URLPATH + "/jquery-1.6.2.min.js") ? Url("jquery-1.6.2.min.js") : Url("jquery-1.6.2.js");
@@ -327,7 +342,7 @@ public static class Scripts {
327 342 public static readonly string modernizr_2_0_6_development_only_js = T4MVCHelpers.IsProduction() && T4Extensions.FileExists(URLPATH + "/modernizr-2.0.6-development-only.min.js") ? Url("modernizr-2.0.6-development-only.min.js") : Url("modernizr-2.0.6-development-only.js");
328 343
329 344 public static readonly string ZeroClipboard_js = T4MVCHelpers.IsProduction() && T4Extensions.FileExists(URLPATH + "/ZeroClipboard.min.js") ? Url("ZeroClipboard.min.js") : Url("ZeroClipboard.js");
330   -
  345 +
331 346 public static readonly string ZeroClipboard_swf = Url("ZeroClipboard.swf");
332 347 }
333 348
12 Website/ViewModels/AggregateStats.cs
... ... @@ -0,0 +1,12 @@
  1 +
  2 +namespace NuGetGallery
  3 +{
  4 + public class AggregateStats
  5 + {
  6 + public long Downloads { get; set; }
  7 +
  8 + public int UniquePackages { get; set; }
  9 +
  10 + public int TotalPackages { get; set; }
  11 + }
  12 +}
20 Website/Views/Pages/Home.cshtml
@@ -11,6 +11,18 @@
11 11 <img src="@Url.Content("~/content/images/hero.png")" alt="NuGet GUI Window" />
12 12 </section>
13 13
  14 +<section class="aggstats">
  15 + <span class="stat">
  16 + <span id="UniquePackages" class="num"></span> unique packages
  17 + </span>
  18 + <span class="stat">
  19 + <span id="Downloads" class="num"></span>total downloads
  20 + </span>
  21 + <span class="stat">
  22 + <span id="TotalPackages" class="num"></span>total packages
  23 + </span>
  24 +</section>
  25 +
14 26 <section class="release">
15 27 <h2>NuGet 1.8 Released</h2>
16 28 <p>
@@ -34,3 +46,11 @@
34 46 <a title="Creating and submitting a package" href="http://docs.nuget.org/docs/creating-packages/creating-and-publishing-a-package">how to
35 47 create and publish a package</a>. If you don&rsquo;t plan on submitting a package, there&rsquo;s no need to register.</p>
36 48 </section>
  49 +
  50 +@section BottomScripts
  51 +{
  52 + <script src="@Url.Content("~/Scripts/stats.js")"></script>
  53 + <script>
  54 + $(getStats);
  55 + </script>
  56 +}
11 Website/Website.csproj
@@ -366,12 +366,14 @@
366 366 <Compile Include="Services\IFileStorageService.cs" />
367 367 <Compile Include="Services\IIndexingService.cs" />
368 368 <Compile Include="Services\INuGetExeDownloaderService.cs" />
  369 + <Compile Include="Services\IAggregateStatsService.cs" />
369 370 <Compile Include="Services\ISearchService.cs" />
370 371 <Compile Include="Services\IUploadFileService.cs" />
371 372 <Compile Include="Services\NuGetExeDownloaderService.cs" />
372 373 <Compile Include="Services\PackageFileService.cs" />
373 374 <Compile Include="RequireRemoteHttpsAttribute.cs" />
374 375 <Compile Include="Services\PackageSearchResults.cs" />
  376 + <Compile Include="Services\AggregateStatsService.cs" />
375 377 <Compile Include="Services\TestableStorageClientException.cs" />
376 378 <Compile Include="Services\UploadFileService.cs" />
377 379 <Compile Include="SharedController.generated.cs">
@@ -386,6 +388,7 @@
386 388 <DependentUpon>T4MVC.tt</DependentUpon>
387 389 </Compile>
388 390 <Compile Include="ViewModels\AccountViewModel.cs" />
  391 + <Compile Include="ViewModels\AggregateStats.cs" />
389 392 <Compile Include="ViewModels\CuratedFeedViewModel.cs" />
390 393 <Compile Include="ViewModels\EditProfileViewModel.cs" />
391 394 <Compile Include="ViewModels\EmailConfirmationModel.cs" />
@@ -773,6 +776,7 @@
773 776 <Content Include="DynamicData\Site.css" />
774 777 <Content Include="Errors\Error.html" />
775 778 <Content Include="favicon.ico" />
  779 + <Content Include="Scripts\stats.js" />
776 780 <Content Include="Scripts\jquery-1.6.2-vsdoc.js" />
777 781 <Content Include="Scripts\jquery-1.6.2.js" />
778 782 <Content Include="Scripts\jquery-1.6.2.min.js" />
@@ -839,13 +843,6 @@
839 843 <Compile Include="Infrastructure\Lucene\LuceneSearchService.cs" />
840 844 <Content Include="Views\CuratedFeeds\CuratedFeed.cshtml" />
841 845 <Content Include="Views\CuratedPackages\CreateCuratedPackageForm.cshtml" />
842   - <None Include="_bin_deployableAssemblies\Microsoft.Web.Infrastructure.dll" />
843   - <None Include="_bin_deployableAssemblies\System.Web.WebPages.Razor.dll" />
844   - <None Include="_bin_deployableAssemblies\System.Web.WebPages.dll" />
845   - <None Include="_bin_deployableAssemblies\System.Web.WebPages.Deployment.dll" />
846   - <None Include="_bin_deployableAssemblies\System.Web.Razor.dll" />
847   - <None Include="_bin_deployableAssemblies\System.Web.Helpers.dll" />
848   - <None Include="_bin_deployableAssemblies\System.Web.Mvc.dll" />
849 846 </ItemGroup>
850 847 <ItemGroup>
851 848 <Content Include="packages.config">

0 comments on commit da9fee5

Please sign in to comment.
Something went wrong with that request. Please try again.