completing part walker, adding month string getters

This commit is contained in:
Andy Pack 2024-01-18 22:30:34 +00:00
parent dd9e3aab42
commit ac8ea2723a
Signed by: sarsoo
GPG Key ID: A55BA3536A5E0ED7
18 changed files with 416 additions and 316 deletions

View File

@ -3,9 +3,11 @@
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<LangVersion>default</LangVersion>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Mixonomer.Fire\Mixonomer.Fire.csproj" /> <ProjectReference Include="..\Mixonomer.Fire\Mixonomer.Fire.csproj" />
<ProjectReference Include="..\Mixonomer.Playlist\Mixonomer.Playlist.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -3,19 +3,21 @@ using System.Linq;
using Mixonomer.Fire; using Mixonomer.Fire;
using System.Threading.Tasks; using System.Threading.Tasks;
using Mixonomer.Fire.Extensions; using Mixonomer.Fire.Extensions;
using Mixonomer.Playlist;
namespace Mixonomer.CLI;
namespace Mixonomer.CLI
{
class Program class Program
{ {
static async Task Main(string[] args) static async Task Main(string[] args)
{ {
var repo = new UserRepo(projectId: "mixonomer-test"); var repo = new UserRepo(projectId: System.Environment.GetEnvironmentVariable("GOOGLE_CLOUD_PROJECT"));
var userContext = await repo.GetUserContext("andy"); var userContext = await repo.GetUserContext("andy");
Console.WriteLine(userContext.User); Console.WriteLine(userContext.User);
}
}
}
var walker = new PartTreeWalker(repo);
var partPlaylists = await walker.GetPlaylistParts("andy", "RAP");
}
}

View File

@ -4,20 +4,30 @@ using System.Threading.Tasks;
using Google.Cloud.Firestore; using Google.Cloud.Firestore;
using Mixonomer.Fire.Model; using Mixonomer.Fire.Model;
namespace Mixonomer.Fire.Extensions namespace Mixonomer.Fire.Extensions;
{
public static class UserRepoExtensions public static class UserRepoExtensions
{ {
public static async IAsyncEnumerable<User> GetUsers(this UserRepo repo)
{
var users = repo.GetUserDocs();
await foreach (var user in users)
{
yield return user.ConvertTo<User>();
}
}
public static async Task<IAsyncEnumerable<DocumentSnapshot>> GetPlaylistDocs(this UserRepo repo, string username) public static async Task<IAsyncEnumerable<DocumentSnapshot>> GetPlaylistDocs(this UserRepo repo, string username)
{ {
var user = await repo.GetUser(username).ConfigureAwait(false); var user = await repo.GetUser(username).ConfigureAwait(false);
return await repo.GetPlaylistDocs(user).ConfigureAwait(false); return repo.GetPlaylistDocs(user);
} }
public static async IAsyncEnumerable<Playlist> GetPlaylists(this UserRepo repo, User user) public static async IAsyncEnumerable<Playlist> GetPlaylists(this UserRepo repo, User user)
{ {
var playlists = await repo.GetPlaylistDocs(user).ConfigureAwait(false); var playlists = repo.GetPlaylistDocs(user);
await foreach (var playlist in playlists) await foreach (var playlist in playlists)
{ {
@ -29,12 +39,12 @@ namespace Mixonomer.Fire.Extensions
{ {
var user = await repo.GetUser(username).ConfigureAwait(false); var user = await repo.GetUser(username).ConfigureAwait(false);
return await repo.GetTagDocs(user).ConfigureAwait(false); return repo.GetTagDocs(user);
} }
public static async IAsyncEnumerable<Tag> GetTags(this UserRepo repo, User user) public static async IAsyncEnumerable<Tag> GetTags(this UserRepo repo, User user)
{ {
var tags = await repo.GetTagDocs(user).ConfigureAwait(false); var tags = repo.GetTagDocs(user);
await foreach (var tag in tags) await foreach (var tag in tags)
{ {
@ -60,4 +70,3 @@ namespace Mixonomer.Fire.Extensions
return user; return user;
} }
} }
}

View File

@ -1,9 +1,7 @@
using System; using System;
namespace Mixonomer.Fire namespace Mixonomer.Fire;
{
public interface IRepository public interface IRepository
{ {
} }
}

View File

@ -2,6 +2,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework> <TargetFramework>netstandard2.1</TargetFramework>
<LangVersion>default</LangVersion>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@ -2,8 +2,8 @@
using System.Collections.Generic; using System.Collections.Generic;
using Google.Cloud.Firestore; using Google.Cloud.Firestore;
namespace Mixonomer.Fire namespace Mixonomer.Fire;
{
[FirestoreData] [FirestoreData]
public class Playlist public class Playlist
{ {
@ -81,5 +81,4 @@ namespace Mixonomer.Fire
[FirestoreDocumentReadTimestamp] [FirestoreDocumentReadTimestamp]
public Timestamp ReadTime { get; set; } public Timestamp ReadTime { get; set; }
} }
}

View File

@ -2,8 +2,8 @@
using System.Collections.Generic; using System.Collections.Generic;
using Google.Cloud.Firestore; using Google.Cloud.Firestore;
namespace Mixonomer.Fire namespace Mixonomer.Fire;
{
[FirestoreData] [FirestoreData]
public class Tag public class Tag
{ {
@ -71,5 +71,4 @@ namespace Mixonomer.Fire
[FirestoreDocumentReadTimestamp] [FirestoreDocumentReadTimestamp]
public Timestamp ReadTime { get; set; } public Timestamp ReadTime { get; set; }
} }
}

View File

@ -2,8 +2,8 @@
using System.Collections.Generic; using System.Collections.Generic;
using Google.Cloud.Firestore; using Google.Cloud.Firestore;
namespace Mixonomer.Fire namespace Mixonomer.Fire;
{
[FirestoreData] [FirestoreData]
public class User public class User
{ {
@ -57,5 +57,3 @@ namespace Mixonomer.Fire
[FirestoreDocumentReadTimestamp] [FirestoreDocumentReadTimestamp]
public Timestamp ReadTime { get; set; } public Timestamp ReadTime { get; set; }
} }
}

View File

@ -1,11 +1,10 @@
using System.Collections.Generic; using System.Collections.Generic;
namespace Mixonomer.Fire.Model namespace Mixonomer.Fire.Model;
{
public class UserContext public class UserContext
{ {
public User User { get; set; } public User User { get; set; }
public IEnumerable<Playlist> Playlists { get; set; } public IEnumerable<Playlist> Playlists { get; set; }
public IEnumerable<Tag> Tags { get; set; } public IEnumerable<Tag> Tags { get; set; }
} }
}

View File

@ -4,8 +4,8 @@ using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Google.Cloud.Firestore; using Google.Cloud.Firestore;
namespace Mixonomer.Fire namespace Mixonomer.Fire;
{
public class UserRepo public class UserRepo
{ {
private static readonly string USER_COLLECTION = "spotify_users"; private static readonly string USER_COLLECTION = "spotify_users";
@ -19,7 +19,7 @@ namespace Mixonomer.Fire
userCollection = this.db.Collection(USER_COLLECTION); userCollection = this.db.Collection(USER_COLLECTION);
} }
public IAsyncEnumerable<DocumentSnapshot> GetUsers() public IAsyncEnumerable<DocumentSnapshot> GetUserDocs()
{ {
return userCollection.StreamAsync(); return userCollection.StreamAsync();
} }
@ -32,19 +32,17 @@ namespace Mixonomer.Fire
return querySnapshot.SingleOrDefault()?.ConvertTo<User>(); return querySnapshot.SingleOrDefault()?.ConvertTo<User>();
} }
public Task<IAsyncEnumerable<DocumentSnapshot>> GetPlaylistDocs(User user) public IAsyncEnumerable<DocumentSnapshot> GetPlaylistDocs(User user)
{ {
var playlistCollection = db.Collection($"{USER_COLLECTION}/{user.Reference.Id}/playlists"); var playlistCollection = db.Collection($"{USER_COLLECTION}/{user.Reference.Id}/playlists");
return Task.FromResult(playlistCollection.StreamAsync()); return playlistCollection.StreamAsync();
} }
public Task<IAsyncEnumerable<DocumentSnapshot>> GetTagDocs(User user) public IAsyncEnumerable<DocumentSnapshot> GetTagDocs(User user)
{ {
var playlistCollection = db.Collection($"{USER_COLLECTION}/{user.Reference.Id}/tags"); var playlistCollection = db.Collection($"{USER_COLLECTION}/{user.Reference.Id}/tags");
return Task.FromResult(playlistCollection.StreamAsync()); return playlistCollection.StreamAsync();
} }
} }
}

View File

@ -3,16 +3,19 @@
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<LangVersion>default</LangVersion>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Google.Cloud.Functions.Hosting" Version="2.1.0" /> <PackageReference Include="Google.Cloud.Functions.Hosting" Version="2.1.0" />
<PackageReference Include="Google.Events.Protobuf" Version="1.3.0" /> <PackageReference Include="Google.Events.Protobuf" Version="1.3.0" />
<None Include="appsettings*.json" CopyToOutputDirectory="PreserveNewest" /> <None Include="appsettings*.json" CopyToOutputDirectory="PreserveNewest" />
<PackageReference Include="SpotifyAPI.Web" Version="7.0.2" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Mixonomer.Fire\Mixonomer.Fire.csproj" /> <ProjectReference Include="..\Mixonomer.Fire\Mixonomer.Fire.csproj" />
<ProjectReference Include="..\Mixonomer.Playlist\Mixonomer.Playlist.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -8,8 +8,8 @@ using Google.Events.Protobuf.Cloud.PubSub.V1;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Mixonomer.Fire; using Mixonomer.Fire;
namespace Mixonomer.Func namespace Mixonomer.Func;
{
public class RunUserPlaylist : ICloudEventFunction<MessagePublishedData> public class RunUserPlaylist : ICloudEventFunction<MessagePublishedData>
{ {
private readonly ILogger _logger; private readonly ILogger _logger;
@ -31,5 +31,3 @@ namespace Mixonomer.Func
_logger.LogInformation($"{user.username} was last refreshed at {user.last_refreshed}"); _logger.LogInformation($"{user.username} was last refreshed at {user.last_refreshed}");
} }
} }
}

View File

@ -11,6 +11,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mixonomer.Tests", "Mixonome
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mixonomer.CLI", "Mixonomer.CLI\Mixonomer.CLI.csproj", "{7469F571-DBF2-4D60-85FE-4041C445A490}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mixonomer.CLI", "Mixonomer.CLI\Mixonomer.CLI.csproj", "{7469F571-DBF2-4D60-85FE-4041C445A490}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mixonomer.Playlist", "Mixonomer.Playlist\Mixonomer.Playlist.csproj", "{274560D0-2EBB-4C6D-BA45-2270DB92F12C}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -33,6 +35,10 @@ Global
{7469F571-DBF2-4D60-85FE-4041C445A490}.Debug|Any CPU.Build.0 = Debug|Any CPU {7469F571-DBF2-4D60-85FE-4041C445A490}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7469F571-DBF2-4D60-85FE-4041C445A490}.Release|Any CPU.ActiveCfg = Release|Any CPU {7469F571-DBF2-4D60-85FE-4041C445A490}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7469F571-DBF2-4D60-85FE-4041C445A490}.Release|Any CPU.Build.0 = Release|Any CPU {7469F571-DBF2-4D60-85FE-4041C445A490}.Release|Any CPU.Build.0 = Release|Any CPU
{274560D0-2EBB-4C6D-BA45-2270DB92F12C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{274560D0-2EBB-4C6D-BA45-2270DB92F12C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{274560D0-2EBB-4C6D-BA45-2270DB92F12C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{274560D0-2EBB-4C6D-BA45-2270DB92F12C}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View File

@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<LangVersion>default</LangVersion>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Mixonomer.Fire\Mixonomer.Fire.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,13 @@
namespace Mixonomer.Playlist;
public static class Months
{
public static string ThisMonth() => DateTime.Now.ToString("MMMM yy").ToLowerInvariant();
public static string LastMonth()
{
var now = DateTime.Now;
var lastMonth = now.AddDays(-now.Day - 1);
return lastMonth.ToString("MMMM yy").ToLowerInvariant();
}
}

View File

@ -0,0 +1,61 @@
using Google.Cloud.Firestore;
using Mixonomer.Fire;
using Mixonomer.Fire.Extensions;
namespace Mixonomer.Playlist;
public class PartTreeWalker
{
private readonly UserRepo _userRepo;
private readonly HashSet<string> _processedPlaylists = new();
public HashSet<string>? SpotifyPlaylistNames { get; private set; }
public PartTreeWalker(UserRepo userRepo)
{
_userRepo = userRepo;
}
public async Task<IEnumerable<string>> GetPlaylistParts(string username, string playlistName)
{
var user = await _userRepo.GetUser(username);
return await GetPlaylistParts(user, playlistName);
}
public async Task<IEnumerable<string>> GetPlaylistParts(User user, string playlistName)
{
var playlist = await _userRepo.GetPlaylists(user).Where(x => x.name == playlistName).FirstOrDefaultAsync();
if (playlist is not null)
{
SpotifyPlaylistNames = new HashSet<string>(playlist.parts);
foreach (var part in playlist.playlist_references)
{
await ProcessPlaylist(part);
}
}
return SpotifyPlaylistNames;
}
private async Task ProcessPlaylist(DocumentReference documentReference)
{
if (!_processedPlaylists.Contains(documentReference.Id))
{
var playlist = (await documentReference.GetSnapshotAsync()).ConvertTo<Fire.Playlist>();
_processedPlaylists.Add(documentReference.Id);
foreach (var p in playlist.parts)
{
SpotifyPlaylistNames?.Add(p);
}
foreach (var p in playlist.playlist_references)
{
await ProcessPlaylist(p);
}
}
}
}

View File

@ -4,6 +4,8 @@
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
<LangVersion>default</LangVersion>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@ -2,8 +2,8 @@
using Mixonomer.Fire; using Mixonomer.Fire;
using Xunit; using Xunit;
namespace Mixonomer.Tests namespace Mixonomer.Tests;
{
public class UnitTest1 public class UnitTest1
{ {
[Fact] [Fact]
@ -14,5 +14,3 @@ namespace Mixonomer.Tests
// var user = await repo.GetUser("andy"); // var user = await repo.GetUser("andy");
} }
} }
}