<|start_filename|>src/NuGetTrends.Data/DailyDownload.cs<|end_filename|>
using System;

namespace NuGetTrends.Data
{
    public class DailyDownload
    {
        public string? PackageId { get; set; }
        public DateTime Date { get; set; }
        public long? DownloadCount { get; set; }
    }
}


<|start_filename|>src/NuGetTrends.Web/Portal/src/app/home/home.component.html<|end_filename|>
<section class="hero is-white hero-space" [@slideInOut]>
  <div class="hero-body">
    <div class="container is-fluid">
      <div class="columns">
        <div class="column is-4">
          <nav class="level">
            <div class="level-item has-text-centered">
              <figure class='image is-96x96'><img src='../../assets/images/chart_icon.png'></figure>
            </div>
          </nav>
          <p class="title is-4 has-text-centered">Compare package download</p>
          <p class="subtitle is-5 has-text-centered">See the download history of NuGet Packages over time</p>
        </div>
        <div class="column is-4">
          <nav class="level">
            <div class="level-item has-text-centered">
              <figure class='image is-96x96'><img src='../../assets/images/target_icon.png'></figure>
            </div>
          </nav>
          <p class="title is-4 has-text-centered">Compare Target Framework Adoption</p>
          <p class="subtitle is-5 has-text-centered">Find out the amount of packages targeting a specific target
            framework</p>
        </div>
        <div class="column is-4">
          <nav class="level">
            <div class="level-item has-text-centered">
              <figure class='image is-96x96'><img src='../../assets/images/decision_icon.png'></figure>
            </div>
          </nav>
          <p class="title is-4 has-text-centered">Decide adoption based on facts</p>
          <p class="subtitle is-5 has-text-centered">Decide whether to use or not that package or target framework based on
            real data</p>
        </div>
      </div>
    </div>
  </div>
</section>


<|start_filename|>src/NuGetTrends.Data/Migrations/20200920210940_ShortenConstrNames.cs<|end_filename|>
using Microsoft.EntityFrameworkCore.Migrations;

namespace NuGetTrends.Data.Migrations
{
    public partial class ShortenConstrNames : Migration
    {
        protected override void Up(MigrationBuilder migrationBuilder)
        {
            // Foreign keys are too long, so EF generated a new migration trying to change it
            // This happened after upgrade to 3.x. So we manually set the name inside OnModelCreating
            migrationBuilder.Sql(@"
ALTER TABLE package_dependency RENAME CONSTRAINT
fk_package_dependency_package_dependency_group_package_dependen TO fk_package_dependency_package_dependency_group_id;");

            migrationBuilder.Sql(@"
ALTER TABLE package_dependency_group RENAME CONSTRAINT
fk_package_dependency_group_package_details_catalog_leafs_packa TO fk_package_dependency_group_package_details_catalog_leafs_id;");
        }

        protected override void Down(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.Sql(@"
ALTER TABLE package_dependency RENAME CONSTRAINT
fk_package_dependency_package_dependency_group_id TO fk_package_dependency_package_dependency_group_package_dependen;");

            migrationBuilder.Sql(@"
ALTER TABLE package_dependency_group RENAME CONSTRAINT
fk_package_dependency_group_package_details_catalog_leafs_id TO fk_package_dependency_group_package_details_catalog_leafs_packa;");
        }
    }
}


<|start_filename|>src/NuGetTrends.Scheduler/INuGetSearchService.cs<|end_filename|>
﻿using System.Threading;
using System.Threading.Tasks;
using NuGet.Protocol.Core.Types;

namespace NuGetTrends.Scheduler
{
    public interface INuGetSearchService
    {
        Task<IPackageSearchMetadata?> GetPackage(string packageId, CancellationToken token);
    }
}


<|start_filename|>src/NuGet.Protocol.Catalog/Serialization/CatalogLeafItemTypeConverter.cs<|end_filename|>
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using NuGet.Protocol.Catalog.Models;

namespace NuGet.Protocol.Catalog.Serialization
{
    internal class CatalogLeafItemTypeConverter : BaseCatalogLeafConverter
    {
        private static readonly Dictionary<CatalogLeafType, string> FromType = new Dictionary<CatalogLeafType, string>
        {
            { CatalogLeafType.PackageDelete, "nuget:PackageDelete" },
            { CatalogLeafType.PackageDetails, "nuget:PackageDetails" },
        };

        private static readonly Dictionary<string, CatalogLeafType> FromString = FromType
            .ToDictionary(x => x.Value, x => x.Key);

        public CatalogLeafItemTypeConverter() : base(FromType)
        {
        }

        public override object ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
        {
            if (reader.Value is string stringValue && FromString.TryGetValue(stringValue, out var output))
            {
                return output;
            }

            throw new JsonSerializationException($"Unexpected value for a {nameof(CatalogLeafType)}.");
        }
    }
}


<|start_filename|>src/NuGetTrends.Data/Migrations/20180929220242_Init.cs<|end_filename|>
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
// ReSharper disable RedundantArgumentDefaultValue

namespace NuGetTrends.Data.Migrations
{
    public partial class Init : Migration
    {
        protected override void Up(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.CreateTable(
                name: "cursors",
                columns: table => new
                {
                    id = table.Column<string>(nullable: false),
                    value = table.Column<DateTimeOffset>(nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("pk_cursors", x => x.id);
                });

            migrationBuilder.CreateTable(
                name: "daily_downloads",
                columns: table => new
                {
                    package_id = table.Column<string>(nullable: false),
                    date = table.Column<DateTime>(nullable: false),
                    download_count = table.Column<long>(nullable: true)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_daily_downloads", x => new { x.package_id, x.date });
                });

            migrationBuilder.CreateTable(
                name: "package_details_catalog_leafs",
                columns: table => new
                {
                    type = table.Column<int>(nullable: false),
                    commit_timestamp = table.Column<DateTimeOffset>(nullable: false),
                    package_id = table.Column<string>(nullable: true),
                    published = table.Column<DateTimeOffset>(nullable: false),
                    package_version = table.Column<string>(nullable: true),
                    id = table.Column<int>(nullable: false)
                        .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn),
                    authors = table.Column<string>(nullable: true),
                    created = table.Column<DateTimeOffset>(nullable: false),
                    last_edited = table.Column<DateTimeOffset>(nullable: false),
                    description = table.Column<string>(nullable: true),
                    icon_url = table.Column<string>(nullable: true),
                    is_prerelease = table.Column<bool>(nullable: false),
                    language = table.Column<string>(nullable: true),
                    license_url = table.Column<string>(nullable: true),
                    listed = table.Column<bool>(nullable: true),
                    min_client_version = table.Column<string>(nullable: true),
                    package_hash = table.Column<string>(nullable: true),
                    package_hash_algorithm = table.Column<string>(nullable: true),
                    package_size = table.Column<long>(nullable: false),
                    project_url = table.Column<string>(nullable: true),
                    release_notes = table.Column<string>(nullable: true),
                    require_license_agreement = table.Column<bool>(nullable: true),
                    summary = table.Column<string>(nullable: true),
                    tags = table.Column<List<string>>(nullable: true),
                    title = table.Column<string>(nullable: true),
                    verbatim_version = table.Column<string>(nullable: true)
                },
                constraints: table =>
                {
                    table.PrimaryKey("pk_package_details_catalog_leafs", x => x.id);
                });

            migrationBuilder.CreateTable(
                name: "package_downloads",
                columns: table => new
                {
                    package_id = table.Column<string>(nullable: false),
                    package_id_lowered = table.Column<string>(nullable: false),
                    latest_download_count = table.Column<long>(nullable: true),
                    latest_download_count_checked_utc = table.Column<DateTime>(nullable: false),
                    icon_url = table.Column<string>(nullable: true)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_package_downloads", x => x.package_id);
                });

            migrationBuilder.CreateTable(
                name: "package_dependency_group",
                columns: table => new
                {
                    id = table.Column<int>(nullable: false)
                        .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn),
                    target_framework = table.Column<string>(nullable: true),
                    package_details_catalog_leaf_id = table.Column<int>(nullable: true)
                },
                constraints: table =>
                {
                    table.PrimaryKey("pk_package_dependency_group", x => x.id);
                    table.ForeignKey(
                        name: "fk_package_dependency_group_package_details_catalog_leafs_package~",
                        column: x => x.package_details_catalog_leaf_id,
                        principalTable: "package_details_catalog_leafs",
                        principalColumn: "id",
                        onDelete: ReferentialAction.Cascade);
                });

            migrationBuilder.CreateTable(
                name: "package_dependency",
                columns: table => new
                {
                    id = table.Column<int>(nullable: false)
                        .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn),
                    dependency_id = table.Column<string>(nullable: true),
                    range = table.Column<string>(nullable: true),
                    package_dependency_group_id = table.Column<int>(nullable: true)
                },
                constraints: table =>
                {
                    table.PrimaryKey("pk_package_dependency", x => x.id);
                    table.ForeignKey(
                        name: "fk_package_dependency_package_dependency_group_package_dependenc~",
                        column: x => x.package_dependency_group_id,
                        principalTable: "package_dependency_group",
                        principalColumn: "id",
                        onDelete: ReferentialAction.Cascade);
                });

            migrationBuilder.Sql(@"CREATE VIEW pending_packages_daily_downloads AS
SELECT l.package_id FROM PACKAGE_DETAILS_CATALOG_LEAFS l
LEFT OUTER JOIN DAILY_DOWNLOADS d
ON l.package_id = d.package_id AND d.date = DATE_TRUNC('day', now())
WHERE d.package_id IS NULL
GROUP BY l.package_id");

            migrationBuilder.InsertData(
                table: "cursors",
                columns: new[] { "id", "value" },
                values: new object[] { "Catalog", new DateTimeOffset(new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)) });

            migrationBuilder.CreateIndex(
                name: "IX_package_dependency_dependency_id",
                table: "package_dependency",
                column: "dependency_id");

            migrationBuilder.CreateIndex(
                name: "ix_package_dependency_package_dependency_group_id",
                table: "package_dependency",
                column: "package_dependency_group_id");

            migrationBuilder.CreateIndex(
                name: "ix_package_dependency_group_package_details_catalog_leaf_id",
                table: "package_dependency_group",
                column: "package_details_catalog_leaf_id");

            migrationBuilder.CreateIndex(
                name: "IX_package_details_catalog_leafs_package_id",
                table: "package_details_catalog_leafs",
                column: "package_id");

            migrationBuilder.CreateIndex(
                name: "IX_package_details_catalog_leafs_package_id_package_version",
                table: "package_details_catalog_leafs",
                columns: new[] { "package_id", "package_version" },
                unique: true);

            migrationBuilder.CreateIndex(
                name: "IX_package_downloads_package_id_lowered",
                table: "package_downloads",
                column: "package_id_lowered",
                unique: true);
        }

        protected override void Down(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.Sql(@"DROP VIEW pending_packages_daily_downloads");

            migrationBuilder.DropTable(
                name: "cursors");

            migrationBuilder.DropTable(
                name: "daily_downloads");

            migrationBuilder.DropTable(
                name: "package_dependency");

            migrationBuilder.DropTable(
                name: "package_downloads");

            migrationBuilder.DropTable(
                name: "package_dependency_group");

            migrationBuilder.DropTable(
                name: "package_details_catalog_leafs");
        }
    }
}


<|start_filename|>src/NuGetTrends.Scheduler/Dockerfile<|end_filename|>
FROM mcr.microsoft.com/dotnet/aspnet:5.0
COPY publish/ App/

WORKDIR /App
ENTRYPOINT ["dotnet", "NuGetTrends.Scheduler.dll"]


<|start_filename|>src/NuGetTrends.Web/Dockerfile<|end_filename|>
FROM mcr.microsoft.com/dotnet/aspnet:5.0
COPY publish/ App/

WORKDIR /App
ENTRYPOINT ["dotnet", "NuGetTrends.Web.dll"]


<|start_filename|>src/NuGetTrends.Scheduler/NuGetSearchService.cs<|end_filename|>
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using NuGet.Configuration;
using NuGet.Protocol.Core.Types;
using ILogger = NuGet.Common.ILogger;

namespace NuGetTrends.Scheduler
{
    public class NuGetSearchService : INuGetSearchService
    {
        private readonly ILogger<NuGetSearchService> _logger;

        private static readonly ILogger NugetLogger = new NuGet.Common.NullLogger();
        private static readonly SearchFilter SearchFilter = new SearchFilter(true);

        private volatile PackageSearchResource? _packageSearchResource;

        private readonly SourceRepository _sourceRepository = new SourceRepository(
            new PackageSource("https://api.nuget.org/v3/index.json"),
            Repository.Provider.GetCoreV3());

        public NuGetSearchService(ILogger<NuGetSearchService> logger) => _logger = logger;

        /// <summary>
        /// Gets the package info
        /// </summary>
        /// <param name="packageId"></param>
        /// <param name="token"></param>
        /// <returns></returns>
        public async Task<IPackageSearchMetadata?> GetPackage(string packageId, CancellationToken token)
        {
            if (_packageSearchResource == null)
            {
                // Yeah, it could get called it more than once
                _packageSearchResource = await _sourceRepository.GetResourceAsync<PackageSearchResource>(token);
            }

            try
            {
                // Search doesn't return matching id as the first result. MySqlConnector was the 7th, for example.
                var package = (await _packageSearchResource.SearchAsync($"packageid:{packageId}", SearchFilter, 0, 1, NugetLogger, token))
                    .FirstOrDefault(p => p.Identity?.Id == packageId);

                if (package == null)
                {
                    _logger.LogDebug("Package {packageId} not found.", packageId);
                }

                return package;

            }
            catch (Exception e)
            {
                e.Data["PackageId"] = packageId;
                throw;
            }
        }
    }
}


<|start_filename|>src/NuGet.Protocol.Catalog/Serialization/BaseCatalogLeafConverter.cs<|end_filename|>
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using NuGet.Protocol.Catalog.Models;

namespace NuGet.Protocol.Catalog.Serialization
{
    internal abstract class BaseCatalogLeafConverter : JsonConverter
    {
        private readonly IReadOnlyDictionary<CatalogLeafType, string> _fromType;

        protected BaseCatalogLeafConverter(IReadOnlyDictionary<CatalogLeafType, string> fromType) => _fromType = fromType;

        public override bool CanConvert(Type objectType) => objectType == typeof(CatalogLeafType);

        public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
        {
            if (value is {} && _fromType.TryGetValue((CatalogLeafType)value, out var output))
            {
                writer.WriteValue(output);
            }

            throw new NotSupportedException($"The catalog leaf type '{value}' is not supported.");
        }
    }
}


<|start_filename|>src/NuGetTrends.Data/DailyDownloadResult.cs<|end_filename|>
using System;
using System.ComponentModel.DataAnnotations.Schema;

namespace NuGetTrends.Data
{
    // ReSharper disable once ClassNeverInstantiated.Global - EF
    public class DailyDownloadResult
    {
        [Column("download_count")]
        public long? Count { get; set; }

        [Column("week")]
        public DateTime Week { get; set; }
    }
}


<|start_filename|>src/NuGet.Protocol.Catalog/Serialization/CatalogJsonSerialization.cs<|end_filename|>
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using Newtonsoft.Json;

namespace NuGet.Protocol.Catalog.Serialization
{
    internal static class CatalogJsonSerialization
    {
        public static JsonSerializer Serializer => JsonSerializer.Create(Settings);

        private static JsonSerializerSettings Settings => new JsonSerializerSettings
        {
            DateTimeZoneHandling = DateTimeZoneHandling.Utc,
            DateParseHandling = DateParseHandling.DateTimeOffset,
            NullValueHandling = NullValueHandling.Ignore,
        };
    }
}


<|start_filename|>src/NuGet.Protocol.Catalog/Models/PackageDetailsCatalogLeaf.cs<|end_filename|>
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using Newtonsoft.Json;

namespace NuGet.Protocol.Catalog.Models
{
    public class PackageDetailsCatalogLeaf : CatalogLeaf
    {
        public int Id { get; set; }

        [JsonProperty("authors")]
        public string? Authors { get; set; }

        [JsonProperty("created")]
        public DateTimeOffset Created { get; set; }

        [JsonProperty("lastEdited")]
        public DateTimeOffset LastEdited { get; set; }

        [JsonProperty("dependencyGroups")]
        public List<PackageDependencyGroup> DependencyGroups { get; set; } = new List<PackageDependencyGroup>(0);

        [JsonProperty("description")]
        public string? Description { get; set; }

        [JsonProperty("iconUrl")]
        public string? IconUrl { get; set; }

        [JsonProperty("isPrerelease")]
        public bool IsPrerelease { get; set; }

        [JsonProperty("language")]
        public string? Language { get; set; }

        [JsonProperty("licenseUrl")]
        public string? LicenseUrl { get; set; }

        [JsonProperty("listed")]
        public bool? Listed { get; set; }

        [JsonProperty("minClientVersion")]
        public string? MinClientVersion { get; set; }

        [JsonProperty("packageHash")]
        public string? PackageHash { get; set; }

        [JsonProperty("packageHashAlgorithm")]
        public string? PackageHashAlgorithm { get; set; }

        [JsonProperty("packageSize")]
        public long PackageSize { get; set; }

        [JsonProperty("projectUrl")]
        public string? ProjectUrl { get; set; }

        [JsonProperty("releaseNotes")]
        public string? ReleaseNotes { get; set; }

        [JsonProperty("requireLicenseAgreement")]
        public bool? RequireLicenseAgreement { get; set; }

        [JsonProperty("summary")]
        public string? Summary { get; set; }

        [JsonProperty("tags")]
        public List<string>? Tags { get; set; } = new List<string>(0);

        [JsonProperty("title")]
        public string? Title { get; set; }

        [JsonProperty("verbatimVersion")]
        public string? VerbatimVersion { get; set; }
    }
}


<|start_filename|>src/NuGet.Protocol.Catalog/Models/ICatalogLeafItem.cs<|end_filename|>
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;

namespace NuGet.Protocol.Catalog.Models
{
    public interface ICatalogLeafItem
    {
        DateTimeOffset CommitTimestamp { get; }
        string? PackageId { get; }
        string? PackageVersion { get; }
        CatalogLeafType Type { get; }
    }
}
