feat: song likes and initial implementation of Spotify playlist support, refs #9
This commit is contained in:
		@@ -12,9 +12,10 @@ public class DataContext : DbContext
 | 
				
			|||||||
    public DbSet<Song>? Songs { get; set; }
 | 
					    public DbSet<Song>? Songs { get; set; }
 | 
				
			||||||
    public DbSet<SongSuggestion>? SongSuggestions { get; set; }
 | 
					    public DbSet<SongSuggestion>? SongSuggestions { get; set; }
 | 
				
			||||||
    public DbSet<SuggestionHelper>? SuggestionHelpers { get; set; }
 | 
					    public DbSet<SuggestionHelper>? SuggestionHelpers { get; set; }
 | 
				
			||||||
 | 
					    public DbSet<SmartPlaylistDefinition>? SmartPlaylistDefinitions { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
 | 
					    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
 | 
				
			||||||
        => optionsBuilder.UseNpgsql($"Host={AppConfiguration.Instance.DatabaseUri}:{AppConfiguration.Instance.DatabasePort};"
 | 
					        => optionsBuilder.UseNpgsql($"Host={AppConfiguration.Instance.DatabaseUri}:{AppConfiguration.Instance.DatabasePort};"
 | 
				
			||||||
                                    + $"Username={AppConfiguration.Instance.DatabaseUser};Password={AppConfiguration.Instance.DatabasePW};"
 | 
					                                    + $"Username={AppConfiguration.Instance.DatabaseUser};Password={AppConfiguration.Instance.DatabasePW};"
 | 
				
			||||||
                                    + $"Database={AppConfiguration.Instance.DatabaseName}");
 | 
					                                    + $"Database={AppConfiguration.Instance.DatabaseName}");    
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
							
								
								
									
										268
									
								
								song_of_the_day/Data/Migrations/20250719185147_adding song likes and playlists.Designer.cs
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										268
									
								
								song_of_the_day/Data/Migrations/20250719185147_adding song likes and playlists.Designer.cs
									
									
									
										generated
									
									
									
										Normal file
									
								
							@@ -0,0 +1,268 @@
 | 
				
			|||||||
 | 
					// <auto-generated />
 | 
				
			||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using Microsoft.EntityFrameworkCore;
 | 
				
			||||||
 | 
					using Microsoft.EntityFrameworkCore.Infrastructure;
 | 
				
			||||||
 | 
					using Microsoft.EntityFrameworkCore.Migrations;
 | 
				
			||||||
 | 
					using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
 | 
				
			||||||
 | 
					using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#nullable disable
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace song_of_the_day.DataMigrations
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    [DbContext(typeof(DataContext))]
 | 
				
			||||||
 | 
					    [Migration("20250719185147_adding song likes and playlists")]
 | 
				
			||||||
 | 
					    partial class addingsonglikesandplaylists
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        /// <inheritdoc />
 | 
				
			||||||
 | 
					        protected override void BuildTargetModel(ModelBuilder modelBuilder)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					#pragma warning disable 612, 618
 | 
				
			||||||
 | 
					            modelBuilder
 | 
				
			||||||
 | 
					                .HasAnnotation("ProductVersion", "9.0.3")
 | 
				
			||||||
 | 
					                .HasAnnotation("Relational:MaxIdentifierLength", 63);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            modelBuilder.Entity("SmartPlaylistDefinition", b =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    b.Property<int>("Id")
 | 
				
			||||||
 | 
					                        .ValueGeneratedOnAdd()
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<int?>("CreatedByUserId")
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("Description")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<bool>("IncludesLikedSongs")
 | 
				
			||||||
 | 
					                        .HasColumnType("boolean");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<bool>("IncludesUnCategorizedSongs")
 | 
				
			||||||
 | 
					                        .HasColumnType("boolean");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("SpotifyPlaylistId")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("Title")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasKey("Id");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasIndex("CreatedByUserId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.ToTable("SmartPlaylistDefinitions");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            modelBuilder.Entity("Song", b =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    b.Property<int>("SongId")
 | 
				
			||||||
 | 
					                        .ValueGeneratedOnAdd()
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("SongId"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("Artist")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("Name")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<int?>("Provider")
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<int?>("SmartPlaylistDefinitionId")
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<int?>("SmartPlaylistDefinitionId1")
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("SpotifyId")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("Url")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<int?>("UserId")
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasKey("SongId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasIndex("SmartPlaylistDefinitionId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasIndex("SmartPlaylistDefinitionId1");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasIndex("UserId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.ToTable("Songs");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            modelBuilder.Entity("SongSuggestion", b =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    b.Property<int>("Id")
 | 
				
			||||||
 | 
					                        .ValueGeneratedOnAdd()
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<DateTime>("Date")
 | 
				
			||||||
 | 
					                        .HasColumnType("timestamp with time zone");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<bool>("HasUsedSuggestion")
 | 
				
			||||||
 | 
					                        .HasColumnType("boolean");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<int?>("SongId")
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<int>("SuggestionHelperId")
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<bool>("UserHasSubmitted")
 | 
				
			||||||
 | 
					                        .HasColumnType("boolean");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<int?>("UserId")
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasKey("Id");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasIndex("SongId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasIndex("SuggestionHelperId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasIndex("UserId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.ToTable("SongSuggestions");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            modelBuilder.Entity("SuggestionHelper", b =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    b.Property<int>("Id")
 | 
				
			||||||
 | 
					                        .ValueGeneratedOnAdd()
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("Description")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<int?>("SmartPlaylistDefinitionId")
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("Title")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasKey("Id");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasIndex("SmartPlaylistDefinitionId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.ToTable("SuggestionHelpers");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            modelBuilder.Entity("User", b =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    b.Property<int>("UserId")
 | 
				
			||||||
 | 
					                        .ValueGeneratedOnAdd()
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("UserId"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<bool>("AssociationInProgress")
 | 
				
			||||||
 | 
					                        .HasColumnType("boolean");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<bool>("IsIntroduced")
 | 
				
			||||||
 | 
					                        .HasColumnType("boolean");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("LdapUserName")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("Name")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("NickName")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("SignalMemberId")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<bool>("WasChosenForSuggestionThisRound")
 | 
				
			||||||
 | 
					                        .HasColumnType("boolean");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasKey("UserId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.ToTable("Users");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            modelBuilder.Entity("SmartPlaylistDefinition", b =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    b.HasOne("User", "CreatedBy")
 | 
				
			||||||
 | 
					                        .WithMany()
 | 
				
			||||||
 | 
					                        .HasForeignKey("CreatedByUserId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Navigation("CreatedBy");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            modelBuilder.Entity("Song", b =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    b.HasOne("SmartPlaylistDefinition", null)
 | 
				
			||||||
 | 
					                        .WithMany("ExplicitlyExcludedSongs")
 | 
				
			||||||
 | 
					                        .HasForeignKey("SmartPlaylistDefinitionId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasOne("SmartPlaylistDefinition", null)
 | 
				
			||||||
 | 
					                        .WithMany("ExplicitlyIncludedSongs")
 | 
				
			||||||
 | 
					                        .HasForeignKey("SmartPlaylistDefinitionId1");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasOne("User", null)
 | 
				
			||||||
 | 
					                        .WithMany("LikedSongs")
 | 
				
			||||||
 | 
					                        .HasForeignKey("UserId");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            modelBuilder.Entity("SongSuggestion", b =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    b.HasOne("Song", "Song")
 | 
				
			||||||
 | 
					                        .WithMany()
 | 
				
			||||||
 | 
					                        .HasForeignKey("SongId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasOne("SuggestionHelper", "SuggestionHelper")
 | 
				
			||||||
 | 
					                        .WithMany()
 | 
				
			||||||
 | 
					                        .HasForeignKey("SuggestionHelperId")
 | 
				
			||||||
 | 
					                        .OnDelete(DeleteBehavior.Cascade)
 | 
				
			||||||
 | 
					                        .IsRequired();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasOne("User", "User")
 | 
				
			||||||
 | 
					                        .WithMany()
 | 
				
			||||||
 | 
					                        .HasForeignKey("UserId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Navigation("Song");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Navigation("SuggestionHelper");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Navigation("User");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            modelBuilder.Entity("SuggestionHelper", b =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    b.HasOne("SmartPlaylistDefinition", null)
 | 
				
			||||||
 | 
					                        .WithMany("Categories")
 | 
				
			||||||
 | 
					                        .HasForeignKey("SmartPlaylistDefinitionId");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            modelBuilder.Entity("SmartPlaylistDefinition", b =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    b.Navigation("Categories");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Navigation("ExplicitlyExcludedSongs");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Navigation("ExplicitlyIncludedSongs");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            modelBuilder.Entity("User", b =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    b.Navigation("LikedSongs");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					#pragma warning restore 612, 618
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,170 @@
 | 
				
			|||||||
 | 
					using Microsoft.EntityFrameworkCore.Migrations;
 | 
				
			||||||
 | 
					using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#nullable disable
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace song_of_the_day.DataMigrations
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /// <inheritdoc />
 | 
				
			||||||
 | 
					    public partial class addingsonglikesandplaylists : Migration
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        /// <inheritdoc />
 | 
				
			||||||
 | 
					        protected override void Up(MigrationBuilder migrationBuilder)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            migrationBuilder.AddColumn<int>(
 | 
				
			||||||
 | 
					                name: "SmartPlaylistDefinitionId",
 | 
				
			||||||
 | 
					                table: "SuggestionHelpers",
 | 
				
			||||||
 | 
					                type: "integer",
 | 
				
			||||||
 | 
					                nullable: true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            migrationBuilder.AddColumn<int>(
 | 
				
			||||||
 | 
					                name: "SmartPlaylistDefinitionId",
 | 
				
			||||||
 | 
					                table: "Songs",
 | 
				
			||||||
 | 
					                type: "integer",
 | 
				
			||||||
 | 
					                nullable: true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            migrationBuilder.AddColumn<int>(
 | 
				
			||||||
 | 
					                name: "SmartPlaylistDefinitionId1",
 | 
				
			||||||
 | 
					                table: "Songs",
 | 
				
			||||||
 | 
					                type: "integer",
 | 
				
			||||||
 | 
					                nullable: true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            migrationBuilder.AddColumn<int>(
 | 
				
			||||||
 | 
					                name: "UserId",
 | 
				
			||||||
 | 
					                table: "Songs",
 | 
				
			||||||
 | 
					                type: "integer",
 | 
				
			||||||
 | 
					                nullable: true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            migrationBuilder.CreateTable(
 | 
				
			||||||
 | 
					                name: "SmartPlaylistDefinitions",
 | 
				
			||||||
 | 
					                columns: table => new
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    Id = table.Column<int>(type: "integer", nullable: false)
 | 
				
			||||||
 | 
					                        .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
 | 
				
			||||||
 | 
					                    Title = table.Column<string>(type: "text", nullable: true),
 | 
				
			||||||
 | 
					                    Description = table.Column<string>(type: "text", nullable: true),
 | 
				
			||||||
 | 
					                    CreatedByUserId = table.Column<int>(type: "integer", nullable: true),
 | 
				
			||||||
 | 
					                    IncludesUnCategorizedSongs = table.Column<bool>(type: "boolean", nullable: false),
 | 
				
			||||||
 | 
					                    IncludesLikedSongs = table.Column<bool>(type: "boolean", nullable: false),
 | 
				
			||||||
 | 
					                    SpotifyPlaylistId = table.Column<string>(type: "text", nullable: true)
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                constraints: table =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    table.PrimaryKey("PK_SmartPlaylistDefinitions", x => x.Id);
 | 
				
			||||||
 | 
					                    table.ForeignKey(
 | 
				
			||||||
 | 
					                        name: "FK_SmartPlaylistDefinitions_Users_CreatedByUserId",
 | 
				
			||||||
 | 
					                        column: x => x.CreatedByUserId,
 | 
				
			||||||
 | 
					                        principalTable: "Users",
 | 
				
			||||||
 | 
					                        principalColumn: "UserId");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            migrationBuilder.CreateIndex(
 | 
				
			||||||
 | 
					                name: "IX_SuggestionHelpers_SmartPlaylistDefinitionId",
 | 
				
			||||||
 | 
					                table: "SuggestionHelpers",
 | 
				
			||||||
 | 
					                column: "SmartPlaylistDefinitionId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            migrationBuilder.CreateIndex(
 | 
				
			||||||
 | 
					                name: "IX_Songs_SmartPlaylistDefinitionId",
 | 
				
			||||||
 | 
					                table: "Songs",
 | 
				
			||||||
 | 
					                column: "SmartPlaylistDefinitionId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            migrationBuilder.CreateIndex(
 | 
				
			||||||
 | 
					                name: "IX_Songs_SmartPlaylistDefinitionId1",
 | 
				
			||||||
 | 
					                table: "Songs",
 | 
				
			||||||
 | 
					                column: "SmartPlaylistDefinitionId1");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            migrationBuilder.CreateIndex(
 | 
				
			||||||
 | 
					                name: "IX_Songs_UserId",
 | 
				
			||||||
 | 
					                table: "Songs",
 | 
				
			||||||
 | 
					                column: "UserId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            migrationBuilder.CreateIndex(
 | 
				
			||||||
 | 
					                name: "IX_SmartPlaylistDefinitions_CreatedByUserId",
 | 
				
			||||||
 | 
					                table: "SmartPlaylistDefinitions",
 | 
				
			||||||
 | 
					                column: "CreatedByUserId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            migrationBuilder.AddForeignKey(
 | 
				
			||||||
 | 
					                name: "FK_Songs_SmartPlaylistDefinitions_SmartPlaylistDefinitionId",
 | 
				
			||||||
 | 
					                table: "Songs",
 | 
				
			||||||
 | 
					                column: "SmartPlaylistDefinitionId",
 | 
				
			||||||
 | 
					                principalTable: "SmartPlaylistDefinitions",
 | 
				
			||||||
 | 
					                principalColumn: "Id");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            migrationBuilder.AddForeignKey(
 | 
				
			||||||
 | 
					                name: "FK_Songs_SmartPlaylistDefinitions_SmartPlaylistDefinitionId1",
 | 
				
			||||||
 | 
					                table: "Songs",
 | 
				
			||||||
 | 
					                column: "SmartPlaylistDefinitionId1",
 | 
				
			||||||
 | 
					                principalTable: "SmartPlaylistDefinitions",
 | 
				
			||||||
 | 
					                principalColumn: "Id");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            migrationBuilder.AddForeignKey(
 | 
				
			||||||
 | 
					                name: "FK_Songs_Users_UserId",
 | 
				
			||||||
 | 
					                table: "Songs",
 | 
				
			||||||
 | 
					                column: "UserId",
 | 
				
			||||||
 | 
					                principalTable: "Users",
 | 
				
			||||||
 | 
					                principalColumn: "UserId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            migrationBuilder.AddForeignKey(
 | 
				
			||||||
 | 
					                name: "FK_SuggestionHelpers_SmartPlaylistDefinitions_SmartPlaylistDef~",
 | 
				
			||||||
 | 
					                table: "SuggestionHelpers",
 | 
				
			||||||
 | 
					                column: "SmartPlaylistDefinitionId",
 | 
				
			||||||
 | 
					                principalTable: "SmartPlaylistDefinitions",
 | 
				
			||||||
 | 
					                principalColumn: "Id");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// <inheritdoc />
 | 
				
			||||||
 | 
					        protected override void Down(MigrationBuilder migrationBuilder)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            migrationBuilder.DropForeignKey(
 | 
				
			||||||
 | 
					                name: "FK_Songs_SmartPlaylistDefinitions_SmartPlaylistDefinitionId",
 | 
				
			||||||
 | 
					                table: "Songs");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            migrationBuilder.DropForeignKey(
 | 
				
			||||||
 | 
					                name: "FK_Songs_SmartPlaylistDefinitions_SmartPlaylistDefinitionId1",
 | 
				
			||||||
 | 
					                table: "Songs");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            migrationBuilder.DropForeignKey(
 | 
				
			||||||
 | 
					                name: "FK_Songs_Users_UserId",
 | 
				
			||||||
 | 
					                table: "Songs");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            migrationBuilder.DropForeignKey(
 | 
				
			||||||
 | 
					                name: "FK_SuggestionHelpers_SmartPlaylistDefinitions_SmartPlaylistDef~",
 | 
				
			||||||
 | 
					                table: "SuggestionHelpers");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            migrationBuilder.DropTable(
 | 
				
			||||||
 | 
					                name: "SmartPlaylistDefinitions");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            migrationBuilder.DropIndex(
 | 
				
			||||||
 | 
					                name: "IX_SuggestionHelpers_SmartPlaylistDefinitionId",
 | 
				
			||||||
 | 
					                table: "SuggestionHelpers");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            migrationBuilder.DropIndex(
 | 
				
			||||||
 | 
					                name: "IX_Songs_SmartPlaylistDefinitionId",
 | 
				
			||||||
 | 
					                table: "Songs");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            migrationBuilder.DropIndex(
 | 
				
			||||||
 | 
					                name: "IX_Songs_SmartPlaylistDefinitionId1",
 | 
				
			||||||
 | 
					                table: "Songs");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            migrationBuilder.DropIndex(
 | 
				
			||||||
 | 
					                name: "IX_Songs_UserId",
 | 
				
			||||||
 | 
					                table: "Songs");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            migrationBuilder.DropColumn(
 | 
				
			||||||
 | 
					                name: "SmartPlaylistDefinitionId",
 | 
				
			||||||
 | 
					                table: "SuggestionHelpers");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            migrationBuilder.DropColumn(
 | 
				
			||||||
 | 
					                name: "SmartPlaylistDefinitionId",
 | 
				
			||||||
 | 
					                table: "Songs");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            migrationBuilder.DropColumn(
 | 
				
			||||||
 | 
					                name: "SmartPlaylistDefinitionId1",
 | 
				
			||||||
 | 
					                table: "Songs");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            migrationBuilder.DropColumn(
 | 
				
			||||||
 | 
					                name: "UserId",
 | 
				
			||||||
 | 
					                table: "Songs");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,282 @@
 | 
				
			|||||||
 | 
					// <auto-generated />
 | 
				
			||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using Microsoft.EntityFrameworkCore;
 | 
				
			||||||
 | 
					using Microsoft.EntityFrameworkCore.Infrastructure;
 | 
				
			||||||
 | 
					using Microsoft.EntityFrameworkCore.Migrations;
 | 
				
			||||||
 | 
					using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
 | 
				
			||||||
 | 
					using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#nullable disable
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace song_of_the_day.DataMigrations
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    [DbContext(typeof(DataContext))]
 | 
				
			||||||
 | 
					    [Migration("20250719222759_fix issues with Spotify session data for users")]
 | 
				
			||||||
 | 
					    partial class fixissueswithSpotifysessiondataforusers
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        /// <inheritdoc />
 | 
				
			||||||
 | 
					        protected override void BuildTargetModel(ModelBuilder modelBuilder)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					#pragma warning disable 612, 618
 | 
				
			||||||
 | 
					            modelBuilder
 | 
				
			||||||
 | 
					                .HasAnnotation("ProductVersion", "9.0.3")
 | 
				
			||||||
 | 
					                .HasAnnotation("Relational:MaxIdentifierLength", 63);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            modelBuilder.Entity("SmartPlaylistDefinition", b =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    b.Property<int>("Id")
 | 
				
			||||||
 | 
					                        .ValueGeneratedOnAdd()
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<int?>("CreatedByUserId")
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("Description")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<bool>("IncludesLikedSongs")
 | 
				
			||||||
 | 
					                        .HasColumnType("boolean");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<bool>("IncludesUnCategorizedSongs")
 | 
				
			||||||
 | 
					                        .HasColumnType("boolean");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("SpotifyPlaylistId")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("Title")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasKey("Id");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasIndex("CreatedByUserId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.ToTable("SmartPlaylistDefinitions");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            modelBuilder.Entity("Song", b =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    b.Property<int>("SongId")
 | 
				
			||||||
 | 
					                        .ValueGeneratedOnAdd()
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("SongId"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("Artist")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("Name")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<int?>("Provider")
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<int?>("SmartPlaylistDefinitionId")
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<int?>("SmartPlaylistDefinitionId1")
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("SpotifyId")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("Url")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<int?>("UserId")
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasKey("SongId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasIndex("SmartPlaylistDefinitionId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasIndex("SmartPlaylistDefinitionId1");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasIndex("UserId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.ToTable("Songs");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            modelBuilder.Entity("SongSuggestion", b =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    b.Property<int>("Id")
 | 
				
			||||||
 | 
					                        .ValueGeneratedOnAdd()
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<DateTime>("Date")
 | 
				
			||||||
 | 
					                        .HasColumnType("timestamp with time zone");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<bool>("HasUsedSuggestion")
 | 
				
			||||||
 | 
					                        .HasColumnType("boolean");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<int?>("SongId")
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<int>("SuggestionHelperId")
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<bool>("UserHasSubmitted")
 | 
				
			||||||
 | 
					                        .HasColumnType("boolean");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<int?>("UserId")
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasKey("Id");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasIndex("SongId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasIndex("SuggestionHelperId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasIndex("UserId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.ToTable("SongSuggestions");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            modelBuilder.Entity("SuggestionHelper", b =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    b.Property<int>("Id")
 | 
				
			||||||
 | 
					                        .ValueGeneratedOnAdd()
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("Description")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<int?>("SmartPlaylistDefinitionId")
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("Title")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasKey("Id");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasIndex("SmartPlaylistDefinitionId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.ToTable("SuggestionHelpers");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            modelBuilder.Entity("User", b =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    b.Property<int>("UserId")
 | 
				
			||||||
 | 
					                        .ValueGeneratedOnAdd()
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("UserId"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<bool>("AssociationInProgress")
 | 
				
			||||||
 | 
					                        .HasColumnType("boolean");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<bool>("IsIntroduced")
 | 
				
			||||||
 | 
					                        .HasColumnType("boolean");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("LdapUserName")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("Name")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("NickName")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("SignalMemberId")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("SpotiyAuthAccessToken")
 | 
				
			||||||
 | 
					                        .IsRequired()
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<DateTime>("SpotiyAuthCreatedAt")
 | 
				
			||||||
 | 
					                        .HasColumnType("timestamp with time zone");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<int>("SpotiyAuthExpiresAfterSeconds")
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("SpotiyAuthRefreshToken")
 | 
				
			||||||
 | 
					                        .IsRequired()
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<bool>("WasChosenForSuggestionThisRound")
 | 
				
			||||||
 | 
					                        .HasColumnType("boolean");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasKey("UserId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.ToTable("Users");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            modelBuilder.Entity("SmartPlaylistDefinition", b =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    b.HasOne("User", "CreatedBy")
 | 
				
			||||||
 | 
					                        .WithMany()
 | 
				
			||||||
 | 
					                        .HasForeignKey("CreatedByUserId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Navigation("CreatedBy");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            modelBuilder.Entity("Song", b =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    b.HasOne("SmartPlaylistDefinition", null)
 | 
				
			||||||
 | 
					                        .WithMany("ExplicitlyExcludedSongs")
 | 
				
			||||||
 | 
					                        .HasForeignKey("SmartPlaylistDefinitionId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasOne("SmartPlaylistDefinition", null)
 | 
				
			||||||
 | 
					                        .WithMany("ExplicitlyIncludedSongs")
 | 
				
			||||||
 | 
					                        .HasForeignKey("SmartPlaylistDefinitionId1");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasOne("User", null)
 | 
				
			||||||
 | 
					                        .WithMany("LikedSongs")
 | 
				
			||||||
 | 
					                        .HasForeignKey("UserId");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            modelBuilder.Entity("SongSuggestion", b =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    b.HasOne("Song", "Song")
 | 
				
			||||||
 | 
					                        .WithMany()
 | 
				
			||||||
 | 
					                        .HasForeignKey("SongId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasOne("SuggestionHelper", "SuggestionHelper")
 | 
				
			||||||
 | 
					                        .WithMany()
 | 
				
			||||||
 | 
					                        .HasForeignKey("SuggestionHelperId")
 | 
				
			||||||
 | 
					                        .OnDelete(DeleteBehavior.Cascade)
 | 
				
			||||||
 | 
					                        .IsRequired();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasOne("User", "User")
 | 
				
			||||||
 | 
					                        .WithMany()
 | 
				
			||||||
 | 
					                        .HasForeignKey("UserId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Navigation("Song");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Navigation("SuggestionHelper");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Navigation("User");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            modelBuilder.Entity("SuggestionHelper", b =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    b.HasOne("SmartPlaylistDefinition", null)
 | 
				
			||||||
 | 
					                        .WithMany("Categories")
 | 
				
			||||||
 | 
					                        .HasForeignKey("SmartPlaylistDefinitionId");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            modelBuilder.Entity("SmartPlaylistDefinition", b =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    b.Navigation("Categories");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Navigation("ExplicitlyExcludedSongs");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Navigation("ExplicitlyIncludedSongs");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            modelBuilder.Entity("User", b =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    b.Navigation("LikedSongs");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					#pragma warning restore 612, 618
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,63 @@
 | 
				
			|||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using Microsoft.EntityFrameworkCore.Migrations;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#nullable disable
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace song_of_the_day.DataMigrations
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /// <inheritdoc />
 | 
				
			||||||
 | 
					    public partial class fixissueswithSpotifysessiondataforusers : Migration
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        /// <inheritdoc />
 | 
				
			||||||
 | 
					        protected override void Up(MigrationBuilder migrationBuilder)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            migrationBuilder.AddColumn<string>(
 | 
				
			||||||
 | 
					                name: "SpotiyAuthAccessToken",
 | 
				
			||||||
 | 
					                table: "Users",
 | 
				
			||||||
 | 
					                type: "text",
 | 
				
			||||||
 | 
					                nullable: false,
 | 
				
			||||||
 | 
					                defaultValue: "");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            migrationBuilder.AddColumn<DateTime>(
 | 
				
			||||||
 | 
					                name: "SpotiyAuthCreatedAt",
 | 
				
			||||||
 | 
					                table: "Users",
 | 
				
			||||||
 | 
					                type: "timestamp with time zone",
 | 
				
			||||||
 | 
					                nullable: false,
 | 
				
			||||||
 | 
					                defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            migrationBuilder.AddColumn<int>(
 | 
				
			||||||
 | 
					                name: "SpotiyAuthExpiresAfterSeconds",
 | 
				
			||||||
 | 
					                table: "Users",
 | 
				
			||||||
 | 
					                type: "integer",
 | 
				
			||||||
 | 
					                nullable: false,
 | 
				
			||||||
 | 
					                defaultValue: 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            migrationBuilder.AddColumn<string>(
 | 
				
			||||||
 | 
					                name: "SpotiyAuthRefreshToken",
 | 
				
			||||||
 | 
					                table: "Users",
 | 
				
			||||||
 | 
					                type: "text",
 | 
				
			||||||
 | 
					                nullable: false,
 | 
				
			||||||
 | 
					                defaultValue: "");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// <inheritdoc />
 | 
				
			||||||
 | 
					        protected override void Down(MigrationBuilder migrationBuilder)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            migrationBuilder.DropColumn(
 | 
				
			||||||
 | 
					                name: "SpotiyAuthAccessToken",
 | 
				
			||||||
 | 
					                table: "Users");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            migrationBuilder.DropColumn(
 | 
				
			||||||
 | 
					                name: "SpotiyAuthCreatedAt",
 | 
				
			||||||
 | 
					                table: "Users");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            migrationBuilder.DropColumn(
 | 
				
			||||||
 | 
					                name: "SpotiyAuthExpiresAfterSeconds",
 | 
				
			||||||
 | 
					                table: "Users");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            migrationBuilder.DropColumn(
 | 
				
			||||||
 | 
					                name: "SpotiyAuthRefreshToken",
 | 
				
			||||||
 | 
					                table: "Users");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										282
									
								
								song_of_the_day/Data/Migrations/20250719230009_some more fixes.Designer.cs
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										282
									
								
								song_of_the_day/Data/Migrations/20250719230009_some more fixes.Designer.cs
									
									
									
										generated
									
									
									
										Normal file
									
								
							@@ -0,0 +1,282 @@
 | 
				
			|||||||
 | 
					// <auto-generated />
 | 
				
			||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using Microsoft.EntityFrameworkCore;
 | 
				
			||||||
 | 
					using Microsoft.EntityFrameworkCore.Infrastructure;
 | 
				
			||||||
 | 
					using Microsoft.EntityFrameworkCore.Migrations;
 | 
				
			||||||
 | 
					using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
 | 
				
			||||||
 | 
					using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#nullable disable
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace song_of_the_day.DataMigrations
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    [DbContext(typeof(DataContext))]
 | 
				
			||||||
 | 
					    [Migration("20250719230009_some more fixes")]
 | 
				
			||||||
 | 
					    partial class somemorefixes
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        /// <inheritdoc />
 | 
				
			||||||
 | 
					        protected override void BuildTargetModel(ModelBuilder modelBuilder)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					#pragma warning disable 612, 618
 | 
				
			||||||
 | 
					            modelBuilder
 | 
				
			||||||
 | 
					                .HasAnnotation("ProductVersion", "9.0.3")
 | 
				
			||||||
 | 
					                .HasAnnotation("Relational:MaxIdentifierLength", 63);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            modelBuilder.Entity("SmartPlaylistDefinition", b =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    b.Property<int>("Id")
 | 
				
			||||||
 | 
					                        .ValueGeneratedOnAdd()
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<int?>("CreatedByUserId")
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("Description")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<bool>("IncludesLikedSongs")
 | 
				
			||||||
 | 
					                        .HasColumnType("boolean");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<bool>("IncludesUnCategorizedSongs")
 | 
				
			||||||
 | 
					                        .HasColumnType("boolean");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("SpotifyPlaylistId")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("Title")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasKey("Id");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasIndex("CreatedByUserId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.ToTable("SmartPlaylistDefinitions");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            modelBuilder.Entity("Song", b =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    b.Property<int>("SongId")
 | 
				
			||||||
 | 
					                        .ValueGeneratedOnAdd()
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("SongId"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("Artist")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("Name")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<int?>("Provider")
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<int?>("SmartPlaylistDefinitionId")
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<int?>("SmartPlaylistDefinitionId1")
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("SpotifyId")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("Url")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<int?>("UserId")
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasKey("SongId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasIndex("SmartPlaylistDefinitionId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasIndex("SmartPlaylistDefinitionId1");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasIndex("UserId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.ToTable("Songs");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            modelBuilder.Entity("SongSuggestion", b =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    b.Property<int>("Id")
 | 
				
			||||||
 | 
					                        .ValueGeneratedOnAdd()
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<DateTime>("Date")
 | 
				
			||||||
 | 
					                        .HasColumnType("timestamp with time zone");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<bool>("HasUsedSuggestion")
 | 
				
			||||||
 | 
					                        .HasColumnType("boolean");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<int?>("SongId")
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<int>("SuggestionHelperId")
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<bool>("UserHasSubmitted")
 | 
				
			||||||
 | 
					                        .HasColumnType("boolean");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<int?>("UserId")
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasKey("Id");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasIndex("SongId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasIndex("SuggestionHelperId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasIndex("UserId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.ToTable("SongSuggestions");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            modelBuilder.Entity("SuggestionHelper", b =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    b.Property<int>("Id")
 | 
				
			||||||
 | 
					                        .ValueGeneratedOnAdd()
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("Description")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<int?>("SmartPlaylistDefinitionId")
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("Title")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasKey("Id");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasIndex("SmartPlaylistDefinitionId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.ToTable("SuggestionHelpers");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            modelBuilder.Entity("User", b =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    b.Property<int>("UserId")
 | 
				
			||||||
 | 
					                        .ValueGeneratedOnAdd()
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("UserId"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<bool>("AssociationInProgress")
 | 
				
			||||||
 | 
					                        .HasColumnType("boolean");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<bool>("IsIntroduced")
 | 
				
			||||||
 | 
					                        .HasColumnType("boolean");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("LdapUserName")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("Name")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("NickName")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("SignalMemberId")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("SpotifyAuthAccessToken")
 | 
				
			||||||
 | 
					                        .IsRequired()
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<DateTime>("SpotifyAuthCreatedAt")
 | 
				
			||||||
 | 
					                        .HasColumnType("timestamp with time zone");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<int>("SpotifyAuthExpiresAfterSeconds")
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("SpotifyAuthRefreshToken")
 | 
				
			||||||
 | 
					                        .IsRequired()
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<bool>("WasChosenForSuggestionThisRound")
 | 
				
			||||||
 | 
					                        .HasColumnType("boolean");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasKey("UserId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.ToTable("Users");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            modelBuilder.Entity("SmartPlaylistDefinition", b =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    b.HasOne("User", "CreatedBy")
 | 
				
			||||||
 | 
					                        .WithMany()
 | 
				
			||||||
 | 
					                        .HasForeignKey("CreatedByUserId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Navigation("CreatedBy");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            modelBuilder.Entity("Song", b =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    b.HasOne("SmartPlaylistDefinition", null)
 | 
				
			||||||
 | 
					                        .WithMany("ExplicitlyExcludedSongs")
 | 
				
			||||||
 | 
					                        .HasForeignKey("SmartPlaylistDefinitionId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasOne("SmartPlaylistDefinition", null)
 | 
				
			||||||
 | 
					                        .WithMany("ExplicitlyIncludedSongs")
 | 
				
			||||||
 | 
					                        .HasForeignKey("SmartPlaylistDefinitionId1");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasOne("User", null)
 | 
				
			||||||
 | 
					                        .WithMany("LikedSongs")
 | 
				
			||||||
 | 
					                        .HasForeignKey("UserId");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            modelBuilder.Entity("SongSuggestion", b =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    b.HasOne("Song", "Song")
 | 
				
			||||||
 | 
					                        .WithMany()
 | 
				
			||||||
 | 
					                        .HasForeignKey("SongId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasOne("SuggestionHelper", "SuggestionHelper")
 | 
				
			||||||
 | 
					                        .WithMany()
 | 
				
			||||||
 | 
					                        .HasForeignKey("SuggestionHelperId")
 | 
				
			||||||
 | 
					                        .OnDelete(DeleteBehavior.Cascade)
 | 
				
			||||||
 | 
					                        .IsRequired();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasOne("User", "User")
 | 
				
			||||||
 | 
					                        .WithMany()
 | 
				
			||||||
 | 
					                        .HasForeignKey("UserId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Navigation("Song");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Navigation("SuggestionHelper");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Navigation("User");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            modelBuilder.Entity("SuggestionHelper", b =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    b.HasOne("SmartPlaylistDefinition", null)
 | 
				
			||||||
 | 
					                        .WithMany("Categories")
 | 
				
			||||||
 | 
					                        .HasForeignKey("SmartPlaylistDefinitionId");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            modelBuilder.Entity("SmartPlaylistDefinition", b =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    b.Navigation("Categories");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Navigation("ExplicitlyExcludedSongs");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Navigation("ExplicitlyIncludedSongs");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            modelBuilder.Entity("User", b =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    b.Navigation("LikedSongs");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					#pragma warning restore 612, 618
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,58 @@
 | 
				
			|||||||
 | 
					using Microsoft.EntityFrameworkCore.Migrations;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#nullable disable
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace song_of_the_day.DataMigrations
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /// <inheritdoc />
 | 
				
			||||||
 | 
					    public partial class somemorefixes : Migration
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        /// <inheritdoc />
 | 
				
			||||||
 | 
					        protected override void Up(MigrationBuilder migrationBuilder)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            migrationBuilder.RenameColumn(
 | 
				
			||||||
 | 
					                name: "SpotiyAuthRefreshToken",
 | 
				
			||||||
 | 
					                table: "Users",
 | 
				
			||||||
 | 
					                newName: "SpotifyAuthRefreshToken");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            migrationBuilder.RenameColumn(
 | 
				
			||||||
 | 
					                name: "SpotiyAuthExpiresAfterSeconds",
 | 
				
			||||||
 | 
					                table: "Users",
 | 
				
			||||||
 | 
					                newName: "SpotifyAuthExpiresAfterSeconds");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            migrationBuilder.RenameColumn(
 | 
				
			||||||
 | 
					                name: "SpotiyAuthCreatedAt",
 | 
				
			||||||
 | 
					                table: "Users",
 | 
				
			||||||
 | 
					                newName: "SpotifyAuthCreatedAt");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            migrationBuilder.RenameColumn(
 | 
				
			||||||
 | 
					                name: "SpotiyAuthAccessToken",
 | 
				
			||||||
 | 
					                table: "Users",
 | 
				
			||||||
 | 
					                newName: "SpotifyAuthAccessToken");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// <inheritdoc />
 | 
				
			||||||
 | 
					        protected override void Down(MigrationBuilder migrationBuilder)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            migrationBuilder.RenameColumn(
 | 
				
			||||||
 | 
					                name: "SpotifyAuthRefreshToken",
 | 
				
			||||||
 | 
					                table: "Users",
 | 
				
			||||||
 | 
					                newName: "SpotiyAuthRefreshToken");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            migrationBuilder.RenameColumn(
 | 
				
			||||||
 | 
					                name: "SpotifyAuthExpiresAfterSeconds",
 | 
				
			||||||
 | 
					                table: "Users",
 | 
				
			||||||
 | 
					                newName: "SpotiyAuthExpiresAfterSeconds");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            migrationBuilder.RenameColumn(
 | 
				
			||||||
 | 
					                name: "SpotifyAuthCreatedAt",
 | 
				
			||||||
 | 
					                table: "Users",
 | 
				
			||||||
 | 
					                newName: "SpotiyAuthCreatedAt");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            migrationBuilder.RenameColumn(
 | 
				
			||||||
 | 
					                name: "SpotifyAuthAccessToken",
 | 
				
			||||||
 | 
					                table: "Users",
 | 
				
			||||||
 | 
					                newName: "SpotiyAuthAccessToken");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										282
									
								
								song_of_the_day/Data/Migrations/20250719233912_explicitly specify keys.Designer.cs
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										282
									
								
								song_of_the_day/Data/Migrations/20250719233912_explicitly specify keys.Designer.cs
									
									
									
										generated
									
									
									
										Normal file
									
								
							@@ -0,0 +1,282 @@
 | 
				
			|||||||
 | 
					// <auto-generated />
 | 
				
			||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using Microsoft.EntityFrameworkCore;
 | 
				
			||||||
 | 
					using Microsoft.EntityFrameworkCore.Infrastructure;
 | 
				
			||||||
 | 
					using Microsoft.EntityFrameworkCore.Migrations;
 | 
				
			||||||
 | 
					using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
 | 
				
			||||||
 | 
					using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#nullable disable
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace song_of_the_day.DataMigrations
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    [DbContext(typeof(DataContext))]
 | 
				
			||||||
 | 
					    [Migration("20250719233912_explicitly specify keys")]
 | 
				
			||||||
 | 
					    partial class explicitlyspecifykeys
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        /// <inheritdoc />
 | 
				
			||||||
 | 
					        protected override void BuildTargetModel(ModelBuilder modelBuilder)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					#pragma warning disable 612, 618
 | 
				
			||||||
 | 
					            modelBuilder
 | 
				
			||||||
 | 
					                .HasAnnotation("ProductVersion", "9.0.3")
 | 
				
			||||||
 | 
					                .HasAnnotation("Relational:MaxIdentifierLength", 63);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            modelBuilder.Entity("SmartPlaylistDefinition", b =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    b.Property<int>("Id")
 | 
				
			||||||
 | 
					                        .ValueGeneratedOnAdd()
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<int?>("CreatedByUserId")
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("Description")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<bool>("IncludesLikedSongs")
 | 
				
			||||||
 | 
					                        .HasColumnType("boolean");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<bool>("IncludesUnCategorizedSongs")
 | 
				
			||||||
 | 
					                        .HasColumnType("boolean");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("SpotifyPlaylistId")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("Title")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasKey("Id");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasIndex("CreatedByUserId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.ToTable("SmartPlaylistDefinitions");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            modelBuilder.Entity("Song", b =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    b.Property<int>("SongId")
 | 
				
			||||||
 | 
					                        .ValueGeneratedOnAdd()
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("SongId"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("Artist")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("Name")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<int?>("Provider")
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<int?>("SmartPlaylistDefinitionId")
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<int?>("SmartPlaylistDefinitionId1")
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("SpotifyId")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("Url")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<int?>("UserId")
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasKey("SongId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasIndex("SmartPlaylistDefinitionId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasIndex("SmartPlaylistDefinitionId1");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasIndex("UserId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.ToTable("Songs");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            modelBuilder.Entity("SongSuggestion", b =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    b.Property<int>("Id")
 | 
				
			||||||
 | 
					                        .ValueGeneratedOnAdd()
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<DateTime>("Date")
 | 
				
			||||||
 | 
					                        .HasColumnType("timestamp with time zone");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<bool>("HasUsedSuggestion")
 | 
				
			||||||
 | 
					                        .HasColumnType("boolean");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<int?>("SongId")
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<int>("SuggestionHelperId")
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<bool>("UserHasSubmitted")
 | 
				
			||||||
 | 
					                        .HasColumnType("boolean");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<int?>("UserId")
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasKey("Id");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasIndex("SongId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasIndex("SuggestionHelperId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasIndex("UserId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.ToTable("SongSuggestions");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            modelBuilder.Entity("SuggestionHelper", b =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    b.Property<int>("Id")
 | 
				
			||||||
 | 
					                        .ValueGeneratedOnAdd()
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("Description")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<int?>("SmartPlaylistDefinitionId")
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("Title")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasKey("Id");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasIndex("SmartPlaylistDefinitionId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.ToTable("SuggestionHelpers");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            modelBuilder.Entity("User", b =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    b.Property<int>("UserId")
 | 
				
			||||||
 | 
					                        .ValueGeneratedOnAdd()
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("UserId"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<bool>("AssociationInProgress")
 | 
				
			||||||
 | 
					                        .HasColumnType("boolean");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<bool>("IsIntroduced")
 | 
				
			||||||
 | 
					                        .HasColumnType("boolean");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("LdapUserName")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("Name")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("NickName")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("SignalMemberId")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("SpotifyAuthAccessToken")
 | 
				
			||||||
 | 
					                        .IsRequired()
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<DateTime>("SpotifyAuthCreatedAt")
 | 
				
			||||||
 | 
					                        .HasColumnType("timestamp with time zone");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<int>("SpotifyAuthExpiresAfterSeconds")
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("SpotifyAuthRefreshToken")
 | 
				
			||||||
 | 
					                        .IsRequired()
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<bool>("WasChosenForSuggestionThisRound")
 | 
				
			||||||
 | 
					                        .HasColumnType("boolean");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasKey("UserId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.ToTable("Users");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            modelBuilder.Entity("SmartPlaylistDefinition", b =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    b.HasOne("User", "CreatedBy")
 | 
				
			||||||
 | 
					                        .WithMany()
 | 
				
			||||||
 | 
					                        .HasForeignKey("CreatedByUserId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Navigation("CreatedBy");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            modelBuilder.Entity("Song", b =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    b.HasOne("SmartPlaylistDefinition", null)
 | 
				
			||||||
 | 
					                        .WithMany("ExplicitlyExcludedSongs")
 | 
				
			||||||
 | 
					                        .HasForeignKey("SmartPlaylistDefinitionId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasOne("SmartPlaylistDefinition", null)
 | 
				
			||||||
 | 
					                        .WithMany("ExplicitlyIncludedSongs")
 | 
				
			||||||
 | 
					                        .HasForeignKey("SmartPlaylistDefinitionId1");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasOne("User", null)
 | 
				
			||||||
 | 
					                        .WithMany("LikedSongs")
 | 
				
			||||||
 | 
					                        .HasForeignKey("UserId");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            modelBuilder.Entity("SongSuggestion", b =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    b.HasOne("Song", "Song")
 | 
				
			||||||
 | 
					                        .WithMany()
 | 
				
			||||||
 | 
					                        .HasForeignKey("SongId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasOne("SuggestionHelper", "SuggestionHelper")
 | 
				
			||||||
 | 
					                        .WithMany()
 | 
				
			||||||
 | 
					                        .HasForeignKey("SuggestionHelperId")
 | 
				
			||||||
 | 
					                        .OnDelete(DeleteBehavior.Cascade)
 | 
				
			||||||
 | 
					                        .IsRequired();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasOne("User", "User")
 | 
				
			||||||
 | 
					                        .WithMany()
 | 
				
			||||||
 | 
					                        .HasForeignKey("UserId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Navigation("Song");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Navigation("SuggestionHelper");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Navigation("User");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            modelBuilder.Entity("SuggestionHelper", b =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    b.HasOne("SmartPlaylistDefinition", null)
 | 
				
			||||||
 | 
					                        .WithMany("Categories")
 | 
				
			||||||
 | 
					                        .HasForeignKey("SmartPlaylistDefinitionId");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            modelBuilder.Entity("SmartPlaylistDefinition", b =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    b.Navigation("Categories");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Navigation("ExplicitlyExcludedSongs");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Navigation("ExplicitlyIncludedSongs");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            modelBuilder.Entity("User", b =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    b.Navigation("LikedSongs");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					#pragma warning restore 612, 618
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,22 @@
 | 
				
			|||||||
 | 
					using Microsoft.EntityFrameworkCore.Migrations;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#nullable disable
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace song_of_the_day.DataMigrations
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /// <inheritdoc />
 | 
				
			||||||
 | 
					    public partial class explicitlyspecifykeys : Migration
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        /// <inheritdoc />
 | 
				
			||||||
 | 
					        protected override void Up(MigrationBuilder migrationBuilder)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// <inheritdoc />
 | 
				
			||||||
 | 
					        protected override void Down(MigrationBuilder migrationBuilder)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										280
									
								
								song_of_the_day/Data/Migrations/20250720003809_fix auth token non-null contraint.Designer.cs
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										280
									
								
								song_of_the_day/Data/Migrations/20250720003809_fix auth token non-null contraint.Designer.cs
									
									
									
										generated
									
									
									
										Normal file
									
								
							@@ -0,0 +1,280 @@
 | 
				
			|||||||
 | 
					// <auto-generated />
 | 
				
			||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using Microsoft.EntityFrameworkCore;
 | 
				
			||||||
 | 
					using Microsoft.EntityFrameworkCore.Infrastructure;
 | 
				
			||||||
 | 
					using Microsoft.EntityFrameworkCore.Migrations;
 | 
				
			||||||
 | 
					using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
 | 
				
			||||||
 | 
					using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#nullable disable
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace song_of_the_day.DataMigrations
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    [DbContext(typeof(DataContext))]
 | 
				
			||||||
 | 
					    [Migration("20250720003809_fix auth token non-null contraint")]
 | 
				
			||||||
 | 
					    partial class fixauthtokennonnullcontraint
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        /// <inheritdoc />
 | 
				
			||||||
 | 
					        protected override void BuildTargetModel(ModelBuilder modelBuilder)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					#pragma warning disable 612, 618
 | 
				
			||||||
 | 
					            modelBuilder
 | 
				
			||||||
 | 
					                .HasAnnotation("ProductVersion", "9.0.3")
 | 
				
			||||||
 | 
					                .HasAnnotation("Relational:MaxIdentifierLength", 63);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            modelBuilder.Entity("SmartPlaylistDefinition", b =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    b.Property<int>("Id")
 | 
				
			||||||
 | 
					                        .ValueGeneratedOnAdd()
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<int?>("CreatedByUserId")
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("Description")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<bool>("IncludesLikedSongs")
 | 
				
			||||||
 | 
					                        .HasColumnType("boolean");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<bool>("IncludesUnCategorizedSongs")
 | 
				
			||||||
 | 
					                        .HasColumnType("boolean");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("SpotifyPlaylistId")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("Title")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasKey("Id");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasIndex("CreatedByUserId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.ToTable("SmartPlaylistDefinitions");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            modelBuilder.Entity("Song", b =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    b.Property<int>("SongId")
 | 
				
			||||||
 | 
					                        .ValueGeneratedOnAdd()
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("SongId"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("Artist")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("Name")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<int?>("Provider")
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<int?>("SmartPlaylistDefinitionId")
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<int?>("SmartPlaylistDefinitionId1")
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("SpotifyId")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("Url")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<int?>("UserId")
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasKey("SongId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasIndex("SmartPlaylistDefinitionId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasIndex("SmartPlaylistDefinitionId1");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasIndex("UserId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.ToTable("Songs");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            modelBuilder.Entity("SongSuggestion", b =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    b.Property<int>("Id")
 | 
				
			||||||
 | 
					                        .ValueGeneratedOnAdd()
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<DateTime>("Date")
 | 
				
			||||||
 | 
					                        .HasColumnType("timestamp with time zone");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<bool>("HasUsedSuggestion")
 | 
				
			||||||
 | 
					                        .HasColumnType("boolean");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<int?>("SongId")
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<int>("SuggestionHelperId")
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<bool>("UserHasSubmitted")
 | 
				
			||||||
 | 
					                        .HasColumnType("boolean");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<int?>("UserId")
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasKey("Id");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasIndex("SongId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasIndex("SuggestionHelperId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasIndex("UserId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.ToTable("SongSuggestions");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            modelBuilder.Entity("SuggestionHelper", b =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    b.Property<int>("Id")
 | 
				
			||||||
 | 
					                        .ValueGeneratedOnAdd()
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("Description")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<int?>("SmartPlaylistDefinitionId")
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("Title")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasKey("Id");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasIndex("SmartPlaylistDefinitionId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.ToTable("SuggestionHelpers");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            modelBuilder.Entity("User", b =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    b.Property<int>("UserId")
 | 
				
			||||||
 | 
					                        .ValueGeneratedOnAdd()
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("UserId"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<bool>("AssociationInProgress")
 | 
				
			||||||
 | 
					                        .HasColumnType("boolean");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<bool>("IsIntroduced")
 | 
				
			||||||
 | 
					                        .HasColumnType("boolean");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("LdapUserName")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("Name")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("NickName")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("SignalMemberId")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("SpotifyAuthAccessToken")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<DateTime?>("SpotifyAuthCreatedAt")
 | 
				
			||||||
 | 
					                        .HasColumnType("timestamp with time zone");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<int?>("SpotifyAuthExpiresAfterSeconds")
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("SpotifyAuthRefreshToken")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<bool>("WasChosenForSuggestionThisRound")
 | 
				
			||||||
 | 
					                        .HasColumnType("boolean");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasKey("UserId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.ToTable("Users");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            modelBuilder.Entity("SmartPlaylistDefinition", b =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    b.HasOne("User", "CreatedBy")
 | 
				
			||||||
 | 
					                        .WithMany()
 | 
				
			||||||
 | 
					                        .HasForeignKey("CreatedByUserId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Navigation("CreatedBy");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            modelBuilder.Entity("Song", b =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    b.HasOne("SmartPlaylistDefinition", null)
 | 
				
			||||||
 | 
					                        .WithMany("ExplicitlyExcludedSongs")
 | 
				
			||||||
 | 
					                        .HasForeignKey("SmartPlaylistDefinitionId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasOne("SmartPlaylistDefinition", null)
 | 
				
			||||||
 | 
					                        .WithMany("ExplicitlyIncludedSongs")
 | 
				
			||||||
 | 
					                        .HasForeignKey("SmartPlaylistDefinitionId1");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasOne("User", null)
 | 
				
			||||||
 | 
					                        .WithMany("LikedSongs")
 | 
				
			||||||
 | 
					                        .HasForeignKey("UserId");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            modelBuilder.Entity("SongSuggestion", b =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    b.HasOne("Song", "Song")
 | 
				
			||||||
 | 
					                        .WithMany()
 | 
				
			||||||
 | 
					                        .HasForeignKey("SongId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasOne("SuggestionHelper", "SuggestionHelper")
 | 
				
			||||||
 | 
					                        .WithMany()
 | 
				
			||||||
 | 
					                        .HasForeignKey("SuggestionHelperId")
 | 
				
			||||||
 | 
					                        .OnDelete(DeleteBehavior.Cascade)
 | 
				
			||||||
 | 
					                        .IsRequired();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasOne("User", "User")
 | 
				
			||||||
 | 
					                        .WithMany()
 | 
				
			||||||
 | 
					                        .HasForeignKey("UserId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Navigation("Song");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Navigation("SuggestionHelper");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Navigation("User");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            modelBuilder.Entity("SuggestionHelper", b =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    b.HasOne("SmartPlaylistDefinition", null)
 | 
				
			||||||
 | 
					                        .WithMany("Categories")
 | 
				
			||||||
 | 
					                        .HasForeignKey("SmartPlaylistDefinitionId");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            modelBuilder.Entity("SmartPlaylistDefinition", b =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    b.Navigation("Categories");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Navigation("ExplicitlyExcludedSongs");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Navigation("ExplicitlyIncludedSongs");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            modelBuilder.Entity("User", b =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    b.Navigation("LikedSongs");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					#pragma warning restore 612, 618
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,91 @@
 | 
				
			|||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using Microsoft.EntityFrameworkCore.Migrations;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#nullable disable
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace song_of_the_day.DataMigrations
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /// <inheritdoc />
 | 
				
			||||||
 | 
					    public partial class fixauthtokennonnullcontraint : Migration
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        /// <inheritdoc />
 | 
				
			||||||
 | 
					        protected override void Up(MigrationBuilder migrationBuilder)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            migrationBuilder.AlterColumn<string>(
 | 
				
			||||||
 | 
					                name: "SpotifyAuthRefreshToken",
 | 
				
			||||||
 | 
					                table: "Users",
 | 
				
			||||||
 | 
					                type: "text",
 | 
				
			||||||
 | 
					                nullable: true,
 | 
				
			||||||
 | 
					                oldClrType: typeof(string),
 | 
				
			||||||
 | 
					                oldType: "text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            migrationBuilder.AlterColumn<int>(
 | 
				
			||||||
 | 
					                name: "SpotifyAuthExpiresAfterSeconds",
 | 
				
			||||||
 | 
					                table: "Users",
 | 
				
			||||||
 | 
					                type: "integer",
 | 
				
			||||||
 | 
					                nullable: true,
 | 
				
			||||||
 | 
					                oldClrType: typeof(int),
 | 
				
			||||||
 | 
					                oldType: "integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            migrationBuilder.AlterColumn<DateTime>(
 | 
				
			||||||
 | 
					                name: "SpotifyAuthCreatedAt",
 | 
				
			||||||
 | 
					                table: "Users",
 | 
				
			||||||
 | 
					                type: "timestamp with time zone",
 | 
				
			||||||
 | 
					                nullable: true,
 | 
				
			||||||
 | 
					                oldClrType: typeof(DateTime),
 | 
				
			||||||
 | 
					                oldType: "timestamp with time zone");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            migrationBuilder.AlterColumn<string>(
 | 
				
			||||||
 | 
					                name: "SpotifyAuthAccessToken",
 | 
				
			||||||
 | 
					                table: "Users",
 | 
				
			||||||
 | 
					                type: "text",
 | 
				
			||||||
 | 
					                nullable: true,
 | 
				
			||||||
 | 
					                oldClrType: typeof(string),
 | 
				
			||||||
 | 
					                oldType: "text");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// <inheritdoc />
 | 
				
			||||||
 | 
					        protected override void Down(MigrationBuilder migrationBuilder)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            migrationBuilder.AlterColumn<string>(
 | 
				
			||||||
 | 
					                name: "SpotifyAuthRefreshToken",
 | 
				
			||||||
 | 
					                table: "Users",
 | 
				
			||||||
 | 
					                type: "text",
 | 
				
			||||||
 | 
					                nullable: false,
 | 
				
			||||||
 | 
					                defaultValue: "",
 | 
				
			||||||
 | 
					                oldClrType: typeof(string),
 | 
				
			||||||
 | 
					                oldType: "text",
 | 
				
			||||||
 | 
					                oldNullable: true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            migrationBuilder.AlterColumn<int>(
 | 
				
			||||||
 | 
					                name: "SpotifyAuthExpiresAfterSeconds",
 | 
				
			||||||
 | 
					                table: "Users",
 | 
				
			||||||
 | 
					                type: "integer",
 | 
				
			||||||
 | 
					                nullable: false,
 | 
				
			||||||
 | 
					                defaultValue: 0,
 | 
				
			||||||
 | 
					                oldClrType: typeof(int),
 | 
				
			||||||
 | 
					                oldType: "integer",
 | 
				
			||||||
 | 
					                oldNullable: true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            migrationBuilder.AlterColumn<DateTime>(
 | 
				
			||||||
 | 
					                name: "SpotifyAuthCreatedAt",
 | 
				
			||||||
 | 
					                table: "Users",
 | 
				
			||||||
 | 
					                type: "timestamp with time zone",
 | 
				
			||||||
 | 
					                nullable: false,
 | 
				
			||||||
 | 
					                defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
 | 
				
			||||||
 | 
					                oldClrType: typeof(DateTime),
 | 
				
			||||||
 | 
					                oldType: "timestamp with time zone",
 | 
				
			||||||
 | 
					                oldNullable: true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            migrationBuilder.AlterColumn<string>(
 | 
				
			||||||
 | 
					                name: "SpotifyAuthAccessToken",
 | 
				
			||||||
 | 
					                table: "Users",
 | 
				
			||||||
 | 
					                type: "text",
 | 
				
			||||||
 | 
					                nullable: false,
 | 
				
			||||||
 | 
					                defaultValue: "",
 | 
				
			||||||
 | 
					                oldClrType: typeof(string),
 | 
				
			||||||
 | 
					                oldType: "text",
 | 
				
			||||||
 | 
					                oldNullable: true);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -21,6 +21,39 @@ namespace song_of_the_day.DataMigrations
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
 | 
					            NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            modelBuilder.Entity("SmartPlaylistDefinition", b =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    b.Property<int>("Id")
 | 
				
			||||||
 | 
					                        .ValueGeneratedOnAdd()
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<int?>("CreatedByUserId")
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("Description")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<bool>("IncludesLikedSongs")
 | 
				
			||||||
 | 
					                        .HasColumnType("boolean");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<bool>("IncludesUnCategorizedSongs")
 | 
				
			||||||
 | 
					                        .HasColumnType("boolean");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("SpotifyPlaylistId")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("Title")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasKey("Id");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasIndex("CreatedByUserId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.ToTable("SmartPlaylistDefinitions");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("Song", b =>
 | 
					            modelBuilder.Entity("Song", b =>
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    b.Property<int>("SongId")
 | 
					                    b.Property<int>("SongId")
 | 
				
			||||||
@@ -38,14 +71,29 @@ namespace song_of_the_day.DataMigrations
 | 
				
			|||||||
                    b.Property<int?>("Provider")
 | 
					                    b.Property<int?>("Provider")
 | 
				
			||||||
                        .HasColumnType("integer");
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<int?>("SmartPlaylistDefinitionId")
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<int?>("SmartPlaylistDefinitionId1")
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.Property<string>("SpotifyId")
 | 
					                    b.Property<string>("SpotifyId")
 | 
				
			||||||
                        .HasColumnType("text");
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.Property<string>("Url")
 | 
					                    b.Property<string>("Url")
 | 
				
			||||||
                        .HasColumnType("text");
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<int?>("UserId")
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.HasKey("SongId");
 | 
					                    b.HasKey("SongId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasIndex("SmartPlaylistDefinitionId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasIndex("SmartPlaylistDefinitionId1");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasIndex("UserId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("Songs");
 | 
					                    b.ToTable("Songs");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -97,11 +145,16 @@ namespace song_of_the_day.DataMigrations
 | 
				
			|||||||
                    b.Property<string>("Description")
 | 
					                    b.Property<string>("Description")
 | 
				
			||||||
                        .HasColumnType("text");
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<int?>("SmartPlaylistDefinitionId")
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.Property<string>("Title")
 | 
					                    b.Property<string>("Title")
 | 
				
			||||||
                        .HasColumnType("text");
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.HasKey("Id");
 | 
					                    b.HasKey("Id");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasIndex("SmartPlaylistDefinitionId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("SuggestionHelpers");
 | 
					                    b.ToTable("SuggestionHelpers");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -131,6 +184,18 @@ namespace song_of_the_day.DataMigrations
 | 
				
			|||||||
                    b.Property<string>("SignalMemberId")
 | 
					                    b.Property<string>("SignalMemberId")
 | 
				
			||||||
                        .HasColumnType("text");
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("SpotifyAuthAccessToken")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<DateTime?>("SpotifyAuthCreatedAt")
 | 
				
			||||||
 | 
					                        .HasColumnType("timestamp with time zone");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<int?>("SpotifyAuthExpiresAfterSeconds")
 | 
				
			||||||
 | 
					                        .HasColumnType("integer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("SpotifyAuthRefreshToken")
 | 
				
			||||||
 | 
					                        .HasColumnType("text");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.Property<bool>("WasChosenForSuggestionThisRound")
 | 
					                    b.Property<bool>("WasChosenForSuggestionThisRound")
 | 
				
			||||||
                        .HasColumnType("boolean");
 | 
					                        .HasColumnType("boolean");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -139,6 +204,30 @@ namespace song_of_the_day.DataMigrations
 | 
				
			|||||||
                    b.ToTable("Users");
 | 
					                    b.ToTable("Users");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            modelBuilder.Entity("SmartPlaylistDefinition", b =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    b.HasOne("User", "CreatedBy")
 | 
				
			||||||
 | 
					                        .WithMany()
 | 
				
			||||||
 | 
					                        .HasForeignKey("CreatedByUserId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Navigation("CreatedBy");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            modelBuilder.Entity("Song", b =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    b.HasOne("SmartPlaylistDefinition", null)
 | 
				
			||||||
 | 
					                        .WithMany("ExplicitlyExcludedSongs")
 | 
				
			||||||
 | 
					                        .HasForeignKey("SmartPlaylistDefinitionId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasOne("SmartPlaylistDefinition", null)
 | 
				
			||||||
 | 
					                        .WithMany("ExplicitlyIncludedSongs")
 | 
				
			||||||
 | 
					                        .HasForeignKey("SmartPlaylistDefinitionId1");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.HasOne("User", null)
 | 
				
			||||||
 | 
					                        .WithMany("LikedSongs")
 | 
				
			||||||
 | 
					                        .HasForeignKey("UserId");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("SongSuggestion", b =>
 | 
					            modelBuilder.Entity("SongSuggestion", b =>
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    b.HasOne("Song", "Song")
 | 
					                    b.HasOne("Song", "Song")
 | 
				
			||||||
@@ -161,6 +250,27 @@ namespace song_of_the_day.DataMigrations
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    b.Navigation("User");
 | 
					                    b.Navigation("User");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            modelBuilder.Entity("SuggestionHelper", b =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    b.HasOne("SmartPlaylistDefinition", null)
 | 
				
			||||||
 | 
					                        .WithMany("Categories")
 | 
				
			||||||
 | 
					                        .HasForeignKey("SmartPlaylistDefinitionId");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            modelBuilder.Entity("SmartPlaylistDefinition", b =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    b.Navigation("Categories");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Navigation("ExplicitlyExcludedSongs");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Navigation("ExplicitlyIncludedSongs");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            modelBuilder.Entity("User", b =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    b.Navigation("LikedSongs");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
#pragma warning restore 612, 618
 | 
					#pragma warning restore 612, 618
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										30
									
								
								song_of_the_day/Data/SmartPlaylistDefinition.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								song_of_the_day/Data/SmartPlaylistDefinition.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
				
			|||||||
 | 
					using Microsoft.EntityFrameworkCore;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[PrimaryKey(nameof(Id))]
 | 
				
			||||||
 | 
					public class SmartPlaylistDefinition
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public int Id { get; set; }
 | 
				
			||||||
 | 
					    public string? Title { get; set; }
 | 
				
			||||||
 | 
					    public string? Description { get; set; }
 | 
				
			||||||
 | 
					    public User? CreatedBy { get; set; }
 | 
				
			||||||
 | 
					    public bool IncludesUnCategorizedSongs { get; set; }
 | 
				
			||||||
 | 
					    public bool IncludesLikedSongs { get; set; }
 | 
				
			||||||
 | 
					    public List<SuggestionHelper>? Categories { get; set; }
 | 
				
			||||||
 | 
					    public string? SpotifyPlaylistId { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public List<Song>? ExplicitlyIncludedSongs { get; set; }
 | 
				
			||||||
 | 
					    public List<Song>? ExplicitlyExcludedSongs { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public bool IsThisUsersLikedSongsPlaylist
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        get
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return IncludesLikedSongs == true
 | 
				
			||||||
 | 
					                    && IncludesUnCategorizedSongs == false
 | 
				
			||||||
 | 
					                    && (ExplicitlyExcludedSongs == null || ExplicitlyExcludedSongs.Count == 0)
 | 
				
			||||||
 | 
					                    && (ExplicitlyIncludedSongs == null || ExplicitlyIncludedSongs.Count == 0)
 | 
				
			||||||
 | 
					                    && (Categories == null || Categories.Count == 0);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -10,4 +10,17 @@ public class User
 | 
				
			|||||||
    public bool AssociationInProgress { get; set; }
 | 
					    public bool AssociationInProgress { get; set; }
 | 
				
			||||||
    public string? LdapUserName { get; set; }
 | 
					    public string? LdapUserName { get; set; }
 | 
				
			||||||
    public bool WasChosenForSuggestionThisRound { get; set; }
 | 
					    public bool WasChosenForSuggestionThisRound { get; set; }
 | 
				
			||||||
 | 
					    public string PreferredName
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        get
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return (string.IsNullOrEmpty(NickName) ? Name : NickName).ToString();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public List<Song> LikedSongs { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public string? SpotifyAuthAccessToken { get; set; }
 | 
				
			||||||
 | 
					    public int? SpotifyAuthExpiresAfterSeconds { get; set; }
 | 
				
			||||||
 | 
					    public DateTime? SpotifyAuthCreatedAt { get; set; }
 | 
				
			||||||
 | 
					    public string? SpotifyAuthRefreshToken { get; set; }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -12,6 +12,7 @@
 | 
				
			|||||||
            <th>Song</th>
 | 
					            <th>Song</th>
 | 
				
			||||||
            <th>Submitter</th>
 | 
					            <th>Submitter</th>
 | 
				
			||||||
            <th>Details</th>
 | 
					            <th>Details</th>
 | 
				
			||||||
 | 
					            <th></th>
 | 
				
			||||||
        </tr>
 | 
					        </tr>
 | 
				
			||||||
        @foreach(var songSuggestion in Model.SongSuggestions)
 | 
					        @foreach(var songSuggestion in Model.SongSuggestions)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
@@ -26,6 +27,17 @@
 | 
				
			|||||||
                    <td><a href="@songSuggestion?.Song.Url" target="_blank">@string.Format("{0} - {1}", songSuggestion?.Song.Name, songSuggestion?.Song.Artist)</a></td>
 | 
					                    <td><a href="@songSuggestion?.Song.Url" target="_blank">@string.Format("{0} - {1}", songSuggestion?.Song.Name, songSuggestion?.Song.Artist)</a></td>
 | 
				
			||||||
                    <td>@displayName</td>
 | 
					                    <td>@displayName</td>
 | 
				
			||||||
                    <td><a href="/SongSubmission/@songSuggestion?.Id">View</a></td>
 | 
					                    <td><a href="/SongSubmission/@songSuggestion?.Id">View</a></td>
 | 
				
			||||||
 | 
					                    <td class="=viewLike">
 | 
				
			||||||
 | 
					                        @if(songSuggestion?.Song != null)
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            var handler = Model.HasUserLikedThisSong(@songSuggestion.Song) ? "UnlikeSong" : "LikeSong";
 | 
				
			||||||
 | 
					                            var likebuttonText = Model.HasUserLikedThisSong(@songSuggestion.Song) ? "Unlike" : "Like";
 | 
				
			||||||
 | 
					                            <form method="post">
 | 
				
			||||||
 | 
					                                <input name="songId" value="@songSuggestion.Song.SongId" type="hidden" /> 
 | 
				
			||||||
 | 
					                                <input type="submit" id="songlike" value="@likebuttonText" asp-page-handler="@handler" />
 | 
				
			||||||
 | 
					                            </form>
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    </td>
 | 
				
			||||||
                </tr>
 | 
					                </tr>
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,14 +17,67 @@ public class IndexModel : PageModel
 | 
				
			|||||||
    [BindProperty]
 | 
					    [BindProperty]
 | 
				
			||||||
    public List<SongSuggestion> SongSuggestions { get; set; } = new List<SongSuggestion>();
 | 
					    public List<SongSuggestion> SongSuggestions { get; set; } = new List<SongSuggestion>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public Task OnGet()
 | 
					    private User _currentUser;
 | 
				
			||||||
    {
 | 
					
 | 
				
			||||||
 | 
					    public User CurrentUser
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        get {
 | 
				
			||||||
 | 
					            if(_currentUser == null)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                var userName = this.User.Identity.Name;
 | 
				
			||||||
 | 
					                using(var dci = DataContext.Instance)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    _currentUser = dci.Users.Include(u => u.LikedSongs).Where(u => u.LdapUserName == userName).SingleOrDefault();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return _currentUser;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public bool HasUserLikedThisSong(Song song)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return CurrentUser.LikedSongs.Where(s => s.SongId == song.SongId).FirstOrDefault() != default(Song);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public Task OnGet()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
        using var dci = DataContext.Instance;
 | 
					        using var dci = DataContext.Instance;
 | 
				
			||||||
        SongSuggestions = dci.SongSuggestions.OrderByDescending(s => s.Date)
 | 
					        SongSuggestions = dci.SongSuggestions.OrderByDescending(s => s.Date)
 | 
				
			||||||
            .Take(50)
 | 
					            .Take(50)
 | 
				
			||||||
            .Include(s => s.Song)
 | 
					            .Include(s => s.Song)
 | 
				
			||||||
            .Include(s => s.User)
 | 
					            .Include(s => s.User)
 | 
				
			||||||
            .ToList();
 | 
					            .ToList();
 | 
				
			||||||
        return Task.CompletedTask;
 | 
					        return Task.CompletedTask;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public async Task<IActionResult> OnPostLikeSong(int? songId)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        using (var dci = DataContext.Instance)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var user = dci.Users.Include(u => u.LikedSongs).Where(u => u.UserId == CurrentUser.UserId).SingleOrDefault();
 | 
				
			||||||
 | 
					            if (user.LikedSongs.Find(s => s.SongId == songId) == default(Song))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                user.LikedSongs.Add(dci.Songs.Find(songId));
 | 
				
			||||||
 | 
					                await dci.SaveChangesAsync();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return RedirectToPage("/");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public async Task<IActionResult> OnPostUnlikeSong(int? songId)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        using(var dci = DataContext.Instance)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var user = dci.Users.Include(u => u.LikedSongs).Where(u => u.UserId == CurrentUser.UserId).SingleOrDefault();
 | 
				
			||||||
 | 
					            var songToRemove = user.LikedSongs.Where(s => s.SongId == songId).SingleOrDefault();
 | 
				
			||||||
 | 
					            if (songToRemove != default(Song))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                user.LikedSongs.Remove(songToRemove);
 | 
				
			||||||
 | 
					                await dci.SaveChangesAsync();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return RedirectToPage("/");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -23,9 +23,15 @@
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    else
 | 
					    else
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					        var userName = User.Identity.Name;
 | 
				
			||||||
 | 
					        var dci = DataContext.Instance;
 | 
				
			||||||
 | 
					        var selectedUsers = dci.Users.Where(u => u.LdapUserName == userName);
 | 
				
			||||||
 | 
					        var userId = selectedUsers.SingleOrDefault()?.UserId;
 | 
				
			||||||
 | 
					        dci.Dispose();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <form method="post" action="Auth/Logout">
 | 
					        <form method="post" action="Auth/Logout">
 | 
				
			||||||
            <div>
 | 
					            <div>
 | 
				
			||||||
                Welcome, @User.Identity.Name!
 | 
					                Welcome, <a href="/User/@userId">@User.Identity.Name</a>!
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
            <div>
 | 
					            <div>
 | 
				
			||||||
                <input name="submit" type="submit" value="Logout" />
 | 
					                <input name="submit" type="submit" value="Logout" />
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,6 +14,7 @@
 | 
				
			|||||||
                <th>Suggestion</th>
 | 
					                <th>Suggestion</th>
 | 
				
			||||||
                <th>Song</th>
 | 
					                <th>Song</th>
 | 
				
			||||||
                <th></th>
 | 
					                <th></th>
 | 
				
			||||||
 | 
					                <th></th>
 | 
				
			||||||
            </tr>
 | 
					            </tr>
 | 
				
			||||||
            @foreach(var submission in Model.UserSongSubmissions)
 | 
					            @foreach(var submission in Model.UserSongSubmissions)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@@ -44,6 +45,17 @@
 | 
				
			|||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        <button class=@buttonClass onclick="redirectTo(@submission.Id)">@buttonText</button>
 | 
					                        <button class=@buttonClass onclick="redirectTo(@submission.Id)">@buttonText</button>
 | 
				
			||||||
                    </td>
 | 
					                    </td>
 | 
				
			||||||
 | 
					                    <td class="=viewLike">
 | 
				
			||||||
 | 
					                        @if(submission.Song != null)
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            var handler = Model.HasUserLikedThisSong(submission.Song) ? "UnlikeSong" : "LikeSong";
 | 
				
			||||||
 | 
					                            var likebuttonText = Model.HasUserLikedThisSong(submission.Song) ? "Unlike" : "Like";
 | 
				
			||||||
 | 
					                            <form method="post">
 | 
				
			||||||
 | 
					                                <input name="songId" value="@submission.Song.SongId" type="hidden" /> 
 | 
				
			||||||
 | 
					                                <input type="submit" id="songlike" value="@likebuttonText" asp-page-handler="@handler" />
 | 
				
			||||||
 | 
					                            </form>
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    </td>
 | 
				
			||||||
                </tr>
 | 
					                </tr>
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        </table>
 | 
					        </table>
 | 
				
			||||||
@@ -53,8 +65,8 @@ else
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    <div class="text-left">
 | 
					    <div class="text-left">
 | 
				
			||||||
        <div class="suggestionHelper">
 | 
					        <div class="suggestionHelper">
 | 
				
			||||||
            <div class="title">Today's suggestionHelper is: <b>@Model.SuggestionHelper.Title</b></div>
 | 
					            <div class="title">Today's suggestionHelper is: <b>@Model.SuggestionHelper?.Title</b></div>
 | 
				
			||||||
            <div class="description" style="font-style: italic;">@Model.SuggestionHelper.Description</div>
 | 
					            <div class="description" style="font-style: italic;">@Model.SuggestionHelper?.Description</div>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
        <form method="post">
 | 
					        <form method="post">
 | 
				
			||||||
            <label asp-for="SubmitUrl" >Song Url:</label>
 | 
					            <label asp-for="SubmitUrl" >Song Url:</label>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -75,6 +75,24 @@ public class SongSubmissionModel : PageModel
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private User _currentUser;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public User CurrentUser
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        get {
 | 
				
			||||||
 | 
					            if(_currentUser == null)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                var userName = this.User.Identity.Name;
 | 
				
			||||||
 | 
					                using(var dci = DataContext.Instance)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    _currentUser = dci.Users.Include(u => u.LikedSongs).Where(u => u.LdapUserName == userName).SingleOrDefault();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return _currentUser;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public List<SpotifyAPI.Web.FullTrack> SpotifySuggestions
 | 
					    public List<SpotifyAPI.Web.FullTrack> SpotifySuggestions
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@@ -115,6 +133,11 @@ public class SongSubmissionModel : PageModel
 | 
				
			|||||||
    [BindProperty(SupportsGet = true)]
 | 
					    [BindProperty(SupportsGet = true)]
 | 
				
			||||||
    public bool HasUsedSuggestion { get; set; } = true;
 | 
					    public bool HasUsedSuggestion { get; set; } = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public bool HasUserLikedThisSong(Song song)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return CurrentUser.LikedSongs.Where(s => s.SongId == song.SongId).FirstOrDefault() != default(Song);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private async Task UpdateGroup(SongSuggestion suggestion)
 | 
					    private async Task UpdateGroup(SongSuggestion suggestion)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        var isItToday = suggestion.Date.Date == DateTime.Today;
 | 
					        var isItToday = suggestion.Date.Date == DateTime.Today;
 | 
				
			||||||
@@ -223,6 +246,35 @@ public class SongSubmissionModel : PageModel
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public async Task<IActionResult> OnPostLikeSong(int? songId)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        using (var dci = DataContext.Instance)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var user = dci.Users.Include(u => u.LikedSongs).Where(u => u.UserId == CurrentUser.UserId).SingleOrDefault();
 | 
				
			||||||
 | 
					            if (user.LikedSongs.Find(s => s.SongId == songId) == default(Song))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                user.LikedSongs.Add(dci.Songs.Find(songId));
 | 
				
			||||||
 | 
					                await dci.SaveChangesAsync();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return RedirectToPage("SongSubmission");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public async Task<IActionResult> OnPostUnlikeSong(int? songId)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        using(var dci = DataContext.Instance)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var user = dci.Users.Include(u => u.LikedSongs).Where(u => u.UserId == CurrentUser.UserId).SingleOrDefault();
 | 
				
			||||||
 | 
					            var songToRemove = user.LikedSongs.Where(s => s.SongId == songId).SingleOrDefault();
 | 
				
			||||||
 | 
					            if (songToRemove != default(Song))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                user.LikedSongs.Remove(songToRemove);
 | 
				
			||||||
 | 
					                await dci.SaveChangesAsync();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return RedirectToPage("SongSubmission");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public IActionResult OnGetUpdate()
 | 
					    public IActionResult OnGetUpdate()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        var songUrl = Request.Query["SubmitUrl"];
 | 
					        var songUrl = Request.Query["SubmitUrl"];
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,5 @@
 | 
				
			|||||||
@page "{userIndex}"
 | 
					@page "{userIndex}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@model UserModel
 | 
					@model UserModel
 | 
				
			||||||
@{
 | 
					@{
 | 
				
			||||||
    ViewData["Title"] = "User #" + @Model.userId;
 | 
					    ViewData["Title"] = "User #" + @Model.userId;
 | 
				
			||||||
@@ -12,6 +13,20 @@
 | 
				
			|||||||
        <label asp-for="UserName">Contact Name</label>
 | 
					        <label asp-for="UserName">Contact Name</label>
 | 
				
			||||||
        <input asp-for="UserName" disabled /> 
 | 
					        <input asp-for="UserName" disabled /> 
 | 
				
			||||||
        <br />
 | 
					        <br />
 | 
				
			||||||
        <input type="submit" title="Submit" />
 | 
					        <input type="submit" value="Submit" />
 | 
				
			||||||
    </form>
 | 
					    </form>
 | 
				
			||||||
 | 
					    @if(@Model.IsSpotifyAuthenticated)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        <form method="post" >
 | 
				
			||||||
 | 
					            <input name="userIndex" value="@Model.userId" type="hidden" /> 
 | 
				
			||||||
 | 
					            <input type="submit" value="Deauthorize Spotify"  asp-page-handler="SpotifyLogout" />
 | 
				
			||||||
 | 
					        </form>
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        <form method="post" >
 | 
				
			||||||
 | 
					            <input name="userIndex" value="@Model.userId" type="hidden" /> 
 | 
				
			||||||
 | 
					            <input type="submit" value="Connect to Spotify"  asp-page-handler="SpotifyLogin" />
 | 
				
			||||||
 | 
					        </form>
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,8 @@
 | 
				
			|||||||
 | 
					using Microsoft.AspNetCore.Components;
 | 
				
			||||||
using Microsoft.AspNetCore.Mvc;
 | 
					using Microsoft.AspNetCore.Mvc;
 | 
				
			||||||
using Microsoft.AspNetCore.Mvc.RazorPages;
 | 
					using Microsoft.AspNetCore.Mvc.RazorPages;
 | 
				
			||||||
 | 
					using SpotifyAPI.Web;
 | 
				
			||||||
 | 
					using System.Web;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace sotd.Pages;
 | 
					namespace sotd.Pages;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -18,10 +21,12 @@ public class UserModel : PageModel
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    public string UserName { get; set; }
 | 
					    public string UserName { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public bool IsSpotifyAuthenticated { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    [BindProperty]
 | 
					    [BindProperty]
 | 
				
			||||||
    public string UserNickName { get; set; }
 | 
					    public string UserNickName { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public void OnGet(int userIndex)
 | 
					    public async Task OnGet(int userIndex, [FromServices] SpotifyApiClient spotifyClient)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        using (var dci = DataContext.Instance)
 | 
					        using (var dci = DataContext.Instance)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
@@ -29,10 +34,11 @@ public class UserModel : PageModel
 | 
				
			|||||||
            this.UserName = user == null ? string.Empty : (user.Name ?? string.Empty);
 | 
					            this.UserName = user == null ? string.Empty : (user.Name ?? string.Empty);
 | 
				
			||||||
            this.UserNickName = user == null ? string.Empty : (user.NickName ?? string.Empty);
 | 
					            this.UserNickName = user == null ? string.Empty : (user.NickName ?? string.Empty);
 | 
				
			||||||
            this.userId = userIndex;
 | 
					            this.userId = userIndex;
 | 
				
			||||||
 | 
					            this.IsSpotifyAuthenticated = await spotifyClient.IsUserAuthenticatedAsync(user);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public void OnPost(int userIndex)
 | 
					    public async Task OnPost(int userIndex, [FromServices] SpotifyApiClient spotifyClient)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        using (var dci = DataContext.Instance)
 | 
					        using (var dci = DataContext.Instance)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
@@ -42,6 +48,39 @@ public class UserModel : PageModel
 | 
				
			|||||||
                user.NickName = this.UserNickName;
 | 
					                user.NickName = this.UserNickName;
 | 
				
			||||||
                dci.SaveChanges();
 | 
					                dci.SaveChanges();
 | 
				
			||||||
                this.UserName = user.Name ?? string.Empty;
 | 
					                this.UserName = user.Name ?? string.Empty;
 | 
				
			||||||
 | 
					                this.IsSpotifyAuthenticated = await spotifyClient.IsUserAuthenticatedAsync(user);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public IActionResult OnPostSpotifyLogin(int userIndex, string currentUri, [FromServices] SpotifyApiClient spotifyClient)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        _logger.LogTrace($"Attempting Spotify login for user with id {userIndex}.");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        var loginRequest = new LoginRequest(
 | 
				
			||||||
 | 
					                    new Uri(spotifyClient.GetLoginRedirectUri()),
 | 
				
			||||||
 | 
					                    AppConfiguration.Instance.SpotifyClientId,
 | 
				
			||||||
 | 
					                    LoginRequest.ResponseType.Code)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                Scope = new[] { Scopes.PlaylistReadPrivate, Scopes.PlaylistReadCollaborative, Scopes.PlaylistModifyPrivate, Scopes.PlaylistModifyPublic, Scopes.UgcImageUpload }
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					        var redirectUri = loginRequest.ToUri().ToString() + $"&finalRedirect={HttpUtility.UrlEncode($"{Request.Scheme}://{Request.Host}:{Request.Host.Port ?? 80}{Request.Path}")}";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return this.Redirect(redirectUri);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public async Task OnPostSpotifyLogout(int userIndex, [FromServices] SpotifyApiClient spotifyClient)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        _logger.LogTrace($"Attempting to deauthorize Spotify for user with id {userIndex}.");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        using (var dci = DataContext.Instance)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var user = dci.Users?.Find(userIndex);
 | 
				
			||||||
 | 
					            if (user != null)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                await spotifyClient.DeAuthorizeUserAsync(user);
 | 
				
			||||||
 | 
					                user.NickName = this.UserNickName;
 | 
				
			||||||
 | 
					                this.UserName = user.Name ?? string.Empty;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,6 +2,9 @@
 | 
				
			|||||||
using Scalar.AspNetCore;
 | 
					using Scalar.AspNetCore;
 | 
				
			||||||
using Microsoft.EntityFrameworkCore;
 | 
					using Microsoft.EntityFrameworkCore;
 | 
				
			||||||
using Microsoft.AspNetCore.Authentication.Cookies;
 | 
					using Microsoft.AspNetCore.Authentication.Cookies;
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore.Mvc;
 | 
				
			||||||
 | 
					using SpotifyAPI.Web;
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore.Components.Authorization;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var builder = WebApplication.CreateBuilder(args);
 | 
					var builder = WebApplication.CreateBuilder(args);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -21,6 +24,7 @@ builder.Services.AddSingleton<PhoneClaimCodeProviderService>();
 | 
				
			|||||||
builder.Services.AddSingleton<SignalIntegration>();
 | 
					builder.Services.AddSingleton<SignalIntegration>();
 | 
				
			||||||
builder.Services.AddSingleton<LdapIntegration>();
 | 
					builder.Services.AddSingleton<LdapIntegration>();
 | 
				
			||||||
builder.Services.AddSingleton<SpotifyApiClient>();
 | 
					builder.Services.AddSingleton<SpotifyApiClient>();
 | 
				
			||||||
 | 
					builder.Services.AddSingleton<PlayListSynchronizer>();
 | 
				
			||||||
builder.Services.AddSingleton<SongResolver>();
 | 
					builder.Services.AddSingleton<SongResolver>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var app = builder.Build();
 | 
					var app = builder.Build();
 | 
				
			||||||
@@ -66,6 +70,43 @@ userCheckTimer.OnOccurence += async (s, ea) =>
 | 
				
			|||||||
    await dci.DisposeAsync();
 | 
					    await dci.DisposeAsync();
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					logger.LogTrace("Setting up liked songs playlist creation timer");
 | 
				
			||||||
 | 
					var likePlaylistCheckTimer = new CronTimer("*/30 * * * *", "Europe/Vienna", includingSeconds: false);
 | 
				
			||||||
 | 
					likePlaylistCheckTimer.OnOccurence += async (s, ea) =>
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    var spotifyApiClient = app.Services.GetService<SpotifyApiClient>();
 | 
				
			||||||
 | 
					    var playlistSynchronizer = app.Services.GetService<PlayListSynchronizer>();
 | 
				
			||||||
 | 
					    var dci = DataContext.Instance;
 | 
				
			||||||
 | 
					    var needsSaving = false;
 | 
				
			||||||
 | 
					    var allUsers = dci.Users.ToList();
 | 
				
			||||||
 | 
					    await dci.DisposeAsync();
 | 
				
			||||||
 | 
					    dci = DataContext.Instance;
 | 
				
			||||||
 | 
					    foreach (var user in allUsers)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (!await spotifyApiClient.IsUserAuthenticatedAsync(user))
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            continue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        var foundPlaylist = dci.SmartPlaylistDefinitions?.Where(p => p.CreatedBy == user).ToList().Where(p => p.IsThisUsersLikedSongsPlaylist).SingleOrDefault();
 | 
				
			||||||
 | 
					        if (foundPlaylist == null)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var title = $"{user.PreferredName}'s liked Songs";
 | 
				
			||||||
 | 
					            var description = $"A collection of the songs liked by {user.PreferredName} on their 'Song of the day' server instance.";
 | 
				
			||||||
 | 
					            // playlist does not exist yet, creating it
 | 
				
			||||||
 | 
					            var newPlaylist = await (await spotifyApiClient.WithUserAuthorizationAsync(user)).CreateSpotifyPlaylist(title, description, false, true, user);
 | 
				
			||||||
 | 
					            await playlistSynchronizer.SynchronizePlaylistAsync(newPlaylist);
 | 
				
			||||||
 | 
					            needsSaving = true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        await playlistSynchronizer.SynchronizeUserPlaylistsAsync(user);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (needsSaving)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        await dci.SaveChangesAsync();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    await dci.DisposeAsync();
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
logger.LogTrace("Setting up user intro timer");
 | 
					logger.LogTrace("Setting up user intro timer");
 | 
				
			||||||
var userIntroTimer = new CronTimer("*/1 * * * *", "Europe/Vienna", includingSeconds: false);
 | 
					var userIntroTimer = new CronTimer("*/1 * * * *", "Europe/Vienna", includingSeconds: false);
 | 
				
			||||||
userIntroTimer.OnOccurence += async (s, ea) =>
 | 
					userIntroTimer.OnOccurence += async (s, ea) =>
 | 
				
			||||||
@@ -217,6 +258,7 @@ if (!app.Environment.IsDevelopment())
 | 
				
			|||||||
    userIntroTimer.Start();
 | 
					    userIntroTimer.Start();
 | 
				
			||||||
    pickOfTheDayTimer.Start();
 | 
					    pickOfTheDayTimer.Start();
 | 
				
			||||||
    ldapAssociationTimer.Start();
 | 
					    ldapAssociationTimer.Start();
 | 
				
			||||||
 | 
					    likePlaylistCheckTimer.Start();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
else
 | 
					else
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -249,6 +291,26 @@ app.MapControllerRoute(
 | 
				
			|||||||
    name: "logout",
 | 
					    name: "logout",
 | 
				
			||||||
    pattern: "{controller=Auth}/{action=Logout}"
 | 
					    pattern: "{controller=Auth}/{action=Logout}"
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					app.MapGet("SpotifyLogin", async (HttpRequest request, HttpResponse response) => {
 | 
				
			||||||
 | 
					    var spotifyClient = app.Services.GetService<SpotifyApiClient>();
 | 
				
			||||||
 | 
					    var code = request.Query["code"];
 | 
				
			||||||
 | 
					    var oAuthResponse = await new OAuthClient().RequestToken(
 | 
				
			||||||
 | 
					        new AuthorizationCodeTokenRequest(AppConfiguration.Instance.SpotifyClientId, AppConfiguration.Instance.SpotifyClientSecret, code, new Uri(spotifyClient.GetLoginRedirectUri()))
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var dci = DataContext.Instance;
 | 
				
			||||||
 | 
					    var userName = request.HttpContext.User.Identity.Name;
 | 
				
			||||||
 | 
					    var loggedInUser = dci.Users.Where(u => u.LdapUserName == userName).SingleOrDefault();
 | 
				
			||||||
 | 
					    var userId = loggedInUser.UserId;
 | 
				
			||||||
 | 
					    loggedInUser.SpotifyAuthAccessToken = oAuthResponse.AccessToken;
 | 
				
			||||||
 | 
					    loggedInUser.SpotifyAuthExpiresAfterSeconds = oAuthResponse.ExpiresIn;
 | 
				
			||||||
 | 
					    loggedInUser.SpotifyAuthCreatedAt = oAuthResponse.CreatedAt;
 | 
				
			||||||
 | 
					    loggedInUser.SpotifyAuthRefreshToken = oAuthResponse.RefreshToken;
 | 
				
			||||||
 | 
					    await dci.SaveChangesAsync();
 | 
				
			||||||
 | 
					    dci.Dispose();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    response.Redirect($"/User/{userId}");
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
app.MapGet("/debug/routes", (IEnumerable<EndpointDataSource> endpointSources) =>
 | 
					app.MapGet("/debug/routes", (IEnumerable<EndpointDataSource> endpointSources) =>
 | 
				
			||||||
        string.Join("\n", endpointSources.SelectMany(source => source.Endpoints)));
 | 
					        string.Join("\n", endpointSources.SelectMany(source => source.Endpoints)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										65
									
								
								song_of_the_day/SpotifyIntegration/PlayListSynchronizer.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								song_of_the_day/SpotifyIntegration/PlayListSynchronizer.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,65 @@
 | 
				
			|||||||
 | 
					using Microsoft.EntityFrameworkCore;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class PlayListSynchronizer
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    private SpotifyApiClient _spotifyAPIClient;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public PlayListSynchronizer(SpotifyApiClient spotifyAPIClient)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        _spotifyAPIClient = spotifyAPIClient;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public async Task SynchronizePlaylistAsync(SmartPlaylistDefinition playlist)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        var songsToInclude = new List<Song>();
 | 
				
			||||||
 | 
					        using (var dci = DataContext.Instance)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            songsToInclude.AddRange(dci.SongSuggestions.Where(ss => ss.HasUsedSuggestion && playlist.Categories.Contains(ss.SuggestionHelper) && !songsToInclude.Contains(ss.Song)).Select(ss => ss.Song));
 | 
				
			||||||
 | 
					            songsToInclude.AddRange(playlist.ExplicitlyIncludedSongs.Where(ss => !songsToInclude.Contains(ss)));
 | 
				
			||||||
 | 
					            if (playlist.IncludesUnCategorizedSongs)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                songsToInclude.AddRange(dci.SongSuggestions.Where(ss => !ss.HasUsedSuggestion && !songsToInclude.Contains(ss.Song)).Select(ss => ss.Song));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (playlist.IncludesLikedSongs)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                var userWithLikes = dci.Users.Include(u => u.LikedSongs).Where(u => u.UserId == playlist.CreatedBy.UserId).SingleOrDefault();
 | 
				
			||||||
 | 
					                var likedSongs = userWithLikes.LikedSongs;
 | 
				
			||||||
 | 
					                songsToInclude.AddRange(likedSongs.Where(s => !songsToInclude.Contains(s)));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        songsToInclude.RemoveAll(song => playlist.ExplicitlyExcludedSongs.Contains(song));
 | 
				
			||||||
 | 
					        var spotifyIdsToInclude = songsToInclude.Select(s => s.SpotifyId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        var apiClient = await _spotifyAPIClient.WithUserAuthorizationAsync(playlist.CreatedBy);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        var songsAlreadyInPlaylist = await apiClient.GetSongsInPlaylist(playlist.SpotifyPlaylistId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        var songsToAdd = spotifyIdsToInclude.Where(sti => !songsAlreadyInPlaylist.Contains(sti)).ToList();
 | 
				
			||||||
 | 
					        var songsToRemove = songsAlreadyInPlaylist.Where(sai => !spotifyIdsToInclude.Contains(sai)).ToList();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        apiClient.AddSongsToPlaylist(playlist.SpotifyPlaylistId, songsToAdd);
 | 
				
			||||||
 | 
					        apiClient.RemoveSongsFromPlaylist(playlist.SpotifyPlaylistId, songsToRemove);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public async Task SynchronizePlaylistsAsync(IList<SmartPlaylistDefinition> playlists)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        foreach(var playlist in playlists)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            await SynchronizePlaylistAsync(playlist);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public async Task SynchronizeUserPlaylistsAsync(User user)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        using(var dci = DataContext.Instance)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var userPlayLists = dci.SmartPlaylistDefinitions
 | 
				
			||||||
 | 
					                .Include(pl => pl.ExplicitlyIncludedSongs)
 | 
				
			||||||
 | 
					                .Include(pl => pl.ExplicitlyExcludedSongs)
 | 
				
			||||||
 | 
					                .Include(pl => pl.Categories)
 | 
				
			||||||
 | 
					                .Include(pl => pl.CreatedBy)
 | 
				
			||||||
 | 
					                .Where(pl => pl.CreatedBy == user).ToList();
 | 
				
			||||||
 | 
					            await SynchronizePlaylistsAsync(userPlayLists);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										287
									
								
								song_of_the_day/SpotifyIntegration/SpotifyApiClient.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										287
									
								
								song_of_the_day/SpotifyIntegration/SpotifyApiClient.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,287 @@
 | 
				
			|||||||
 | 
					using SpotifyAPI.Web;
 | 
				
			||||||
 | 
					using System.Web;
 | 
				
			||||||
 | 
					using Microsoft.EntityFrameworkCore;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class SpotifyApiClient
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    private SpotifyClient _spotifyClient;
 | 
				
			||||||
 | 
					    private SpotifyClient _userAuthorizedSpotifyClient;
 | 
				
			||||||
 | 
					    private ILogger<SpotifyApiClient> _logger;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public SpotifyApiClient(ILogger<SpotifyApiClient> logger)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        var config = SpotifyClientConfig.CreateDefault()
 | 
				
			||||||
 | 
					            .WithAuthenticator(new ClientCredentialsAuthenticator(
 | 
				
			||||||
 | 
					                AppConfiguration.Instance.SpotifyClientId,
 | 
				
			||||||
 | 
					                AppConfiguration.Instance.SpotifyClientSecret));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        _spotifyClient = new SpotifyClient(config);
 | 
				
			||||||
 | 
					        _userAuthorizedSpotifyClient = null;
 | 
				
			||||||
 | 
					        _logger = logger;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public async Task<SpotifyApiClient> WithUserAuthorizationAsync(User user)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        var refreshResponse = await new OAuthClient().RequestToken(
 | 
				
			||||||
 | 
					            new AuthorizationCodeRefreshRequest(
 | 
				
			||||||
 | 
					                            AppConfiguration.Instance.SpotifyClientId,
 | 
				
			||||||
 | 
					                            AppConfiguration.Instance.SpotifyClientSecret, 
 | 
				
			||||||
 | 
					                            user.SpotifyAuthRefreshToken)
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        var config = SpotifyClientConfig
 | 
				
			||||||
 | 
					            .CreateDefault()
 | 
				
			||||||
 | 
					            .WithAuthenticator(new AuthorizationCodeAuthenticator(
 | 
				
			||||||
 | 
					                            AppConfiguration.Instance.SpotifyClientId,
 | 
				
			||||||
 | 
					                            AppConfiguration.Instance.SpotifyClientSecret,
 | 
				
			||||||
 | 
					                            new AuthorizationCodeTokenResponse() {
 | 
				
			||||||
 | 
					                                RefreshToken = refreshResponse.RefreshToken,
 | 
				
			||||||
 | 
					                                AccessToken = refreshResponse.AccessToken,
 | 
				
			||||||
 | 
					                                TokenType = refreshResponse.TokenType,
 | 
				
			||||||
 | 
					                                ExpiresIn = refreshResponse.ExpiresIn,
 | 
				
			||||||
 | 
					                                Scope = refreshResponse.Scope,
 | 
				
			||||||
 | 
					                                CreatedAt = refreshResponse.CreatedAt
 | 
				
			||||||
 | 
					                            }));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        _userAuthorizedSpotifyClient = new SpotifyClient(config);
 | 
				
			||||||
 | 
					        return this;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private SpotifyClient UserAuthorizedSpotifyClient {
 | 
				
			||||||
 | 
					        get {
 | 
				
			||||||
 | 
					            if (_userAuthorizedSpotifyClient == null)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                throw new Exception("Cannot perform Spotify API call without user authorization. Authorize Spotify access from your user page first!");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return _userAuthorizedSpotifyClient;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public async Task<List<FullTrack>> GetTrackCandidatesAsync(string trackName, string artistName)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        try
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var searchResponse = await _spotifyClient.Search.Item(new SearchRequest(SearchRequest.Types.Track, $"{trackName} {artistName}")
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                Limit = 5
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            return searchResponse.Tracks.Items ?? new List<FullTrack>();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch (APIException ex)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            throw new Exception($"Error fetching tracks by query: \"{trackName} {artistName}\": {ex.Message}", ex);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public string GetLoginRedirectUri()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return "http://127.0.0.1:5000/SpotifyLogin";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private bool IsAuthTokenExpired(User user)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (user.SpotifyAuthCreatedAt == null || user.SpotifyAuthExpiresAfterSeconds == null)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        var expirationdate = user.SpotifyAuthCreatedAt.Value.AddSeconds(user.SpotifyAuthExpiresAfterSeconds.Value);
 | 
				
			||||||
 | 
					        return expirationdate < DateTime.UtcNow;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public async Task DeAuthorizeUserAsync(User user)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        using(var dci = DataContext.Instance)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var isEntityTracked = dci.Entry(user).State != EntityState.Detached;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if(!isEntityTracked)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                user = dci.Users.Find(user.UserId); 
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            user.SpotifyAuthAccessToken = string.Empty;
 | 
				
			||||||
 | 
					            user.SpotifyAuthRefreshToken = string.Empty;
 | 
				
			||||||
 | 
					            user.SpotifyAuthExpiresAfterSeconds = null;
 | 
				
			||||||
 | 
					            user.SpotifyAuthCreatedAt = null;
 | 
				
			||||||
 | 
					            await dci.SaveChangesAsync();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public async Task<string> GetValidAuthorizationTokenAsync(User user)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if(string.IsNullOrEmpty(user.SpotifyAuthAccessToken))
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            // user either never connected Spotify or we failed to refresh token - user needs to re-authenticate
 | 
				
			||||||
 | 
					            return string.Empty;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if(!this.IsAuthTokenExpired(user))
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return user.SpotifyAuthAccessToken;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // if token is expired, attempt a refresh
 | 
				
			||||||
 | 
					        var dci = DataContext.Instance;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        var isEntityTracked = dci.Entry(user).State != EntityState.Detached;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if(!isEntityTracked)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            user = dci.Users.Find(user.UserId); 
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try  
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var oAuthResponse = await new OAuthClient().RequestToken(
 | 
				
			||||||
 | 
					                new AuthorizationCodeRefreshRequest(AppConfiguration.Instance.SpotifyClientId, AppConfiguration.Instance.SpotifyClientSecret, user.SpotifyAuthRefreshToken)
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					            user.SpotifyAuthAccessToken = oAuthResponse.AccessToken;
 | 
				
			||||||
 | 
					            user.SpotifyAuthExpiresAfterSeconds = oAuthResponse.ExpiresIn;
 | 
				
			||||||
 | 
					            user.SpotifyAuthCreatedAt = oAuthResponse.CreatedAt;
 | 
				
			||||||
 | 
					            user.SpotifyAuthRefreshToken = oAuthResponse.RefreshToken;
 | 
				
			||||||
 | 
					            return user.SpotifyAuthAccessToken;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch(Exception ex)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _logger.LogWarning($"Failed to refresh SpotifyAuth token for user {user.LdapUserName}: {ex.Message}");
 | 
				
			||||||
 | 
					            await DeAuthorizeUserAsync(user);
 | 
				
			||||||
 | 
					            return string.Empty;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        finally
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            await dci.SaveChangesAsync();
 | 
				
			||||||
 | 
					            dci.Dispose();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public async Task<bool> IsUserAuthenticatedAsync(User user)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return !string.IsNullOrEmpty(await this.GetValidAuthorizationTokenAsync(user));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public async Task<FullTrack> GetTrackByIdAsync(string trackId)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        try
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return await _spotifyClient.Tracks.Get(trackId);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch (APIException ex)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            throw new Exception($"Error fetching track by ID: {trackId}: {ex.Message}", ex);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public async Task<SmartPlaylistDefinition> CreateSpotifyPlaylist(string playlistTitle,
 | 
				
			||||||
 | 
					                                                                    string description,
 | 
				
			||||||
 | 
					                                                                    bool IncludesUnCategorizedSongs,
 | 
				
			||||||
 | 
					                                                                    bool IncludesLikedSongs,
 | 
				
			||||||
 | 
					                                                                    User createdBy)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        try
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            // for now hardcoded with my user ID
 | 
				
			||||||
 | 
					            var playlistCreationRequest = new PlaylistCreateRequest(playlistTitle);
 | 
				
			||||||
 | 
					            playlistCreationRequest.Public = true;
 | 
				
			||||||
 | 
					            playlistCreationRequest.Collaborative = false;
 | 
				
			||||||
 | 
					            playlistCreationRequest.Description = description;
 | 
				
			||||||
 | 
					            var currentUser = await UserAuthorizedSpotifyClient.UserProfile.Current();
 | 
				
			||||||
 | 
					            var playlist = await UserAuthorizedSpotifyClient.Playlists.Create(currentUser.Id, playlistCreationRequest);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            _logger.LogWarning($"Creating new playlist '{playlistTitle}'");
 | 
				
			||||||
 | 
					            using(var dci = DataContext.Instance)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                var trackedUserEntity = dci.Users.Find(createdBy.UserId);
 | 
				
			||||||
 | 
					                var newPlaylist = new SmartPlaylistDefinition()
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    Title = playlistTitle,
 | 
				
			||||||
 | 
					                    Description = description,
 | 
				
			||||||
 | 
					                    Categories = new List<SuggestionHelper>(),
 | 
				
			||||||
 | 
					                    IncludesLikedSongs = IncludesLikedSongs,
 | 
				
			||||||
 | 
					                    IncludesUnCategorizedSongs = IncludesUnCategorizedSongs,
 | 
				
			||||||
 | 
					                    SpotifyPlaylistId = playlist.Id,
 | 
				
			||||||
 | 
					                    CreatedBy = trackedUserEntity,
 | 
				
			||||||
 | 
					                    ExplicitlyExcludedSongs = new List<Song>(),
 | 
				
			||||||
 | 
					                    ExplicitlyIncludedSongs = new List<Song>()
 | 
				
			||||||
 | 
					                };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                var trackedEntity = dci.SmartPlaylistDefinitions.Add(newPlaylist);
 | 
				
			||||||
 | 
					                await dci.SaveChangesAsync();
 | 
				
			||||||
 | 
					                return trackedEntity.Entity;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch (APIException ex)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            throw new Exception($"Error creating playlist with title: {playlistTitle}: {ex.Message}", ex);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public async Task<List<string>> GetSongsInPlaylist(string playlistId)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        try
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            // for now hardcoded with my user ID
 | 
				
			||||||
 | 
					            var ids = new List<string>();
 | 
				
			||||||
 | 
					            var firstContentPage = await UserAuthorizedSpotifyClient.Playlists.GetItems(playlistId);
 | 
				
			||||||
 | 
					            var allPages = await UserAuthorizedSpotifyClient.PaginateAll(firstContentPage);
 | 
				
			||||||
 | 
					            ids.AddRange(allPages.Select(track => (track.Track as FullTrack).Id));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return ids;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch (APIException ex)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            throw new Exception($"Error fetching playlist contents for playlist with id: {playlistId}: {ex.Message}", ex);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public async Task<string> AddSongsToPlaylist(string playlistId, List<string> songIds)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (songIds.Count == 0)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _logger.LogWarning($"No songs to add to playlist with id '{playlistId}'");
 | 
				
			||||||
 | 
					            return string.Empty;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        try
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                // for now hardcoded with my user ID
 | 
				
			||||||
 | 
					                var addItemRequest = new PlaylistAddItemsRequest(songIds.Select(id => $"spotify:track:{id}").ToList());
 | 
				
			||||||
 | 
					                _logger.LogWarning($"Adding songs to playlist with id '{playlistId}'");
 | 
				
			||||||
 | 
					                var response = await UserAuthorizedSpotifyClient.Playlists.AddItems(playlistId, addItemRequest);
 | 
				
			||||||
 | 
					                return response.SnapshotId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            catch (APIException ex)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                throw new Exception($"Error adding songs to playlist with id: {playlistId}: {ex.Message}", ex);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public async Task<string> RemoveSongsFromPlaylist(string playlistId, List<string> songIds)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (songIds.Count == 0)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _logger.LogWarning($"No songs to remove from playlist with id '{playlistId}'");
 | 
				
			||||||
 | 
					            return string.Empty;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        try
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            // for now hardcoded with my user ID
 | 
				
			||||||
 | 
					            var removeItemsRequest = new PlaylistRemoveItemsRequest();
 | 
				
			||||||
 | 
					            removeItemsRequest.Tracks = new List<PlaylistRemoveItemsRequest.Item>();
 | 
				
			||||||
 | 
					            foreach (var song in songIds)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                var item = new PlaylistRemoveItemsRequest.Item()
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    Uri = $"spotify:track:{song}",
 | 
				
			||||||
 | 
					                };
 | 
				
			||||||
 | 
					                removeItemsRequest.Tracks.Add(item);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            _logger.LogWarning($"Removing songs from playlist with id '{playlistId}'");
 | 
				
			||||||
 | 
					            var response = await UserAuthorizedSpotifyClient.Playlists.RemoveItems(playlistId, removeItemsRequest);
 | 
				
			||||||
 | 
					            return response.SnapshotId;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch (APIException ex)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            throw new Exception($"Error removing songs from playlist with id: {playlistId}: {ex.Message}", ex);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,44 +0,0 @@
 | 
				
			|||||||
using SpotifyAPI.Web;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public class SpotifyApiClient
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    private SpotifyClient _spotifyClient;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public SpotifyApiClient()
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        var config = SpotifyClientConfig.CreateDefault()
 | 
					 | 
				
			||||||
            .WithAuthenticator(new ClientCredentialsAuthenticator(
 | 
					 | 
				
			||||||
                AppConfiguration.Instance.SpotifyClientId,
 | 
					 | 
				
			||||||
                AppConfiguration.Instance.SpotifyClientSecret));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        _spotifyClient = new SpotifyClient(config);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public async Task<List<FullTrack>> GetTrackCandidatesAsync(string trackName, string artistName)
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        try
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var searchResponse = await _spotifyClient.Search.Item(new SearchRequest(SearchRequest.Types.Track, $"{trackName} {artistName}")
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                Limit = 5
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
            return searchResponse.Tracks.Items ?? new List<FullTrack>();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        catch (APIException ex)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            throw new Exception($"Error fetching tracks by query: \"{trackName} {artistName}\": {ex.Message}", ex);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public async Task<FullTrack> GetTrackByIdAsync(string trackId)
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        try
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return await _spotifyClient.Tracks.Get(trackId);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        catch (APIException ex)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            throw new Exception($"Error fetching track by ID: {trackId}: {ex.Message}", ex);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
		Reference in New Issue
	
	Block a user