Fully implement photos for users

This commit is contained in:
jvyden 2021-11-05 21:54:43 -04:00
commit 9b7686bb91
No known key found for this signature in database
GPG key ID: 18BCF2BE0262B278
8 changed files with 679 additions and 6 deletions

View file

@ -1,3 +1,4 @@
#nullable enable
using System.Collections.Generic;
using System.IO;
using System.Linq;
@ -25,14 +26,19 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpPost("uploadPhoto")]
public async Task<IActionResult> UploadPhoto()
{
User? user = await this.database.UserFromRequest(this.Request);
if (user == null) return this.StatusCode(403, "");
this.Request.Body.Position = 0;
string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync();
XmlSerializer serializer = new(typeof(Photo));
Photo photo = (Photo)serializer.Deserialize(new StringReader(bodyString));
Photo? photo = (Photo?)serializer.Deserialize(new StringReader(bodyString));
if (photo == null) return this.BadRequest();
photo.CreatorId = user.UserId;
photo.Creator = user;
foreach (PhotoSubject subject in photo.Subjects)
{
subject.User = await this.database.Users.FirstOrDefaultAsync(u => u.Username == subject.Username);
@ -59,10 +65,19 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpGet("photos/user/{id:int}")]
public async Task<IActionResult> SlotPhotos(int id)
{
List<Photo> photos = await this.database.Photos.Take(50).ToListAsync();
List<Photo> photos = await this.database.Photos.Take(10).ToListAsync();
string response = photos.Aggregate(string.Empty, (s, photo) => s + photo.Serialize(id));
return this.Ok(LbpSerializer.StringElement("photos", response));
}
[HttpGet("photos/by")]
public async Task<IActionResult> UserPhotos([FromQuery] string user)
{
User? userFromQuery = await this.database.Users.FirstOrDefaultAsync(u => u.Username == user);
if (user == null) return this.NotFound();
List<Photo> photos = await this.database.Photos.Where(p => p.CreatorId == userFromQuery.UserId).Take(10).ToListAsync();
string response = photos.Aggregate(string.Empty, (s, photo) => s + photo.Serialize(0));
return this.Ok(LbpSerializer.StringElement("photos", response));
}
}

View file

@ -107,6 +107,9 @@ namespace LBPUnion.ProjectLighthouse
return (user, token);
}
public async Task<Photo?> PhotoFromSubject(PhotoSubject subject)
=> await this.Photos.FirstOrDefaultAsync(p => p.PhotoSubjectIds.Contains(subject.PhotoSubjectId.ToString()));
#nullable disable
}
}

View file

@ -0,0 +1,541 @@
// <auto-generated />
using LBPUnion.ProjectLighthouse;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
namespace ProjectLighthouse.Migrations
{
[DbContext(typeof(Database))]
[Migration("20211106010424_AddCreatorToPhoto")]
partial class AddCreatorToPhoto
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("Relational:MaxIdentifierLength", 64)
.HasAnnotation("ProductVersion", "5.0.11");
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.HeartedProfile", b =>
{
b.Property<int>("HeartedProfileId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("HeartedUserId")
.HasColumnType("int");
b.Property<int>("UserId")
.HasColumnType("int");
b.HasKey("HeartedProfileId");
b.HasIndex("HeartedUserId");
b.HasIndex("UserId");
b.ToTable("HeartedProfiles");
});
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.HeartedLevel", b =>
{
b.Property<int>("HeartedLevelId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("SlotId")
.HasColumnType("int");
b.Property<int>("UserId")
.HasColumnType("int");
b.HasKey("HeartedLevelId");
b.HasIndex("SlotId");
b.HasIndex("UserId");
b.ToTable("HeartedLevels");
});
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.QueuedLevel", b =>
{
b.Property<int>("QueuedLevelId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("SlotId")
.HasColumnType("int");
b.Property<int>("UserId")
.HasColumnType("int");
b.HasKey("QueuedLevelId");
b.HasIndex("SlotId");
b.HasIndex("UserId");
b.ToTable("QueuedLevels");
});
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.Slot", b =>
{
b.Property<int>("SlotId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("AuthorLabels")
.HasColumnType("longtext");
b.Property<string>("BackgroundHash")
.HasColumnType("longtext");
b.Property<int>("CreatorId")
.HasColumnType("int");
b.Property<string>("Description")
.HasColumnType("longtext");
b.Property<long>("FirstUploaded")
.HasColumnType("bigint");
b.Property<int>("GameVersion")
.HasColumnType("int");
b.Property<string>("IconHash")
.HasColumnType("longtext");
b.Property<bool>("InitiallyLocked")
.HasColumnType("tinyint(1)");
b.Property<long>("LastUpdated")
.HasColumnType("bigint");
b.Property<bool>("Lbp1Only")
.HasColumnType("tinyint(1)");
b.Property<int>("LocationId")
.HasColumnType("int");
b.Property<int>("MaximumPlayers")
.HasColumnType("int");
b.Property<int>("MinimumPlayers")
.HasColumnType("int");
b.Property<bool>("MoveRequired")
.HasColumnType("tinyint(1)");
b.Property<string>("Name")
.HasColumnType("longtext");
b.Property<string>("ResourceCollection")
.HasColumnType("longtext");
b.Property<string>("RootLevel")
.HasColumnType("longtext");
b.Property<int>("Shareable")
.HasColumnType("int");
b.Property<bool>("SubLevel")
.HasColumnType("tinyint(1)");
b.Property<bool>("TeamPick")
.HasColumnType("tinyint(1)");
b.HasKey("SlotId");
b.HasIndex("CreatorId");
b.HasIndex("LocationId");
b.ToTable("Slots");
});
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Photo", b =>
{
b.Property<int>("PhotoId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("CreatorId")
.HasColumnType("int");
b.Property<string>("LargeHash")
.HasColumnType("longtext");
b.Property<string>("MediumHash")
.HasColumnType("longtext");
b.Property<string>("PhotoSubjectCollection")
.HasColumnType("longtext");
b.Property<string>("PlanHash")
.HasColumnType("longtext");
b.Property<string>("SmallHash")
.HasColumnType("longtext");
b.Property<long>("Timestamp")
.HasColumnType("bigint");
b.HasKey("PhotoId");
b.HasIndex("CreatorId");
b.ToTable("Photos");
});
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.PhotoSubject", b =>
{
b.Property<int>("PhotoSubjectId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("Bounds")
.HasColumnType("longtext");
b.Property<int>("UserId")
.HasColumnType("int");
b.HasKey("PhotoSubjectId");
b.HasIndex("UserId");
b.ToTable("PhotoSubjects");
});
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.Comment", b =>
{
b.Property<int>("CommentId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("Message")
.HasColumnType("longtext");
b.Property<int>("PosterUserId")
.HasColumnType("int");
b.Property<int>("TargetUserId")
.HasColumnType("int");
b.Property<int>("ThumbsDown")
.HasColumnType("int");
b.Property<int>("ThumbsUp")
.HasColumnType("int");
b.Property<long>("Timestamp")
.HasColumnType("bigint");
b.HasKey("CommentId");
b.HasIndex("PosterUserId");
b.HasIndex("TargetUserId");
b.ToTable("Comments");
});
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.LastMatch", b =>
{
b.Property<int>("UserId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<long>("Timestamp")
.HasColumnType("bigint");
b.HasKey("UserId");
b.ToTable("LastMatches");
});
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.Location", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("X")
.HasColumnType("int");
b.Property<int>("Y")
.HasColumnType("int");
b.HasKey("Id");
b.ToTable("Locations");
});
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Score", b =>
{
b.Property<int>("ScoreId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("PlayerIdCollection")
.HasColumnType("longtext");
b.Property<int>("Points")
.HasColumnType("int");
b.Property<int>("SlotId")
.HasColumnType("int");
b.Property<int>("Type")
.HasColumnType("int");
b.HasKey("ScoreId");
b.HasIndex("SlotId");
b.ToTable("Scores");
});
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Token", b =>
{
b.Property<int>("TokenId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("GameVersion")
.HasColumnType("int");
b.Property<int>("UserId")
.HasColumnType("int");
b.Property<string>("UserToken")
.HasColumnType("longtext");
b.HasKey("TokenId");
b.ToTable("Tokens");
});
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.User", b =>
{
b.Property<int>("UserId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("Biography")
.HasColumnType("longtext");
b.Property<string>("BooHash")
.HasColumnType("longtext");
b.Property<int>("CommentCount")
.HasColumnType("int");
b.Property<bool>("CommentsEnabled")
.HasColumnType("tinyint(1)");
b.Property<int>("FavouriteSlotCount")
.HasColumnType("int");
b.Property<int>("FavouriteUserCount")
.HasColumnType("int");
b.Property<int>("Game")
.HasColumnType("int");
b.Property<int>("HeartCount")
.HasColumnType("int");
b.Property<string>("IconHash")
.HasColumnType("longtext");
b.Property<int>("Lists")
.HasColumnType("int");
b.Property<int>("LocationId")
.HasColumnType("int");
b.Property<int>("LolCatFtwCount")
.HasColumnType("int");
b.Property<int>("PhotosByMeCount")
.HasColumnType("int");
b.Property<int>("PhotosWithMeCount")
.HasColumnType("int");
b.Property<string>("Pins")
.HasColumnType("longtext");
b.Property<string>("PlanetHash")
.HasColumnType("longtext");
b.Property<int>("ReviewCount")
.HasColumnType("int");
b.Property<int>("StaffChallengeBronzeCount")
.HasColumnType("int");
b.Property<int>("StaffChallengeGoldCount")
.HasColumnType("int");
b.Property<int>("StaffChallengeSilverCount")
.HasColumnType("int");
b.Property<string>("Username")
.HasColumnType("longtext");
b.Property<string>("YayHash")
.HasColumnType("longtext");
b.HasKey("UserId");
b.HasIndex("LocationId");
b.ToTable("Users");
});
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.HeartedProfile", b =>
{
b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "HeartedUser")
.WithMany()
.HasForeignKey("HeartedUserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("HeartedUser");
b.Navigation("User");
});
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.HeartedLevel", b =>
{
b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot")
.WithMany()
.HasForeignKey("SlotId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Slot");
b.Navigation("User");
});
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.QueuedLevel", b =>
{
b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot")
.WithMany()
.HasForeignKey("SlotId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Slot");
b.Navigation("User");
});
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.Slot", b =>
{
b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Creator")
.WithMany()
.HasForeignKey("CreatorId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("LBPUnion.ProjectLighthouse.Types.Profiles.Location", "Location")
.WithMany()
.HasForeignKey("LocationId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Creator");
b.Navigation("Location");
});
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Photo", b =>
{
b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Creator")
.WithMany()
.HasForeignKey("CreatorId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Creator");
});
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.PhotoSubject", b =>
{
b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.Comment", b =>
{
b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Poster")
.WithMany()
.HasForeignKey("PosterUserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Target")
.WithMany()
.HasForeignKey("TargetUserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Poster");
b.Navigation("Target");
});
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Score", b =>
{
b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot")
.WithMany()
.HasForeignKey("SlotId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Slot");
});
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.User", b =>
{
b.HasOne("LBPUnion.ProjectLighthouse.Types.Profiles.Location", "Location")
.WithMany()
.HasForeignKey("LocationId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Location");
});
#pragma warning restore 612, 618
}
}
}

View file

@ -0,0 +1,49 @@
using Microsoft.EntityFrameworkCore.Migrations;
namespace ProjectLighthouse.Migrations
{
public partial class AddCreatorToPhoto : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
// Conflicts with old data.
migrationBuilder.Sql("DELETE FROM Photos;");
migrationBuilder.Sql("DELETE FROM PhotoSubjects;"); // Also delete PhotoSubjects while we're at it.
migrationBuilder.AddColumn<int>(
name: "CreatorId",
table: "Photos",
type: "int",
nullable: false,
defaultValue: 0);
migrationBuilder.CreateIndex(
name: "IX_Photos_CreatorId",
table: "Photos",
column: "CreatorId");
migrationBuilder.AddForeignKey(
name: "FK_Photos_Users_CreatorId",
table: "Photos",
column: "CreatorId",
principalTable: "Users",
principalColumn: "UserId",
onDelete: ReferentialAction.Cascade);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_Photos_Users_CreatorId",
table: "Photos");
migrationBuilder.DropIndex(
name: "IX_Photos_CreatorId",
table: "Photos");
migrationBuilder.DropColumn(
name: "CreatorId",
table: "Photos");
}
}
}

View file

@ -160,6 +160,9 @@ namespace ProjectLighthouse.Migrations
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("CreatorId")
.HasColumnType("int");
b.Property<string>("LargeHash")
.HasColumnType("longtext");
@ -180,6 +183,8 @@ namespace ProjectLighthouse.Migrations
b.HasKey("PhotoId");
b.HasIndex("CreatorId");
b.ToTable("Photos");
});
@ -466,6 +471,17 @@ namespace ProjectLighthouse.Migrations
b.Navigation("Location");
});
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Photo", b =>
{
b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Creator")
.WithMany()
.HasForeignKey("CreatorId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Creator");
});
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.PhotoSubject", b =>
{
b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User")

View file

@ -1,8 +1,11 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Xml.Serialization;
using LBPUnion.ProjectLighthouse.Serialization;
using Microsoft.EntityFrameworkCore;
namespace LBPUnion.ProjectLighthouse.Types
{
@ -53,16 +56,45 @@ namespace LBPUnion.ProjectLighthouse.Types
public string PhotoSubjectCollection { get; set; }
public int CreatorId { get; set; }
[ForeignKey(nameof(CreatorId))]
public User Creator { get; set; }
public List<PhotoSubject> GetSubjects()
{
List<PhotoSubject> response = new();
using Database database = new();
foreach (string idStr in this.PhotoSubjectIds)
{
if (string.IsNullOrEmpty(idStr)) continue;
if (!int.TryParse(idStr, out int id)) throw new InvalidCastException(idStr + " is not a valid number.");
PhotoSubject? photoSubject = database.PhotoSubjects.Include(p => p.User).FirstOrDefault(p => p.PhotoSubjectId == id);
if (photoSubject == null) continue;
response.Add(photoSubject);
}
return response;
}
public string Serialize(int slotId)
{
string slot = LbpSerializer.TaggedStringElement("slot", LbpSerializer.StringElement("id", slotId), "type", "user");
string subjects = this.GetSubjects().Aggregate(string.Empty, (s, subject) => s + subject.Serialize());
string photo = LbpSerializer.StringElement("id", this.PhotoId) +
LbpSerializer.StringElement("small", this.SmallHash) +
LbpSerializer.StringElement("medium", this.MediumHash) +
LbpSerializer.StringElement("large", this.LargeHash) +
LbpSerializer.StringElement("plan", this.PlanHash) +
LbpSerializer.StringElement("author", "a43243221") +
LbpSerializer.StringElement("author", this.CreatorId) +
LbpSerializer.StringElement("subjects", subjects) +
slot;
return LbpSerializer.TaggedStringElement("photo", photo, "timestamp", Timestamp * 1000);

View file

@ -2,6 +2,7 @@ using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Xml.Serialization;
using LBPUnion.ProjectLighthouse.Serialization;
namespace LBPUnion.ProjectLighthouse.Types
{
@ -27,5 +28,14 @@ namespace LBPUnion.ProjectLighthouse.Types
[XmlElement("bounds")]
public string Bounds { get; set; }
public string Serialize()
{
string response = LbpSerializer.StringElement("npHandle", User.Username) +
LbpSerializer.StringElement("displayName", User.Username) +
LbpSerializer.StringElement("bounds", Bounds);
return LbpSerializer.StringElement("subject", response);
}
}
}

View file

@ -27,7 +27,14 @@ namespace LBPUnion.ProjectLighthouse.Types
public int ReviewCount { get; set; }
public int CommentCount { get; set; }
public int PhotosByMeCount { get; set; }
public int PhotosByMeCount {
get {
using Database database = new();
return database.Photos.Count(p => p.CreatorId == this.UserId);
}
}
public int PhotosWithMeCount { get; set; }
public bool CommentsEnabled { get; set; }