state after adding all of the user stuff and messing around with the wierd brave bug
This commit is contained in:
226
OpenArchival.DataAccess/ApplicationDbContext.cs
Normal file
226
OpenArchival.DataAccess/ApplicationDbContext.cs
Normal file
@@ -0,0 +1,226 @@
|
||||
/*
|
||||
<EFBFBD>Commons Clause<73> License Condition v1.0
|
||||
https://commonsclause.com/
|
||||
|
||||
The Software is provided to you by the Licensor under the License, as defined below, subject to the following condition.
|
||||
|
||||
Without limiting other conditions in the License, the grant of rights under the License will not include, and the License does not grant to you, the right to Sell the Software.
|
||||
|
||||
For purposes of the foregoing, <20>Sell<6C> means practicing any or all of the rights granted to you under the License to provide to third parties, for a fee or other consideration (including without limitation fees for hosting or consulting/ support services related to the Software), a product or service whose value derives, entirely or substantially, from the functionality of the Software. Any license notice or attribution required by the License must also include this Commons Clause License Condition notice.
|
||||
|
||||
Software: Open Archival
|
||||
|
||||
License: GNU GPL: https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
|
||||
Licensor: Vincent Allen
|
||||
*/
|
||||
|
||||
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.ChangeTracking;
|
||||
using OpenArchival.DataAccess;
|
||||
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using OpenArchival.DataAccess;
|
||||
|
||||
namespace OpenArchival.DataAccess;
|
||||
|
||||
public class ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : IdentityDbContext<ApplicationUser>(options)
|
||||
{
|
||||
public DbSet<ArtifactGrouping> ArtifactGroupings { get; set; }
|
||||
|
||||
public DbSet<ArtifactEntry> ArtifactEntries { get; set; }
|
||||
|
||||
public DbSet<ArtifactEntryTag> ArtifactEntryTags { get; set; }
|
||||
|
||||
public DbSet<ArchiveCategory> ArchiveCategories { get; set; }
|
||||
|
||||
public DbSet<ListedName> ArtifactAssociatedNames { get; set; }
|
||||
|
||||
public DbSet<FilePathListing> ArtifactFilePaths { get; set; }
|
||||
|
||||
public DbSet<ArtifactDefect> ArtifactDefects { get; set; }
|
||||
|
||||
public DbSet<ArtifactStorageLocation> ArtifactStorageLocations { get; set; }
|
||||
|
||||
public DbSet<ArtifactType> ArtifactTypes { get; set; }
|
||||
|
||||
public DbSet<ArtifactGroupingViewCount> ArtifactGroupingViewCounts { get; set; }
|
||||
|
||||
public DbSet<BlogPost> BlogPosts { get; set; }
|
||||
|
||||
public DbSet<BlogPostTag> BlogPostTags { get; set; }
|
||||
|
||||
public DbSet<BlogPostViewCount> BlogPostViews { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Configuration for what featured artifacts will be show on the homepage of the search page
|
||||
/// </summary>
|
||||
public DbSet<SearchPageSliderEntry> SearchPageSliderEntries { get; set; }
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
base.OnModelCreating(modelBuilder);
|
||||
|
||||
modelBuilder.Entity<ArtifactEntry>()
|
||||
.HasMany(a => a.RelatedTo)
|
||||
.WithMany(a => a.RelatedBy)
|
||||
.UsingEntity(j => j.ToTable("ArtifactRelationships"));
|
||||
|
||||
modelBuilder.Entity<ArtifactEntry>()
|
||||
.HasOne(a => a.StorageLocation)
|
||||
.WithMany(l => l.ArtifactEntries);
|
||||
|
||||
modelBuilder.Entity<ArtifactGroupingViewCount>()
|
||||
.HasOne(vc => vc.Grouping)
|
||||
.WithOne(g => g.ViewCount)
|
||||
.HasForeignKey<ArtifactGroupingViewCount>(vc => vc.ArtifactGroupingId);
|
||||
|
||||
modelBuilder.Entity<ArtifactGrouping>()
|
||||
.OwnsOne(p => p.IdentifierFields)
|
||||
.ToJson();
|
||||
|
||||
modelBuilder.Entity<ArtifactGrouping>()
|
||||
.HasMany(grouping => grouping.ChildArtifactEntries)
|
||||
.WithOne(entry => entry.ArtifactGrouping)
|
||||
.HasForeignKey(entry => entry.ArtifactGroupingId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
modelBuilder.Entity<ArtifactEntry>()
|
||||
.HasMany(entry => entry.Files)
|
||||
.WithOne(file => file.ParentArtifactEntry)
|
||||
.HasForeignKey(file => file.ParentArtifactEntryId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
modelBuilder.Entity<BlogPost>()
|
||||
.HasOne(p => p.MainPhoto)
|
||||
.WithOne(p => p.ParentBlogPost)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
modelBuilder.Entity<ArtifactGrouping>()
|
||||
.Navigation(g => g.IdentifierFields)
|
||||
.UsePropertyAccessMode(PropertyAccessMode.Field);
|
||||
|
||||
var dictionaryComparer = new ValueComparer<Dictionary<string, string>>(
|
||||
(dictionary1, dictionary2) => dictionary1.OrderBy(pair => pair.Key)
|
||||
.SequenceEqual(dictionary2.OrderBy(pair => pair.Key)),
|
||||
|
||||
dictionary => dictionary.Aggregate(
|
||||
0,
|
||||
(aggregatedHash, pair) => HashCode.Combine(aggregatedHash, pair.Key.GetHashCode(), pair.Value.GetHashCode())),
|
||||
|
||||
sourceDictionary => new Dictionary<string, string>(sourceDictionary)
|
||||
);
|
||||
|
||||
modelBuilder.Entity<BlogPost>()
|
||||
.HasOne(post => post.MainPhoto)
|
||||
.WithOne(file => file.ParentBlogPost)
|
||||
.HasForeignKey<FilePathListing>(file => file.ParentBlogPostId);
|
||||
|
||||
// Create the search vector columns for artifat groupings
|
||||
modelBuilder.Entity<ArtifactGrouping>()
|
||||
.HasGeneratedTsVectorColumn(
|
||||
p => p.AllSearchVector,
|
||||
"english",
|
||||
p => p.AllSearchString
|
||||
)
|
||||
.HasIndex(p => p.AllSearchVector)
|
||||
.HasMethod("GIN");
|
||||
|
||||
modelBuilder.Entity<ArtifactGrouping>()
|
||||
.HasGeneratedTsVectorColumn(
|
||||
p => p.TagsSearchVector,
|
||||
"english",
|
||||
p => p.TagsSearchString
|
||||
)
|
||||
.HasIndex(p => p.TagsSearchVector)
|
||||
.HasMethod("GIN");
|
||||
|
||||
modelBuilder.Entity<ArtifactGrouping>()
|
||||
.HasGeneratedTsVectorColumn(
|
||||
p => p.DefectsSearchVector,
|
||||
"english",
|
||||
p => p.DefectsSearchString
|
||||
)
|
||||
.HasIndex(p => p.DefectsSearchVector)
|
||||
.HasMethod("GIN");
|
||||
|
||||
modelBuilder.Entity<ArtifactGrouping>()
|
||||
.HasGeneratedTsVectorColumn(
|
||||
p => p.ListedNamesSearchVector,
|
||||
"english",
|
||||
p => p.ListedNamesSearchString
|
||||
)
|
||||
.HasIndex(p => p.ListedNamesSearchVector)
|
||||
.HasMethod("GIN");
|
||||
|
||||
modelBuilder.Entity<ArtifactGrouping>()
|
||||
.HasGeneratedTsVectorColumn(
|
||||
p => p.TitleSearchVector,
|
||||
"english",
|
||||
p => p.TitleSearchString
|
||||
)
|
||||
.HasIndex(p => p.TitleSearchVector)
|
||||
.HasMethod("GIN");
|
||||
|
||||
modelBuilder.Entity<ArtifactGrouping>()
|
||||
.HasGeneratedTsVectorColumn(
|
||||
p => p.DescriptionSearchVector,
|
||||
"english",
|
||||
p => p.DescriptionSearchString
|
||||
)
|
||||
.HasIndex(p => p.DescriptionSearchVector)
|
||||
.HasMethod("GIN");
|
||||
|
||||
modelBuilder.Entity<ArtifactGrouping>()
|
||||
.HasGeneratedTsVectorColumn(
|
||||
p => p.FilenamesSearchVector,
|
||||
"english",
|
||||
p => p.FilenamesSearchString
|
||||
)
|
||||
.HasIndex(p => p.FilenamesSearchVector)
|
||||
.HasMethod("GIN");
|
||||
|
||||
modelBuilder.Entity<ArtifactGrouping>()
|
||||
.HasGeneratedTsVectorColumn(
|
||||
p => p.FileContentSearchVector,
|
||||
"english",
|
||||
p => p.FileContentSearchString
|
||||
)
|
||||
.HasIndex(p => p.FileContentSearchVector)
|
||||
.HasMethod("GIN");
|
||||
|
||||
// Create the search vector columns for blog posts
|
||||
modelBuilder.Entity<BlogPost>()
|
||||
.HasGeneratedTsVectorColumn(
|
||||
p => p.ContentSearchVector,
|
||||
"english",
|
||||
p => p.Content)
|
||||
.HasIndex(p => p.ContentSearchVector)
|
||||
.HasMethod("GIN");
|
||||
|
||||
modelBuilder.Entity<BlogPost>()
|
||||
.HasGeneratedTsVectorColumn(
|
||||
p => p.TitleSearchVector,
|
||||
"english",
|
||||
p => p.Title)
|
||||
.HasIndex(p => p.TitleSearchVector)
|
||||
.HasMethod("GIN");
|
||||
|
||||
modelBuilder.Entity<BlogPost>()
|
||||
.HasGeneratedTsVectorColumn(
|
||||
p => p.TagsSearchVector,
|
||||
"english",
|
||||
p => p.TagsSearchString)
|
||||
.HasIndex(p => p.TagsSearchVector)
|
||||
.HasMethod("GIN");
|
||||
|
||||
modelBuilder.Entity<BlogPost>()
|
||||
.HasGeneratedTsVectorColumn(
|
||||
p => p.AllSearchVector,
|
||||
"english",
|
||||
p => p.AllSearchString)
|
||||
.HasIndex(p => p.AllSearchVector)
|
||||
.HasMethod("GIN");
|
||||
}
|
||||
}
|
||||
79
OpenArchival.DataAccess/GenerateSearchIndex.cs
Normal file
79
OpenArchival.DataAccess/GenerateSearchIndex.cs
Normal file
@@ -0,0 +1,79 @@
|
||||
using System.Text;
|
||||
|
||||
namespace OpenArchival.DataAccess;
|
||||
|
||||
public static class ArtifactGroupingExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Concatinates all data that can be searched on into large strings so that Postgres can turn them into search vectors
|
||||
/// </summary>
|
||||
/// <param name="grouping"></param>
|
||||
public static void GenerateSearchIndex(this ArtifactGrouping grouping)
|
||||
{
|
||||
var allSearchSb = new StringBuilder();
|
||||
var tagsSb = new StringBuilder();
|
||||
var defectsSb = new StringBuilder();
|
||||
var listedNamesSb = new StringBuilder();
|
||||
var titlesSb = new StringBuilder();
|
||||
var descriptionsSb = new StringBuilder();
|
||||
var filenamesSb = new StringBuilder();
|
||||
var fileContentSb = new StringBuilder();
|
||||
|
||||
// Put all the top level data in
|
||||
allSearchSb.Append($"{grouping.Title} ");
|
||||
titlesSb.Append($"{grouping.Title} ");
|
||||
|
||||
allSearchSb.Append($"{grouping.Description} ");
|
||||
descriptionsSb.Append($"{grouping.Description} ");
|
||||
|
||||
foreach (ArtifactEntry entry in grouping.ChildArtifactEntries)
|
||||
{
|
||||
allSearchSb.Append($"{entry.Title} ");
|
||||
titlesSb.Append($"{entry.Title} ");
|
||||
|
||||
allSearchSb.Append($"{entry.Description} ");
|
||||
descriptionsSb.Append($"{entry.Description} ");
|
||||
|
||||
foreach (ArtifactEntryTag tag in entry.Tags)
|
||||
{
|
||||
allSearchSb.Append($"{tag.Name} ");
|
||||
tagsSb.Append($"{tag.Name} ");
|
||||
}
|
||||
|
||||
if (entry.Defects is not null) {
|
||||
foreach (ArtifactDefect defect in entry.Defects)
|
||||
{
|
||||
allSearchSb.Append($"{defect.Description} ");
|
||||
defectsSb.Append($"{defect.Description} ");
|
||||
}
|
||||
}
|
||||
|
||||
if (entry.ListedNames is not null)
|
||||
{
|
||||
foreach (ListedName name in entry.ListedNames)
|
||||
{
|
||||
allSearchSb.Append($"{name.Value} ");
|
||||
listedNamesSb.Append($"{name.Value} ");
|
||||
}
|
||||
}
|
||||
|
||||
foreach (FilePathListing file in entry.Files)
|
||||
{
|
||||
allSearchSb.Append($"{file.OriginalName} ");
|
||||
filenamesSb.Append($"{file.OriginalName} ");
|
||||
}
|
||||
|
||||
fileContentSb.Append($"{entry.FileTextContent} ");
|
||||
allSearchSb.Append($"{entry.FileTextContent} ");
|
||||
}
|
||||
|
||||
grouping.AllSearchString = allSearchSb.ToString();
|
||||
grouping.TagsSearchString = tagsSb.ToString();
|
||||
grouping.DefectsSearchString = defectsSb.ToString();
|
||||
grouping.ListedNamesSearchString = listedNamesSb.ToString();
|
||||
grouping.TitleSearchString = titlesSb.ToString();
|
||||
grouping.DescriptionSearchString = descriptionsSb.ToString();
|
||||
grouping.FilenamesSearchString = filenamesSb.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
45
OpenArchival.DataAccess/IdentityDataSeeder.cs
Normal file
45
OpenArchival.DataAccess/IdentityDataSeeder.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
// Create a new file, e.g., /Data/IdentityDataSeeder.cs
|
||||
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using OpenArchival.DataAccess; // Your project's namespace for ApplicationUser
|
||||
|
||||
public static class IdentityDataSeeder
|
||||
{
|
||||
public static async Task SeedRolesAndAdminUserAsync(IServiceProvider serviceProvider)
|
||||
{
|
||||
// Get the required services
|
||||
var roleManager = serviceProvider.GetRequiredService<RoleManager<IdentityRole>>();
|
||||
var userManager = serviceProvider.GetRequiredService<UserManager<ApplicationUser>>();
|
||||
|
||||
// Create Roles If They Don't Exist
|
||||
string[] roleNames = {UserRoles.Admin, UserRoles.Writer, UserRoles.User};
|
||||
foreach (var roleName in roleNames)
|
||||
{
|
||||
if (!await roleManager.RoleExistsAsync(roleName))
|
||||
{
|
||||
await roleManager.CreateAsync(new IdentityRole(roleName));
|
||||
}
|
||||
}
|
||||
|
||||
// Create a Default Admin User If It Doesn't Exist
|
||||
var adminEmail = "admin@admin.com";
|
||||
if (await userManager.FindByEmailAsync(adminEmail) == null)
|
||||
{
|
||||
var adminUser = new ApplicationUser
|
||||
{
|
||||
UserName = adminEmail,
|
||||
Email = adminEmail,
|
||||
EmailConfirmed = true // Bypass email confirmation for the seeder
|
||||
};
|
||||
|
||||
// Be sure to use a strong password from configuration in a real app
|
||||
var result = await userManager.CreateAsync(adminUser, "StrongAdminPassword123!");
|
||||
|
||||
if (result.Succeeded)
|
||||
{
|
||||
// Assign the 'Admin' role to the new user
|
||||
await userManager.AddToRoleAsync(adminUser, "Admin");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1154
OpenArchival.DataAccess/Migrations/20251028013051_InitalCreate.Designer.cs
generated
Normal file
1154
OpenArchival.DataAccess/Migrations/20251028013051_InitalCreate.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,933 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
using NpgsqlTypes;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace OpenArchival.DataAccess.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class InitalCreate : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ArchiveCategories",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
Name = table.Column<string>(type: "text", nullable: false),
|
||||
Description = table.Column<string>(type: "text", nullable: true),
|
||||
FieldSeparator = table.Column<string>(type: "text", nullable: false),
|
||||
FieldNames = table.Column<List<string>>(type: "text[]", nullable: false),
|
||||
FieldDescriptions = table.Column<List<string>>(type: "text[]", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ArchiveCategories", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ArtifactAssociatedNames",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
Value = table.Column<string>(type: "text", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ArtifactAssociatedNames", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ArtifactDefects",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
Description = table.Column<string>(type: "text", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ArtifactDefects", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ArtifactEntryTags",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
Name = table.Column<string>(type: "text", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ArtifactEntryTags", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ArtifactStorageLocations",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
Location = table.Column<string>(type: "text", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ArtifactStorageLocations", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ArtifactTypes",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
Name = table.Column<string>(type: "text", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ArtifactTypes", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetRoles",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<string>(type: "text", nullable: false),
|
||||
Name = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
||||
NormalizedName = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
||||
ConcurrencyStamp = table.Column<string>(type: "text", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetRoles", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetUsers",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<string>(type: "text", nullable: false),
|
||||
PermissionLevel = table.Column<string>(type: "text", nullable: false),
|
||||
UserName = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
||||
NormalizedUserName = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
||||
Email = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
||||
NormalizedEmail = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
||||
EmailConfirmed = table.Column<bool>(type: "boolean", nullable: false),
|
||||
PasswordHash = table.Column<string>(type: "text", nullable: true),
|
||||
SecurityStamp = table.Column<string>(type: "text", nullable: true),
|
||||
ConcurrencyStamp = table.Column<string>(type: "text", nullable: true),
|
||||
PhoneNumber = table.Column<string>(type: "text", nullable: true),
|
||||
PhoneNumberConfirmed = table.Column<bool>(type: "boolean", nullable: false),
|
||||
TwoFactorEnabled = table.Column<bool>(type: "boolean", nullable: false),
|
||||
LockoutEnd = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
|
||||
LockoutEnabled = table.Column<bool>(type: "boolean", nullable: false),
|
||||
AccessFailedCount = table.Column<int>(type: "integer", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetUsers", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "BlogPosts",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
Title = table.Column<string>(type: "text", nullable: false),
|
||||
Content = table.Column<string>(type: "text", nullable: false),
|
||||
CreationTime = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||
ModifiedTime = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||
ContentSearchVector = table.Column<NpgsqlTsVector>(type: "tsvector", nullable: false)
|
||||
.Annotation("Npgsql:TsVectorConfig", "english")
|
||||
.Annotation("Npgsql:TsVectorProperties", new[] { "Content" }),
|
||||
TitleSearchVector = table.Column<NpgsqlTsVector>(type: "tsvector", nullable: false)
|
||||
.Annotation("Npgsql:TsVectorConfig", "english")
|
||||
.Annotation("Npgsql:TsVectorProperties", new[] { "Title" }),
|
||||
TagsSearchString = table.Column<string>(type: "text", nullable: false),
|
||||
TagsSearchVector = table.Column<NpgsqlTsVector>(type: "tsvector", nullable: false)
|
||||
.Annotation("Npgsql:TsVectorConfig", "english")
|
||||
.Annotation("Npgsql:TsVectorProperties", new[] { "TagsSearchString" }),
|
||||
AllSearchString = table.Column<string>(type: "text", nullable: false),
|
||||
AllSearchVector = table.Column<NpgsqlTsVector>(type: "tsvector", nullable: false)
|
||||
.Annotation("Npgsql:TsVectorConfig", "english")
|
||||
.Annotation("Npgsql:TsVectorProperties", new[] { "AllSearchString" })
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_BlogPosts", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "BlogPostTags",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
Name = table.Column<string>(type: "text", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_BlogPostTags", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "SearchPageSliderEntries",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
Title = table.Column<string>(type: "text", nullable: false),
|
||||
Description = table.Column<string>(type: "text", nullable: false),
|
||||
MaxCount = table.Column<int>(type: "integer", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_SearchPageSliderEntries", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ArtifactGroupings",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
CategoryId = table.Column<int>(type: "integer", nullable: false),
|
||||
Title = table.Column<string>(type: "text", nullable: false),
|
||||
Description = table.Column<string>(type: "text", nullable: true),
|
||||
TypeId = table.Column<int>(type: "integer", nullable: false),
|
||||
IsPublicallyVisible = table.Column<bool>(type: "boolean", nullable: false),
|
||||
AllSearchString = table.Column<string>(type: "text", nullable: false),
|
||||
AllSearchVector = table.Column<NpgsqlTsVector>(type: "tsvector", nullable: true)
|
||||
.Annotation("Npgsql:TsVectorConfig", "english")
|
||||
.Annotation("Npgsql:TsVectorProperties", new[] { "AllSearchString" }),
|
||||
TagsSearchString = table.Column<string>(type: "text", nullable: false),
|
||||
TagsSearchVector = table.Column<NpgsqlTsVector>(type: "tsvector", nullable: true)
|
||||
.Annotation("Npgsql:TsVectorConfig", "english")
|
||||
.Annotation("Npgsql:TsVectorProperties", new[] { "TagsSearchString" }),
|
||||
DefectsSearchString = table.Column<string>(type: "text", nullable: false),
|
||||
DefectsSearchVector = table.Column<NpgsqlTsVector>(type: "tsvector", nullable: true)
|
||||
.Annotation("Npgsql:TsVectorConfig", "english")
|
||||
.Annotation("Npgsql:TsVectorProperties", new[] { "DefectsSearchString" }),
|
||||
ListedNamesSearchString = table.Column<string>(type: "text", nullable: false),
|
||||
ListedNamesSearchVector = table.Column<NpgsqlTsVector>(type: "tsvector", nullable: true)
|
||||
.Annotation("Npgsql:TsVectorConfig", "english")
|
||||
.Annotation("Npgsql:TsVectorProperties", new[] { "ListedNamesSearchString" }),
|
||||
TitleSearchString = table.Column<string>(type: "text", nullable: false),
|
||||
TitleSearchVector = table.Column<NpgsqlTsVector>(type: "tsvector", nullable: true)
|
||||
.Annotation("Npgsql:TsVectorConfig", "english")
|
||||
.Annotation("Npgsql:TsVectorProperties", new[] { "TitleSearchString" }),
|
||||
DescriptionSearchString = table.Column<string>(type: "text", nullable: false),
|
||||
DescriptionSearchVector = table.Column<NpgsqlTsVector>(type: "tsvector", nullable: true)
|
||||
.Annotation("Npgsql:TsVectorConfig", "english")
|
||||
.Annotation("Npgsql:TsVectorProperties", new[] { "DescriptionSearchString" }),
|
||||
FilenamesSearchString = table.Column<string>(type: "text", nullable: false),
|
||||
FilenamesSearchVector = table.Column<NpgsqlTsVector>(type: "tsvector", nullable: false)
|
||||
.Annotation("Npgsql:TsVectorConfig", "english")
|
||||
.Annotation("Npgsql:TsVectorProperties", new[] { "FilenamesSearchString" }),
|
||||
FileContentSearchString = table.Column<string>(type: "text", nullable: false),
|
||||
FileContentSearchVector = table.Column<NpgsqlTsVector>(type: "tsvector", nullable: false)
|
||||
.Annotation("Npgsql:TsVectorConfig", "english")
|
||||
.Annotation("Npgsql:TsVectorProperties", new[] { "FileContentSearchString" }),
|
||||
IdentifierFields = table.Column<string>(type: "jsonb", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ArtifactGroupings", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_ArtifactGroupings_ArchiveCategories_CategoryId",
|
||||
column: x => x.CategoryId,
|
||||
principalTable: "ArchiveCategories",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_ArtifactGroupings_ArtifactTypes_TypeId",
|
||||
column: x => x.TypeId,
|
||||
principalTable: "ArtifactTypes",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetRoleClaims",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
RoleId = table.Column<string>(type: "text", nullable: false),
|
||||
ClaimType = table.Column<string>(type: "text", nullable: true),
|
||||
ClaimValue = table.Column<string>(type: "text", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_AspNetRoleClaims_AspNetRoles_RoleId",
|
||||
column: x => x.RoleId,
|
||||
principalTable: "AspNetRoles",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetUserClaims",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
UserId = table.Column<string>(type: "text", nullable: false),
|
||||
ClaimType = table.Column<string>(type: "text", nullable: true),
|
||||
ClaimValue = table.Column<string>(type: "text", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetUserClaims", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_AspNetUserClaims_AspNetUsers_UserId",
|
||||
column: x => x.UserId,
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetUserLogins",
|
||||
columns: table => new
|
||||
{
|
||||
LoginProvider = table.Column<string>(type: "text", nullable: false),
|
||||
ProviderKey = table.Column<string>(type: "text", nullable: false),
|
||||
ProviderDisplayName = table.Column<string>(type: "text", nullable: true),
|
||||
UserId = table.Column<string>(type: "text", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey });
|
||||
table.ForeignKey(
|
||||
name: "FK_AspNetUserLogins_AspNetUsers_UserId",
|
||||
column: x => x.UserId,
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetUserRoles",
|
||||
columns: table => new
|
||||
{
|
||||
UserId = table.Column<string>(type: "text", nullable: false),
|
||||
RoleId = table.Column<string>(type: "text", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId });
|
||||
table.ForeignKey(
|
||||
name: "FK_AspNetUserRoles_AspNetRoles_RoleId",
|
||||
column: x => x.RoleId,
|
||||
principalTable: "AspNetRoles",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_AspNetUserRoles_AspNetUsers_UserId",
|
||||
column: x => x.UserId,
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetUserTokens",
|
||||
columns: table => new
|
||||
{
|
||||
UserId = table.Column<string>(type: "text", nullable: false),
|
||||
LoginProvider = table.Column<string>(type: "text", nullable: false),
|
||||
Name = table.Column<string>(type: "text", nullable: false),
|
||||
Value = table.Column<string>(type: "text", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name });
|
||||
table.ForeignKey(
|
||||
name: "FK_AspNetUserTokens_AspNetUsers_UserId",
|
||||
column: x => x.UserId,
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "BlogPostViews",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
BlogPostId = table.Column<int>(type: "integer", nullable: false),
|
||||
Views = table.Column<int>(type: "integer", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_BlogPostViews", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_BlogPostViews_BlogPosts_BlogPostId",
|
||||
column: x => x.BlogPostId,
|
||||
principalTable: "BlogPosts",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "BlogPostBlogPostTag",
|
||||
columns: table => new
|
||||
{
|
||||
BlogPostsId = table.Column<int>(type: "integer", nullable: false),
|
||||
TagsId = table.Column<int>(type: "integer", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_BlogPostBlogPostTag", x => new { x.BlogPostsId, x.TagsId });
|
||||
table.ForeignKey(
|
||||
name: "FK_BlogPostBlogPostTag_BlogPostTags_TagsId",
|
||||
column: x => x.TagsId,
|
||||
principalTable: "BlogPostTags",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_BlogPostBlogPostTag_BlogPosts_BlogPostsId",
|
||||
column: x => x.BlogPostsId,
|
||||
principalTable: "BlogPosts",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ArtifactEntryTagSearchPageSliderEntry",
|
||||
columns: table => new
|
||||
{
|
||||
FilterTagsId = table.Column<int>(type: "integer", nullable: false),
|
||||
SearchPageSlidersId = table.Column<int>(type: "integer", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ArtifactEntryTagSearchPageSliderEntry", x => new { x.FilterTagsId, x.SearchPageSlidersId });
|
||||
table.ForeignKey(
|
||||
name: "FK_ArtifactEntryTagSearchPageSliderEntry_ArtifactEntryTags_Fil~",
|
||||
column: x => x.FilterTagsId,
|
||||
principalTable: "ArtifactEntryTags",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_ArtifactEntryTagSearchPageSliderEntry_SearchPageSliderEntri~",
|
||||
column: x => x.SearchPageSlidersId,
|
||||
principalTable: "SearchPageSliderEntries",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ArtifactEntries",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
ArtifactNumber = table.Column<string>(type: "text", nullable: true),
|
||||
Title = table.Column<string>(type: "text", nullable: false),
|
||||
Description = table.Column<string>(type: "text", nullable: true),
|
||||
StorageLocationId = table.Column<int>(type: "integer", nullable: false),
|
||||
AssociatedDates = table.Column<List<DateTime>>(type: "timestamp with time zone[]", nullable: true),
|
||||
Links = table.Column<List<string>>(type: "text[]", nullable: true),
|
||||
FileTextContent = table.Column<string>(type: "text", nullable: true),
|
||||
TypeId = table.Column<int>(type: "integer", nullable: false),
|
||||
IsPubliclyVisible = table.Column<bool>(type: "boolean", nullable: false),
|
||||
Quantity = table.Column<int>(type: "integer", nullable: false),
|
||||
ArtifactGroupingId = table.Column<int>(type: "integer", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ArtifactEntries", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_ArtifactEntries_ArtifactGroupings_ArtifactGroupingId",
|
||||
column: x => x.ArtifactGroupingId,
|
||||
principalTable: "ArtifactGroupings",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_ArtifactEntries_ArtifactStorageLocations_StorageLocationId",
|
||||
column: x => x.StorageLocationId,
|
||||
principalTable: "ArtifactStorageLocations",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_ArtifactEntries_ArtifactTypes_TypeId",
|
||||
column: x => x.TypeId,
|
||||
principalTable: "ArtifactTypes",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ArtifactGroupingBlogPost",
|
||||
columns: table => new
|
||||
{
|
||||
ArtifactGroupingsId = table.Column<int>(type: "integer", nullable: false),
|
||||
BlogPostsId = table.Column<int>(type: "integer", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ArtifactGroupingBlogPost", x => new { x.ArtifactGroupingsId, x.BlogPostsId });
|
||||
table.ForeignKey(
|
||||
name: "FK_ArtifactGroupingBlogPost_ArtifactGroupings_ArtifactGrouping~",
|
||||
column: x => x.ArtifactGroupingsId,
|
||||
principalTable: "ArtifactGroupings",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_ArtifactGroupingBlogPost_BlogPosts_BlogPostsId",
|
||||
column: x => x.BlogPostsId,
|
||||
principalTable: "BlogPosts",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ArtifactGroupingViewCounts",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
ArtifactGroupingId = table.Column<int>(type: "integer", nullable: false),
|
||||
Views = table.Column<int>(type: "integer", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ArtifactGroupingViewCounts", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_ArtifactGroupingViewCounts_ArtifactGroupings_ArtifactGroupi~",
|
||||
column: x => x.ArtifactGroupingId,
|
||||
principalTable: "ArtifactGroupings",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ArtifactDefectArtifactEntry",
|
||||
columns: table => new
|
||||
{
|
||||
ArtifactEntriesId = table.Column<int>(type: "integer", nullable: false),
|
||||
DefectsId = table.Column<int>(type: "integer", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ArtifactDefectArtifactEntry", x => new { x.ArtifactEntriesId, x.DefectsId });
|
||||
table.ForeignKey(
|
||||
name: "FK_ArtifactDefectArtifactEntry_ArtifactDefects_DefectsId",
|
||||
column: x => x.DefectsId,
|
||||
principalTable: "ArtifactDefects",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_ArtifactDefectArtifactEntry_ArtifactEntries_ArtifactEntries~",
|
||||
column: x => x.ArtifactEntriesId,
|
||||
principalTable: "ArtifactEntries",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ArtifactEntryArtifactEntryTag",
|
||||
columns: table => new
|
||||
{
|
||||
ArtifactEntriesId = table.Column<int>(type: "integer", nullable: false),
|
||||
TagsId = table.Column<int>(type: "integer", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ArtifactEntryArtifactEntryTag", x => new { x.ArtifactEntriesId, x.TagsId });
|
||||
table.ForeignKey(
|
||||
name: "FK_ArtifactEntryArtifactEntryTag_ArtifactEntries_ArtifactEntri~",
|
||||
column: x => x.ArtifactEntriesId,
|
||||
principalTable: "ArtifactEntries",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_ArtifactEntryArtifactEntryTag_ArtifactEntryTags_TagsId",
|
||||
column: x => x.TagsId,
|
||||
principalTable: "ArtifactEntryTags",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ArtifactEntryListedName",
|
||||
columns: table => new
|
||||
{
|
||||
ArtifactEntriesId = table.Column<int>(type: "integer", nullable: false),
|
||||
ListedNamesId = table.Column<int>(type: "integer", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ArtifactEntryListedName", x => new { x.ArtifactEntriesId, x.ListedNamesId });
|
||||
table.ForeignKey(
|
||||
name: "FK_ArtifactEntryListedName_ArtifactAssociatedNames_ListedNames~",
|
||||
column: x => x.ListedNamesId,
|
||||
principalTable: "ArtifactAssociatedNames",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_ArtifactEntryListedName_ArtifactEntries_ArtifactEntriesId",
|
||||
column: x => x.ArtifactEntriesId,
|
||||
principalTable: "ArtifactEntries",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ArtifactFilePaths",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
ParentArtifactEntryId = table.Column<int>(type: "integer", nullable: true),
|
||||
ParentBlogPostId = table.Column<int>(type: "integer", nullable: true),
|
||||
OriginalName = table.Column<string>(type: "text", nullable: false),
|
||||
Path = table.Column<string>(type: "text", nullable: false),
|
||||
ArtifactGroupingId = table.Column<int>(type: "integer", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ArtifactFilePaths", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_ArtifactFilePaths_ArtifactEntries_ParentArtifactEntryId",
|
||||
column: x => x.ParentArtifactEntryId,
|
||||
principalTable: "ArtifactEntries",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_ArtifactFilePaths_ArtifactGroupings_ArtifactGroupingId",
|
||||
column: x => x.ArtifactGroupingId,
|
||||
principalTable: "ArtifactGroupings",
|
||||
principalColumn: "Id");
|
||||
table.ForeignKey(
|
||||
name: "FK_ArtifactFilePaths_BlogPosts_ParentBlogPostId",
|
||||
column: x => x.ParentBlogPostId,
|
||||
principalTable: "BlogPosts",
|
||||
principalColumn: "Id");
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ArtifactRelationships",
|
||||
columns: table => new
|
||||
{
|
||||
RelatedById = table.Column<int>(type: "integer", nullable: false),
|
||||
RelatedToId = table.Column<int>(type: "integer", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ArtifactRelationships", x => new { x.RelatedById, x.RelatedToId });
|
||||
table.ForeignKey(
|
||||
name: "FK_ArtifactRelationships_ArtifactEntries_RelatedById",
|
||||
column: x => x.RelatedById,
|
||||
principalTable: "ArtifactEntries",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_ArtifactRelationships_ArtifactEntries_RelatedToId",
|
||||
column: x => x.RelatedToId,
|
||||
principalTable: "ArtifactEntries",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ArtifactDefectArtifactEntry_DefectsId",
|
||||
table: "ArtifactDefectArtifactEntry",
|
||||
column: "DefectsId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ArtifactEntries_ArtifactGroupingId",
|
||||
table: "ArtifactEntries",
|
||||
column: "ArtifactGroupingId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ArtifactEntries_StorageLocationId",
|
||||
table: "ArtifactEntries",
|
||||
column: "StorageLocationId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ArtifactEntries_TypeId",
|
||||
table: "ArtifactEntries",
|
||||
column: "TypeId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ArtifactEntryArtifactEntryTag_TagsId",
|
||||
table: "ArtifactEntryArtifactEntryTag",
|
||||
column: "TagsId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ArtifactEntryListedName_ListedNamesId",
|
||||
table: "ArtifactEntryListedName",
|
||||
column: "ListedNamesId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ArtifactEntryTagSearchPageSliderEntry_SearchPageSlidersId",
|
||||
table: "ArtifactEntryTagSearchPageSliderEntry",
|
||||
column: "SearchPageSlidersId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ArtifactFilePaths_ArtifactGroupingId",
|
||||
table: "ArtifactFilePaths",
|
||||
column: "ArtifactGroupingId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ArtifactFilePaths_ParentArtifactEntryId",
|
||||
table: "ArtifactFilePaths",
|
||||
column: "ParentArtifactEntryId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ArtifactFilePaths_ParentBlogPostId",
|
||||
table: "ArtifactFilePaths",
|
||||
column: "ParentBlogPostId",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ArtifactGroupingBlogPost_BlogPostsId",
|
||||
table: "ArtifactGroupingBlogPost",
|
||||
column: "BlogPostsId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ArtifactGroupings_AllSearchVector",
|
||||
table: "ArtifactGroupings",
|
||||
column: "AllSearchVector")
|
||||
.Annotation("Npgsql:IndexMethod", "GIN");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ArtifactGroupings_CategoryId",
|
||||
table: "ArtifactGroupings",
|
||||
column: "CategoryId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ArtifactGroupings_DefectsSearchVector",
|
||||
table: "ArtifactGroupings",
|
||||
column: "DefectsSearchVector")
|
||||
.Annotation("Npgsql:IndexMethod", "GIN");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ArtifactGroupings_DescriptionSearchVector",
|
||||
table: "ArtifactGroupings",
|
||||
column: "DescriptionSearchVector")
|
||||
.Annotation("Npgsql:IndexMethod", "GIN");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ArtifactGroupings_FileContentSearchVector",
|
||||
table: "ArtifactGroupings",
|
||||
column: "FileContentSearchVector")
|
||||
.Annotation("Npgsql:IndexMethod", "GIN");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ArtifactGroupings_FilenamesSearchVector",
|
||||
table: "ArtifactGroupings",
|
||||
column: "FilenamesSearchVector")
|
||||
.Annotation("Npgsql:IndexMethod", "GIN");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ArtifactGroupings_ListedNamesSearchVector",
|
||||
table: "ArtifactGroupings",
|
||||
column: "ListedNamesSearchVector")
|
||||
.Annotation("Npgsql:IndexMethod", "GIN");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ArtifactGroupings_TagsSearchVector",
|
||||
table: "ArtifactGroupings",
|
||||
column: "TagsSearchVector")
|
||||
.Annotation("Npgsql:IndexMethod", "GIN");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ArtifactGroupings_TitleSearchVector",
|
||||
table: "ArtifactGroupings",
|
||||
column: "TitleSearchVector")
|
||||
.Annotation("Npgsql:IndexMethod", "GIN");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ArtifactGroupings_TypeId",
|
||||
table: "ArtifactGroupings",
|
||||
column: "TypeId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ArtifactGroupingViewCounts_ArtifactGroupingId",
|
||||
table: "ArtifactGroupingViewCounts",
|
||||
column: "ArtifactGroupingId",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ArtifactRelationships_RelatedToId",
|
||||
table: "ArtifactRelationships",
|
||||
column: "RelatedToId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_AspNetRoleClaims_RoleId",
|
||||
table: "AspNetRoleClaims",
|
||||
column: "RoleId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "RoleNameIndex",
|
||||
table: "AspNetRoles",
|
||||
column: "NormalizedName",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_AspNetUserClaims_UserId",
|
||||
table: "AspNetUserClaims",
|
||||
column: "UserId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_AspNetUserLogins_UserId",
|
||||
table: "AspNetUserLogins",
|
||||
column: "UserId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_AspNetUserRoles_RoleId",
|
||||
table: "AspNetUserRoles",
|
||||
column: "RoleId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "EmailIndex",
|
||||
table: "AspNetUsers",
|
||||
column: "NormalizedEmail");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "UserNameIndex",
|
||||
table: "AspNetUsers",
|
||||
column: "NormalizedUserName",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_BlogPostBlogPostTag_TagsId",
|
||||
table: "BlogPostBlogPostTag",
|
||||
column: "TagsId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_BlogPosts_AllSearchVector",
|
||||
table: "BlogPosts",
|
||||
column: "AllSearchVector")
|
||||
.Annotation("Npgsql:IndexMethod", "GIN");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_BlogPosts_ContentSearchVector",
|
||||
table: "BlogPosts",
|
||||
column: "ContentSearchVector")
|
||||
.Annotation("Npgsql:IndexMethod", "GIN");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_BlogPosts_TagsSearchVector",
|
||||
table: "BlogPosts",
|
||||
column: "TagsSearchVector")
|
||||
.Annotation("Npgsql:IndexMethod", "GIN");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_BlogPosts_TitleSearchVector",
|
||||
table: "BlogPosts",
|
||||
column: "TitleSearchVector")
|
||||
.Annotation("Npgsql:IndexMethod", "GIN");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_BlogPostViews_BlogPostId",
|
||||
table: "BlogPostViews",
|
||||
column: "BlogPostId",
|
||||
unique: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "ArtifactDefectArtifactEntry");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ArtifactEntryArtifactEntryTag");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ArtifactEntryListedName");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ArtifactEntryTagSearchPageSliderEntry");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ArtifactFilePaths");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ArtifactGroupingBlogPost");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ArtifactGroupingViewCounts");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ArtifactRelationships");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetRoleClaims");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetUserClaims");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetUserLogins");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetUserRoles");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetUserTokens");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "BlogPostBlogPostTag");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "BlogPostViews");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ArtifactDefects");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ArtifactAssociatedNames");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ArtifactEntryTags");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "SearchPageSliderEntries");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ArtifactEntries");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetRoles");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetUsers");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "BlogPostTags");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "BlogPosts");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ArtifactGroupings");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ArtifactStorageLocations");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ArchiveCategories");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ArtifactTypes");
|
||||
}
|
||||
}
|
||||
}
|
||||
1155
OpenArchival.DataAccess/Migrations/20251107162952_FixedBlogPostFileCascade.Designer.cs
generated
Normal file
1155
OpenArchival.DataAccess/Migrations/20251107162952_FixedBlogPostFileCascade.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,41 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace OpenArchival.DataAccess.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class FixedBlogPostFileCascade : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_ArtifactFilePaths_BlogPosts_ParentBlogPostId",
|
||||
table: "ArtifactFilePaths");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_ArtifactFilePaths_BlogPosts_ParentBlogPostId",
|
||||
table: "ArtifactFilePaths",
|
||||
column: "ParentBlogPostId",
|
||||
principalTable: "BlogPosts",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_ArtifactFilePaths_BlogPosts_ParentBlogPostId",
|
||||
table: "ArtifactFilePaths");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_ArtifactFilePaths_BlogPosts_ParentBlogPostId",
|
||||
table: "ArtifactFilePaths",
|
||||
column: "ParentBlogPostId",
|
||||
principalTable: "BlogPosts",
|
||||
principalColumn: "Id");
|
||||
}
|
||||
}
|
||||
}
|
||||
1144
OpenArchival.DataAccess/Migrations/20251112233520_FixErrorFilePathForeignKey.Designer.cs
generated
Normal file
1144
OpenArchival.DataAccess/Migrations/20251112233520_FixErrorFilePathForeignKey.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,48 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace OpenArchival.DataAccess.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class FixErrorFilePathForeignKey : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_ArtifactFilePaths_ArtifactGroupings_ArtifactGroupingId",
|
||||
table: "ArtifactFilePaths");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_ArtifactFilePaths_ArtifactGroupingId",
|
||||
table: "ArtifactFilePaths");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "ArtifactGroupingId",
|
||||
table: "ArtifactFilePaths");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "ArtifactGroupingId",
|
||||
table: "ArtifactFilePaths",
|
||||
type: "integer",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ArtifactFilePaths_ArtifactGroupingId",
|
||||
table: "ArtifactFilePaths",
|
||||
column: "ArtifactGroupingId");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_ArtifactFilePaths_ArtifactGroupings_ArtifactGroupingId",
|
||||
table: "ArtifactFilePaths",
|
||||
column: "ArtifactGroupingId",
|
||||
principalTable: "ArtifactGroupings",
|
||||
principalColumn: "Id");
|
||||
}
|
||||
}
|
||||
}
|
||||
1140
OpenArchival.DataAccess/Migrations/20260301235559_UpdatedUsers.Designer.cs
generated
Normal file
1140
OpenArchival.DataAccess/Migrations/20260301235559_UpdatedUsers.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,29 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace OpenArchival.DataAccess.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class UpdatedUsers : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "PermissionLevel",
|
||||
table: "AspNetUsers");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "PermissionLevel",
|
||||
table: "AspNetUsers",
|
||||
type: "text",
|
||||
nullable: false,
|
||||
defaultValue: "");
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
8
OpenArchival.DataAccess/Models/ApplicationUser.cs
Normal file
8
OpenArchival.DataAccess/Models/ApplicationUser.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
||||
namespace OpenArchival.DataAccess;
|
||||
|
||||
// Add profile data for application users by adding properties to the ApplicationUser class
|
||||
public class ApplicationUser : IdentityUser
|
||||
{
|
||||
}
|
||||
21
OpenArchival.DataAccess/Models/ArchiveCategory.cs
Normal file
21
OpenArchival.DataAccess/Models/ArchiveCategory.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
namespace OpenArchival.DataAccess;
|
||||
|
||||
public class ArchiveCategory
|
||||
{
|
||||
[Key]
|
||||
public int Id { get; set; }
|
||||
|
||||
[System.ComponentModel.DataAnnotations.Schema.Index(IsUnique =true)]
|
||||
public required string Name { get; set; }
|
||||
|
||||
public string? Description { get; set; }
|
||||
|
||||
public required string FieldSeparator { get; set; } = "-";
|
||||
|
||||
public List<string> FieldNames { get; set; } = [];
|
||||
|
||||
public List<string> FieldDescriptions { get; set; } = [];
|
||||
}
|
||||
20
OpenArchival.DataAccess/Models/ArtifactDefect.cs
Normal file
20
OpenArchival.DataAccess/Models/ArtifactDefect.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace OpenArchival.DataAccess;
|
||||
|
||||
public class ArtifactDefect
|
||||
{
|
||||
[Key]
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public int Id { get; set; }
|
||||
|
||||
public required string Description { get; set; }
|
||||
|
||||
public List<ArtifactEntry> ArtifactEntries { get; set; } = [];
|
||||
}
|
||||
99
OpenArchival.DataAccess/Models/ArtifactEntry.cs
Normal file
99
OpenArchival.DataAccess/Models/ArtifactEntry.cs
Normal file
@@ -0,0 +1,99 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Text;
|
||||
|
||||
namespace OpenArchival.DataAccess;
|
||||
|
||||
public class ArtifactEntry
|
||||
{
|
||||
[Key]
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This value gets appended on the end of the contianing ArtifactGrouping's
|
||||
/// Category value
|
||||
/// </summary>
|
||||
public string? ArtifactIdentifier
|
||||
{
|
||||
get
|
||||
{
|
||||
return (ArtifactGrouping is not null)
|
||||
? ModelHelpers.MakeIdentifier(ArtifactGrouping.IdentifierFields.Values, ArtifactGrouping.Category.FieldSeparator, ArtifactNumber)
|
||||
: null;
|
||||
}
|
||||
}
|
||||
|
||||
public string? ArtifactNumber { get; set; }
|
||||
|
||||
public required string Title { get; set; }
|
||||
|
||||
public string? Description { get; set; }
|
||||
|
||||
public required ArtifactStorageLocation StorageLocation { get; set; }
|
||||
|
||||
//public List<ArtifactEntryTag>? Tags { get; set; } = [];
|
||||
|
||||
public List<ArtifactEntryTag> Tags { get; set; } = [];
|
||||
|
||||
public List<ListedName>? ListedNames { get; set; } = [];
|
||||
|
||||
public List<DateTime>? AssociatedDates { get; set; } = [];
|
||||
|
||||
public List<ArtifactDefect>? Defects { get; set; } = [];
|
||||
|
||||
public List<string>? Links { get; set; } = [];
|
||||
|
||||
public required List<FilePathListing> Files { get; set; } = [];
|
||||
|
||||
public string? FileTextContent { get; set; } = null;
|
||||
|
||||
public required ArtifactType Type { get; set; }
|
||||
|
||||
public bool IsPubliclyVisible { get; set; }
|
||||
|
||||
public int Quantity { get; set; }
|
||||
|
||||
|
||||
// Relationships this artifact has TO other artifacts
|
||||
public List<ArtifactEntry> RelatedTo { get; set; } = [];
|
||||
|
||||
// Relationships other artifacts have TO this artifact
|
||||
public List<ArtifactEntry> RelatedBy { get; set; } = [];
|
||||
|
||||
|
||||
public int ArtifactGroupingId { get; set; }
|
||||
|
||||
public required ArtifactGrouping ArtifactGrouping { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
|
||||
sb.AppendLine($"--- ArtifactEntry (ID: {Id}) ---");
|
||||
sb.AppendLine($" Title: {Title}");
|
||||
sb.AppendLine($" ArtifactIdentifier: {ArtifactIdentifier ?? "N/A"}");
|
||||
sb.AppendLine($" ArtifactNumber: {ArtifactNumber ?? "N/A"}");
|
||||
sb.AppendLine($" StorageLocation: {StorageLocation}"); // Assumes ArtifactStorageLocation has a useful ToString()
|
||||
sb.AppendLine($" IsPubliclyVisible: {IsPubliclyVisible}");
|
||||
|
||||
// Handle Description (it could be long, so you might truncate it if needed)
|
||||
sb.AppendLine($" Description: {(string.IsNullOrWhiteSpace(Description) ? "N/A" : Description)}");
|
||||
|
||||
// Handle Lists
|
||||
sb.AppendLine($" Tags: {(Tags is not null && Tags.Any() ? string.Join(", ", Tags) : "None")}");
|
||||
sb.AppendLine($" ListedNames: {(ListedNames is not null && ListedNames.Any() ? string.Join(", ", ListedNames) : "None")}");
|
||||
sb.AppendLine($" AssociatedDates: {(AssociatedDates is not null && AssociatedDates.Any() ? string.Join(", ", AssociatedDates.Select(d => d.ToShortDateString())) : "None")}");
|
||||
sb.AppendLine($" Defects: {(Defects is not null && Defects.Any() ? string.Join(", ", Defects) : "None")}");
|
||||
sb.AppendLine($" Links: {(Links is not null && Links.Any() ? string.Join(", ", Links) : "None")}");
|
||||
sb.AppendLine($" Files: {(Files is not null && Files.Any() ? string.Join(", ", Files) : "None")}");
|
||||
|
||||
// Handle potentially very large text content
|
||||
sb.AppendLine($" FileTextContent: {(string.IsNullOrEmpty(FileTextContent) ? "Not Present" : $"Present (Length: {FileTextContent.Length})")}");
|
||||
|
||||
sb.Append("--------------------------");
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
27
OpenArchival.DataAccess/Models/ArtifactEntryTag.cs
Normal file
27
OpenArchival.DataAccess/Models/ArtifactEntryTag.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace OpenArchival.DataAccess;
|
||||
|
||||
public class ArtifactEntryTag
|
||||
{
|
||||
[Key]
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public int Id { get; set; }
|
||||
|
||||
[Index(IsUnique = true)]
|
||||
public required string Name { get; set; }
|
||||
|
||||
public List<ArtifactEntry> ArtifactEntries { get; set; } = [];
|
||||
|
||||
public List<SearchPageSliderEntry> SearchPageSliders { get; set; } = [];
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
}
|
||||
158
OpenArchival.DataAccess/Models/ArtifactGrouping.cs
Normal file
158
OpenArchival.DataAccess/Models/ArtifactGrouping.cs
Normal file
@@ -0,0 +1,158 @@
|
||||
using NpgsqlTypes;
|
||||
using Persic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Text;
|
||||
|
||||
namespace OpenArchival.DataAccess;
|
||||
|
||||
public class ArtifactGrouping
|
||||
{
|
||||
[Key]
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public int Id { get; set; }
|
||||
|
||||
public string? ArtifactGroupingIdentifier
|
||||
{
|
||||
get
|
||||
{
|
||||
return ModelHelpers.MakeIdentifier(IdentifierFields.Values, Category.FieldSeparator, null);
|
||||
}
|
||||
}
|
||||
|
||||
[NotMapped]
|
||||
public List<FilePathListing> ChildFilePathListings
|
||||
{
|
||||
get
|
||||
{
|
||||
var list = new List<FilePathListing>();
|
||||
foreach (ArtifactEntry entry in ChildArtifactEntries)
|
||||
{
|
||||
list.AddRange(entry.Files);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
public required ArchiveCategory Category { get; set; }
|
||||
|
||||
private IdentifierFields _identifierFields;
|
||||
public required IdentifierFields IdentifierFields
|
||||
{
|
||||
get => _identifierFields;
|
||||
set
|
||||
{
|
||||
if (value.Values.Count != Category.FieldNames.Count)
|
||||
{
|
||||
throw new ArgumentException(nameof(IdentifierFields), $"The number of field values must be equal to the field count of the {nameof(ArchiveCategory)}");
|
||||
}
|
||||
|
||||
_identifierFields = value;
|
||||
}
|
||||
}
|
||||
|
||||
public required string Title { get; set; }
|
||||
|
||||
public string? Description { get; set; }
|
||||
|
||||
public ArtifactType Type { get; set; }
|
||||
|
||||
public bool IsPublicallyVisible { get; set; }
|
||||
|
||||
public required List<ArtifactEntry> ChildArtifactEntries { get; set; } = new();
|
||||
|
||||
public ArtifactGroupingViewCount? ViewCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The list of all blog posts about this grouping
|
||||
/// </summary>
|
||||
public List<BlogPost> BlogPosts { get; set; } = [];
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
|
||||
sb.AppendLine($"Id: {Id}");
|
||||
sb.AppendLine($"Artifact Grouping Identifier: {ArtifactGroupingIdentifier}");
|
||||
sb.AppendLine($"Category:");
|
||||
sb.AppendLine(Category.ToString());
|
||||
sb.AppendLine($"Title: {Title}");
|
||||
sb.AppendLine($"Description: {Description}");
|
||||
sb.AppendLine($"Type:{Type}");
|
||||
sb.AppendLine($"Publically Visible: {IsPublicallyVisible}");
|
||||
sb.AppendLine($"Artifact Entries:");
|
||||
foreach (var artifact in ChildArtifactEntries)
|
||||
{
|
||||
sb.AppendLine(artifact.ToString());
|
||||
sb.AppendLine();
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
[NotMapped]
|
||||
public IEnumerable<ArtifactEntryTag> ChildTags
|
||||
{
|
||||
get
|
||||
{
|
||||
HashSet<ArtifactEntryTag> seenTags = [];
|
||||
for (int index = 0; index < ChildArtifactEntries.Count; ++index)
|
||||
{
|
||||
// Get the tags for this entry, skip if no tags
|
||||
List<ArtifactEntryTag>? tags = ChildArtifactEntries[index].Tags;
|
||||
if (tags is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Only yield a tag if we have not yielded it yet
|
||||
foreach (ArtifactEntryTag tag in tags)
|
||||
{
|
||||
if (seenTags.Contains(tag))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
seenTags.Add(tag);
|
||||
yield return tag;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enables searching using postgres full text search
|
||||
/// </summary>
|
||||
///
|
||||
public string AllSearchString { get; set; } = "";
|
||||
public NpgsqlTsVector? AllSearchVector { get; set; }
|
||||
|
||||
public string TagsSearchString { get; set; } = "";
|
||||
|
||||
public NpgsqlTsVector? TagsSearchVector { get; set; }
|
||||
|
||||
public string DefectsSearchString { get; set; } = "";
|
||||
|
||||
public NpgsqlTsVector? DefectsSearchVector { get; set; }
|
||||
|
||||
public string ListedNamesSearchString { get; set; } = "";
|
||||
|
||||
public NpgsqlTsVector? ListedNamesSearchVector { get; set; }
|
||||
|
||||
public string TitleSearchString { get; set; } = "";
|
||||
|
||||
public NpgsqlTsVector? TitleSearchVector { get; set; }
|
||||
|
||||
public string DescriptionSearchString { get; set; } = "";
|
||||
|
||||
public NpgsqlTsVector? DescriptionSearchVector { get; set; }
|
||||
|
||||
public string FilenamesSearchString { get; set; } = "";
|
||||
public NpgsqlTsVector FilenamesSearchVector { get; set; }
|
||||
|
||||
public string FileContentSearchString { get; set; } = "";
|
||||
|
||||
public NpgsqlTsVector FileContentSearchVector { get; set; }
|
||||
}
|
||||
12
OpenArchival.DataAccess/Models/ArtifactGroupingViewCount.cs
Normal file
12
OpenArchival.DataAccess/Models/ArtifactGroupingViewCount.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
namespace OpenArchival.DataAccess;
|
||||
|
||||
public class ArtifactGroupingViewCount
|
||||
{
|
||||
public int Id { get; set; }
|
||||
|
||||
public required ArtifactGrouping Grouping { get; set; }
|
||||
|
||||
public int ArtifactGroupingId { get; set; }
|
||||
|
||||
public int Views { get; set; }
|
||||
}
|
||||
16
OpenArchival.DataAccess/Models/ArtifactStorageLocation.cs
Normal file
16
OpenArchival.DataAccess/Models/ArtifactStorageLocation.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace OpenArchival.DataAccess;
|
||||
|
||||
public class ArtifactStorageLocation
|
||||
{
|
||||
[Key]
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public int Id { get; set; }
|
||||
|
||||
[System.ComponentModel.DataAnnotations.Schema.Index(IsUnique =true)]
|
||||
public required string Location { get; set; }
|
||||
|
||||
public List<ArtifactEntry> ArtifactEntries { get; set; } = [];
|
||||
}
|
||||
16
OpenArchival.DataAccess/Models/ArtifactType.cs
Normal file
16
OpenArchival.DataAccess/Models/ArtifactType.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace OpenArchival.DataAccess;
|
||||
|
||||
public class ArtifactType
|
||||
{
|
||||
[Key]
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public int Id { get; set; }
|
||||
|
||||
[System.ComponentModel.DataAnnotations.Schema.Index(IsUnique =true)]
|
||||
public required string Name { get; set; }
|
||||
|
||||
public List<ArtifactEntry>? ArtifactEntries { get; set; } = [];
|
||||
}
|
||||
43
OpenArchival.DataAccess/Models/Blog/BlogPost.cs
Normal file
43
OpenArchival.DataAccess/Models/Blog/BlogPost.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
using NpgsqlTypes;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace OpenArchival.DataAccess;
|
||||
|
||||
public class BlogPost
|
||||
{
|
||||
[Key]
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The title of the blog post
|
||||
/// </summary>
|
||||
public string Title { get; set; } = "";
|
||||
|
||||
/// <summary>
|
||||
/// The HTML content of the post
|
||||
/// </summary>
|
||||
public string Content { get; set; } = "";
|
||||
|
||||
public DateTime CreationTime { get; set; }
|
||||
|
||||
public DateTime ModifiedTime { get; set; }
|
||||
|
||||
public List<BlogPostTag> Tags { get; set; } = [];
|
||||
|
||||
public BlogPostViewCount Views { get; set; } = default!;
|
||||
|
||||
public FilePathListing MainPhoto { get; set; } = default!;
|
||||
|
||||
public List<ArtifactGrouping> ArtifactGroupings { get; set; } = [];
|
||||
|
||||
public NpgsqlTsVector ContentSearchVector { get; set; } = default!;
|
||||
|
||||
public NpgsqlTsVector TitleSearchVector { get; set; } = default!;
|
||||
|
||||
public string TagsSearchString { get; set; } = "";
|
||||
public NpgsqlTsVector TagsSearchVector { get; set; } = default!;
|
||||
|
||||
public string AllSearchString { get; set; } = "";
|
||||
public NpgsqlTsVector AllSearchVector { get; set; } = default!;
|
||||
|
||||
}
|
||||
24
OpenArchival.DataAccess/Models/Blog/BlogPostTag.cs
Normal file
24
OpenArchival.DataAccess/Models/Blog/BlogPostTag.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace OpenArchival.DataAccess;
|
||||
|
||||
public class BlogPostTag
|
||||
{
|
||||
[Key]
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of this tags
|
||||
/// </summary>
|
||||
public string Name { get; set; } = "";
|
||||
|
||||
/// <summary>
|
||||
/// Blog posts assocaited with this tag
|
||||
/// </summary>
|
||||
public List<BlogPost> BlogPosts { get; set; } = [];
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
}
|
||||
12
OpenArchival.DataAccess/Models/Blog/BlogViewCount.cs
Normal file
12
OpenArchival.DataAccess/Models/Blog/BlogViewCount.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
namespace OpenArchival.DataAccess;
|
||||
|
||||
public class BlogPostViewCount
|
||||
{
|
||||
public int Id { get; set; }
|
||||
|
||||
public required BlogPost Post { get; set; }
|
||||
|
||||
public int BlogPostId { get; set; }
|
||||
|
||||
public int Views { get; set; }
|
||||
}
|
||||
23
OpenArchival.DataAccess/Models/FilePathListing.cs
Normal file
23
OpenArchival.DataAccess/Models/FilePathListing.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace OpenArchival.DataAccess;
|
||||
|
||||
public class FilePathListing
|
||||
{
|
||||
[Key]
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public int Id { get; set; }
|
||||
|
||||
public int? ParentArtifactEntryId { get; set; }
|
||||
|
||||
public ArtifactEntry? ParentArtifactEntry { get; set; }
|
||||
|
||||
public BlogPost? ParentBlogPost { get; set; }
|
||||
|
||||
public int? ParentBlogPostId { get; set; }
|
||||
|
||||
public required string OriginalName { get; set; }
|
||||
|
||||
public required string Path { get; set; }
|
||||
}
|
||||
6
OpenArchival.DataAccess/Models/IdentifierFields.cs
Normal file
6
OpenArchival.DataAccess/Models/IdentifierFields.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace OpenArchival.DataAccess;
|
||||
|
||||
public class IdentifierFields
|
||||
{
|
||||
public List<string> Values { get; set; } = [];
|
||||
}
|
||||
20
OpenArchival.DataAccess/Models/ListedName.cs
Normal file
20
OpenArchival.DataAccess/Models/ListedName.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace OpenArchival.DataAccess;
|
||||
|
||||
public class ListedName
|
||||
{
|
||||
[Key]
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public int Id { get; set; }
|
||||
|
||||
public required string Value { get; set; }
|
||||
|
||||
public List<ArtifactEntry> ArtifactEntries { get; set; } = [];
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Value;
|
||||
}
|
||||
}
|
||||
11
OpenArchival.DataAccess/Models/ModelHelpers.cs
Normal file
11
OpenArchival.DataAccess/Models/ModelHelpers.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
namespace OpenArchival.DataAccess;
|
||||
|
||||
public class ModelHelpers
|
||||
{
|
||||
public static string? MakeIdentifier(List<string>? values, string fieldSeperator, string? archiveEntryNumber)
|
||||
{
|
||||
if (values is null || values.Count == 0) return null;
|
||||
|
||||
return (archiveEntryNumber is not null) ? $"{string.Join(fieldSeperator, values)}{archiveEntryNumber}" : string.Join(fieldSeperator, values);
|
||||
}
|
||||
}
|
||||
26
OpenArchival.DataAccess/Models/SearchPageSliderEntry.cs
Normal file
26
OpenArchival.DataAccess/Models/SearchPageSliderEntry.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace OpenArchival.DataAccess;
|
||||
|
||||
/// <summary>
|
||||
/// Used to display sliders of featured artifacts on the search page before a search is entered.
|
||||
/// </summary>
|
||||
public class SearchPageSliderEntry
|
||||
{
|
||||
[Key]
|
||||
public int Id { get; set; }
|
||||
|
||||
public string Title { get; set; } = "";
|
||||
|
||||
public string Description { get; set; } = "";
|
||||
|
||||
/// <summary>
|
||||
/// The tags used to find artifacts for the slider
|
||||
/// </summary>
|
||||
public List<ArtifactEntryTag> FilterTags { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// The maximum number of artifact entries that should be pulled for this slider
|
||||
/// </summary>
|
||||
public int MaxCount { get; set; } = 10;
|
||||
}
|
||||
27
OpenArchival.DataAccess/OpenArchival.DataAccess.csproj
Normal file
27
OpenArchival.DataAccess/OpenArchival.DataAccess.csproj
Normal file
@@ -0,0 +1,27 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<UserSecretsId>699b0057-1557-4ee6-a42f-747e6cc9d2c0</UserSecretsId>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="EntityFramework" Version="6.5.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="9.0.8" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.8" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.8">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.8" />
|
||||
<PackageReference Include="Npgsql" Version="9.0.3" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" />
|
||||
<PackageReference Include="Persic.EF.Postgres" Version="2025.106.102.11" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Migrations\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,19 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.7" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.7">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.7" />
|
||||
<PackageReference Include="Npgsql" Version="9.0.3" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
28
OpenArchival.DataAccess/Program.cs
Normal file
28
OpenArchival.DataAccess/Program.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace OpenArchival.DataAccess;
|
||||
static class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
// In Program.cs
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// Retrieve the connection string from appsettings.json
|
||||
var connectionString = builder.Configuration.GetConnectionString("PostgresConnection");
|
||||
|
||||
// Add the DbContext to the dependency injection container
|
||||
builder.Services.AddDbContext<ApplicationDbContext>(options =>
|
||||
options.UseNpgsql(connectionString));
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
app.Run();
|
||||
}
|
||||
};
|
||||
12
OpenArchival.DataAccess/Properties/launchSettings.json
Normal file
12
OpenArchival.DataAccess/Properties/launchSettings.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"profiles": {
|
||||
"OpenArchival.DataAccess": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": false,
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"applicationUrl": "https://localhost:52509;http://localhost:52510"
|
||||
}
|
||||
}
|
||||
}
|
||||
12
OpenArchival.DataAccess/Properties/serviceDependencies.json
Normal file
12
OpenArchival.DataAccess/Properties/serviceDependencies.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"secrets1": {
|
||||
"type": "secrets"
|
||||
},
|
||||
"postgresql1": {
|
||||
"type": "postgresql",
|
||||
"connectionId": "ConnectionStrings:DatabaseConnection",
|
||||
"dynamicId": null
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"secrets1": {
|
||||
"type": "secrets.user"
|
||||
},
|
||||
"postgresql1": {
|
||||
"containerPorts": "5432:5432",
|
||||
"secretStore": "LocalSecretsFile",
|
||||
"containerName": "postgresql",
|
||||
"containerImage": "postgres",
|
||||
"type": "postgresql.container",
|
||||
"connectionId": "ConnectionStrings:DatabaseConnection",
|
||||
"dynamicId": null
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"postgresql1": {
|
||||
"restored": true,
|
||||
"restoreTime": "2025-08-01T13:34:48.1334288Z"
|
||||
},
|
||||
"secrets1": {
|
||||
"restored": true,
|
||||
"restoreTime": "2025-08-01T13:34:47.8718098Z"
|
||||
}
|
||||
},
|
||||
"parameters": {}
|
||||
}
|
||||
80
OpenArchival.DataAccess/Providers/ArchiveCategoryProvider.cs
Normal file
80
OpenArchival.DataAccess/Providers/ArchiveCategoryProvider.cs
Normal file
@@ -0,0 +1,80 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace OpenArchival.DataAccess;
|
||||
|
||||
public class ArchiveCategoryProvider : IArchiveCategoryProvider
|
||||
{
|
||||
private Microsoft.EntityFrameworkCore.IDbContextFactory<ApplicationDbContext> _dbFactory;
|
||||
private ILogger _logger;
|
||||
|
||||
[SetsRequiredMembers]
|
||||
public ArchiveCategoryProvider(Microsoft.EntityFrameworkCore.IDbContextFactory<ApplicationDbContext> dbFactory, ILogger<ArchiveCategoryProvider> logger)
|
||||
{
|
||||
_dbFactory = dbFactory;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task CreateCategoryAsync(ArchiveCategory category)
|
||||
{
|
||||
await using var context = await _dbFactory.CreateDbContextAsync();
|
||||
|
||||
context.ArchiveCategories.Add(category);
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task UpdateCategoryAsync(ArchiveCategory category)
|
||||
{
|
||||
await using var context = await _dbFactory.CreateDbContextAsync();
|
||||
|
||||
context.ArchiveCategories.Update(category);
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task DeleteCategoryAsync(ArchiveCategory category)
|
||||
{
|
||||
await using var context = await _dbFactory.CreateDbContextAsync();
|
||||
|
||||
context.ArchiveCategories.Remove(category);
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task<List<ArchiveCategory>?> GetArchiveCategory(string categoryName)
|
||||
{
|
||||
await using var context = await _dbFactory.CreateDbContextAsync();
|
||||
|
||||
return await context.ArchiveCategories.Where(a => a.Name == categoryName).ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<ArchiveCategory?> GetArchiveCategory(int id)
|
||||
{
|
||||
await using var context = await _dbFactory.CreateDbContextAsync();
|
||||
|
||||
return await context.ArchiveCategories.Where(a => a.Id == id).FirstOrDefaultAsync();
|
||||
}
|
||||
|
||||
public async Task<List<ArchiveCategory>?> GetAllArchiveCategories()
|
||||
{
|
||||
await using var context = await _dbFactory.CreateDbContextAsync();
|
||||
|
||||
return await context.ArchiveCategories.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<List<ArchiveCategory>?> Search(string query)
|
||||
{
|
||||
await using var context = await _dbFactory.CreateDbContextAsync();
|
||||
|
||||
return await context.ArchiveCategories
|
||||
.Where(p => p.Name.ToLower().Contains(query.ToLower())).ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<List<ArchiveCategory>?> Top(int count)
|
||||
{
|
||||
await using var context = await _dbFactory.CreateDbContextAsync();
|
||||
|
||||
return await context.ArchiveCategories
|
||||
.OrderBy(p => p.Name)
|
||||
.Take(count)
|
||||
.ToListAsync();
|
||||
}
|
||||
}
|
||||
73
OpenArchival.DataAccess/Providers/ArchiveEntryTagProvider.cs
Normal file
73
OpenArchival.DataAccess/Providers/ArchiveEntryTagProvider.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace OpenArchival.DataAccess;
|
||||
|
||||
public class ArchiveEntryTagProvider : IArchiveEntryTagProvider
|
||||
{
|
||||
private readonly IDbContextFactory<ApplicationDbContext> _dbFactory;
|
||||
private readonly ILogger<ArchiveEntryTagProvider> _logger;
|
||||
|
||||
[SetsRequiredMembers]
|
||||
public ArchiveEntryTagProvider(IDbContextFactory<ApplicationDbContext> context, ILogger<ArchiveEntryTagProvider> logger)
|
||||
{
|
||||
_dbFactory = context;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<ArtifactEntryTag?> GetEntryTagAsync(int id)
|
||||
{
|
||||
await using var context = await _dbFactory.CreateDbContextAsync();
|
||||
|
||||
return await context.ArtifactEntryTags.Where(t => t.Id == id).FirstOrDefaultAsync();
|
||||
}
|
||||
|
||||
public async Task<List<ArtifactEntryTag>?> GetEntryTagAsync(string name)
|
||||
{
|
||||
await using var context = await _dbFactory.CreateDbContextAsync();
|
||||
|
||||
return await context.ArtifactEntryTags.Where(t => t.Name == name).ToListAsync();
|
||||
}
|
||||
|
||||
public async Task UpdateEntryTagAsync(ArtifactEntryTag entryTag)
|
||||
{
|
||||
await using var context = await _dbFactory.CreateDbContextAsync();
|
||||
|
||||
context.ArtifactEntryTags.Update(entryTag);
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task CreateEntryTagAsync(ArtifactEntryTag entryTag)
|
||||
{
|
||||
await using var context = await _dbFactory.CreateDbContextAsync();
|
||||
|
||||
context.ArtifactEntryTags.Add(entryTag);
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task DeleteEntryTagAsync(ArtifactEntryTag entryTag)
|
||||
{
|
||||
await using var context = await _dbFactory.CreateDbContextAsync();
|
||||
|
||||
context.ArtifactEntryTags.Remove(entryTag);
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task<List<ArtifactEntryTag>?> Search(string query)
|
||||
{
|
||||
await using var context = await _dbFactory.CreateDbContextAsync();
|
||||
|
||||
return await context.ArtifactEntryTags
|
||||
.Where(p => p.Name.ToLower().Contains(query.ToLower())).ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<List<ArtifactEntryTag>?> Top(int count)
|
||||
{
|
||||
await using var context = await _dbFactory.CreateDbContextAsync();
|
||||
|
||||
return await context.ArtifactEntryTags
|
||||
.OrderBy(p => p.Name)
|
||||
.Take(count)
|
||||
.ToListAsync();
|
||||
}
|
||||
}
|
||||
72
OpenArchival.DataAccess/Providers/ArtifactDefectProvider.cs
Normal file
72
OpenArchival.DataAccess/Providers/ArtifactDefectProvider.cs
Normal file
@@ -0,0 +1,72 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace OpenArchival.DataAccess;
|
||||
|
||||
public class ArtifactDefectProvider : IArtifactDefectProvider
|
||||
{
|
||||
private readonly IDbContextFactory<ApplicationDbContext> _dbFactory;
|
||||
private readonly ILogger<ArtifactDefectProvider> _logger;
|
||||
|
||||
[SetsRequiredMembers]
|
||||
public ArtifactDefectProvider(IDbContextFactory<ApplicationDbContext> context, ILogger<ArtifactDefectProvider> logger)
|
||||
{
|
||||
_dbFactory = context;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<ArtifactDefect?> GetDefectAsync(int id)
|
||||
{
|
||||
await using var context = await _dbFactory.CreateDbContextAsync();
|
||||
return await context.ArtifactDefects.Where(d => d.Id == id).FirstOrDefaultAsync();
|
||||
}
|
||||
|
||||
public async Task<List<ArtifactDefect>?> GetDefectAsync(string description)
|
||||
{
|
||||
await using var context = await _dbFactory.CreateDbContextAsync();
|
||||
return await context.ArtifactDefects.Where(d => d.Description == description).ToListAsync();
|
||||
}
|
||||
|
||||
public async Task UpdateDefectAsync(ArtifactDefect artifactDefect)
|
||||
{
|
||||
await using var context = await _dbFactory.CreateDbContextAsync();
|
||||
context.ArtifactDefects.Update(artifactDefect);
|
||||
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task CreateDefectAsync(ArtifactDefect artifactDefect)
|
||||
{
|
||||
await using var context = await _dbFactory.CreateDbContextAsync();
|
||||
|
||||
context.ArtifactDefects.Add(artifactDefect);
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task DeleteDefectAsync(ArtifactDefect artifactDefect)
|
||||
{
|
||||
await using var context = await _dbFactory.CreateDbContextAsync();
|
||||
|
||||
context.ArtifactDefects.Remove(artifactDefect);
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task<List<ArtifactDefect>?> Search(string query)
|
||||
{
|
||||
await using var context = await _dbFactory.CreateDbContextAsync();
|
||||
|
||||
return await context.ArtifactDefects
|
||||
.Where(p => p.Description.ToLower().Contains(query.ToLower())).ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<List<ArtifactDefect>?> Top(int count)
|
||||
{
|
||||
await using var context = await _dbFactory.CreateDbContextAsync();
|
||||
|
||||
return await context.ArtifactDefects
|
||||
.OrderBy(p => p.Description)
|
||||
.Take(count)
|
||||
.ToListAsync();
|
||||
}
|
||||
}
|
||||
525
OpenArchival.DataAccess/Providers/ArtifactGroupingProvider.cs
Normal file
525
OpenArchival.DataAccess/Providers/ArtifactGroupingProvider.cs
Normal file
@@ -0,0 +1,525 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace OpenArchival.DataAccess;
|
||||
|
||||
public class ArtifactGroupingProvider : IArtifactGroupingProvider
|
||||
{
|
||||
private readonly IDbContextFactory<ApplicationDbContext> _context;
|
||||
private readonly ILogger<ArtifactGroupingProvider> _logger;
|
||||
|
||||
[SetsRequiredMembers]
|
||||
public ArtifactGroupingProvider(IDbContextFactory<ApplicationDbContext> context, ILogger<ArtifactGroupingProvider> logger)
|
||||
{
|
||||
_context = context;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<ArtifactGrouping?> GetGroupingAsync(int id)
|
||||
{
|
||||
await using var context = await _context.CreateDbContextAsync();
|
||||
return await context.ArtifactGroupings
|
||||
.Include(g => g.Category)
|
||||
.Include(g => g.IdentifierFields)
|
||||
.Include(g => g.Type)
|
||||
.Include(g => g.ChildArtifactEntries)
|
||||
.ThenInclude(e => e.StorageLocation)
|
||||
.Include(g => g.ChildArtifactEntries)
|
||||
.ThenInclude(e => e.Type)
|
||||
.Include(g => g.ChildArtifactEntries)
|
||||
.ThenInclude(e => e.Tags)
|
||||
.Include(g => g.ChildArtifactEntries)
|
||||
.ThenInclude(e => e.ListedNames)
|
||||
.Include(g => g.ChildArtifactEntries)
|
||||
.ThenInclude(e => e.Defects)
|
||||
.Include(g => g.ViewCount) // Added
|
||||
.Include(g => g.IdentifierFields)
|
||||
.Where(g => g.Id == id)
|
||||
.FirstOrDefaultAsync();
|
||||
}
|
||||
|
||||
public async Task<ArtifactGrouping?> GetGroupingAsync(string artifactGroupingIdentifier)
|
||||
{
|
||||
await using var context = await _context.CreateDbContextAsync();
|
||||
return await context.ArtifactGroupings
|
||||
.Include(g => g.Category)
|
||||
.Include(g => g.IdentifierFields)
|
||||
.Include(g => g.Type)
|
||||
.Include(g => g.ChildArtifactEntries)
|
||||
.ThenInclude(e => e.StorageLocation)
|
||||
.Include(g => g.ChildArtifactEntries)
|
||||
.ThenInclude(e => e.Type)
|
||||
.Include(g => g.ChildArtifactEntries)
|
||||
.ThenInclude(e => e.Tags)
|
||||
.Include(g => g.ChildArtifactEntries)
|
||||
.ThenInclude(e => e.ListedNames)
|
||||
.Include(g => g.ChildArtifactEntries)
|
||||
.ThenInclude(e => e.Defects)
|
||||
.Include(g => g.ViewCount) // Added
|
||||
.Where(g => g.ArtifactGroupingIdentifier == artifactGroupingIdentifier)
|
||||
.FirstOrDefaultAsync();
|
||||
}
|
||||
|
||||
public async Task CreateGroupingAsync(ArtifactGrouping grouping)
|
||||
{
|
||||
await using var context = await _context.CreateDbContextAsync();
|
||||
|
||||
// Attach the Category to the context. If it has a key, it will be tracked.
|
||||
context.Attach(grouping.Category);
|
||||
|
||||
// --- Local caches for de-duplication within this transaction ---
|
||||
var processedTypes = new Dictionary<string, ArtifactType>();
|
||||
var processedLocations = new Dictionary<string, ArtifactStorageLocation>();
|
||||
var processedTags = new Dictionary<string, ArtifactEntryTag>();
|
||||
var processedNames = new Dictionary<string, ListedName>();
|
||||
var processedDefects = new Dictionary<string, ArtifactDefect>();
|
||||
// --- End local caches ---
|
||||
|
||||
// --- Helper functions to get unique entities (from cache or DB) ---
|
||||
async Task<ArtifactType> GetUniqueTypeAsync(ArtifactType typeToProcess)
|
||||
{
|
||||
if (string.IsNullOrEmpty(typeToProcess?.Name)) return typeToProcess;
|
||||
if (processedTypes.TryGetValue(typeToProcess.Name, out var uniqueType))
|
||||
{
|
||||
return uniqueType;
|
||||
}
|
||||
var dbType = await context.ArtifactTypes.FirstOrDefaultAsync(t => t.Name == typeToProcess.Name);
|
||||
if (dbType != null)
|
||||
{
|
||||
processedTypes[dbType.Name] = dbType;
|
||||
return dbType;
|
||||
}
|
||||
processedTypes[typeToProcess.Name] = typeToProcess;
|
||||
return typeToProcess;
|
||||
}
|
||||
|
||||
async Task<ArtifactStorageLocation> GetUniqueLocationAsync(ArtifactStorageLocation locationToProcess)
|
||||
{
|
||||
if (string.IsNullOrEmpty(locationToProcess?.Location)) return locationToProcess;
|
||||
if (processedLocations.TryGetValue(locationToProcess.Location, out var uniqueLocation))
|
||||
{
|
||||
return uniqueLocation;
|
||||
}
|
||||
var dbLocation = await context.ArtifactStorageLocations.FirstOrDefaultAsync(l => l.Location == locationToProcess.Location);
|
||||
if (dbLocation != null)
|
||||
{
|
||||
processedLocations[dbLocation.Location] = dbLocation;
|
||||
return dbLocation;
|
||||
}
|
||||
processedLocations[locationToProcess.Location] = locationToProcess;
|
||||
return locationToProcess;
|
||||
}
|
||||
|
||||
async Task<ArtifactEntryTag> GetUniqueTagAsync(ArtifactEntryTag tagToProcess)
|
||||
{
|
||||
if (string.IsNullOrEmpty(tagToProcess?.Name)) return tagToProcess;
|
||||
if (processedTags.TryGetValue(tagToProcess.Name, out var uniqueTag))
|
||||
{
|
||||
return uniqueTag;
|
||||
}
|
||||
var dbTag = await context.ArtifactEntryTags.FirstOrDefaultAsync(t => t.Name == tagToProcess.Name);
|
||||
if (dbTag != null)
|
||||
{
|
||||
processedTags[dbTag.Name] = dbTag;
|
||||
return dbTag;
|
||||
}
|
||||
processedTags[tagToProcess.Name] = tagToProcess;
|
||||
return tagToProcess;
|
||||
}
|
||||
|
||||
async Task<ListedName> GetUniqueNameAsync(ListedName nameToProcess)
|
||||
{
|
||||
if (string.IsNullOrEmpty(nameToProcess?.Value)) return nameToProcess;
|
||||
if (processedNames.TryGetValue(nameToProcess.Value, out var uniqueName))
|
||||
{
|
||||
return uniqueName;
|
||||
}
|
||||
var dbName = await context.ArtifactAssociatedNames.FirstOrDefaultAsync(n => n.Value == nameToProcess.Value);
|
||||
if (dbName != null)
|
||||
{
|
||||
processedNames[dbName.Value] = dbName;
|
||||
return dbName;
|
||||
}
|
||||
processedNames[nameToProcess.Value] = nameToProcess;
|
||||
return nameToProcess;
|
||||
}
|
||||
|
||||
async Task<ArtifactDefect> GetUniqueDefectAsync(ArtifactDefect defectToProcess)
|
||||
{
|
||||
if (string.IsNullOrEmpty(defectToProcess?.Description)) return defectToProcess;
|
||||
if (processedDefects.TryGetValue(defectToProcess.Description, out var uniqueDefect))
|
||||
{
|
||||
return uniqueDefect;
|
||||
}
|
||||
var dbDefect = await context.ArtifactDefects.FirstOrDefaultAsync(d => d.Description == defectToProcess.Description);
|
||||
if (dbDefect != null)
|
||||
{
|
||||
processedDefects[dbDefect.Description] = dbDefect;
|
||||
return dbDefect;
|
||||
}
|
||||
processedDefects[defectToProcess.Description] = defectToProcess;
|
||||
return defectToProcess;
|
||||
}
|
||||
// --- End helper functions ---
|
||||
|
||||
// De-duplicate the main grouping's type
|
||||
grouping.Type = await GetUniqueTypeAsync(grouping.Type);
|
||||
|
||||
// Iterate through all child entries to handle their related entities.
|
||||
foreach (var entry in grouping.ChildArtifactEntries)
|
||||
{
|
||||
// Handle Artifact Types
|
||||
entry.Type = await GetUniqueTypeAsync(entry.Type);
|
||||
|
||||
// Handle Storage Location
|
||||
entry.StorageLocation = await GetUniqueLocationAsync(entry.StorageLocation);
|
||||
|
||||
// Handle Tags
|
||||
var managedTags = new List<ArtifactEntryTag>();
|
||||
foreach (var tag in entry.Tags)
|
||||
{
|
||||
managedTags.Add(await GetUniqueTagAsync(tag));
|
||||
}
|
||||
entry.Tags = managedTags;
|
||||
|
||||
// Handle Listed Names
|
||||
var managedNames = new List<ListedName>();
|
||||
foreach (var name in entry.ListedNames)
|
||||
{
|
||||
managedNames.Add(await GetUniqueNameAsync(name));
|
||||
}
|
||||
entry.ListedNames = managedNames;
|
||||
|
||||
// Handle Defects
|
||||
var managedDefects = new List<ArtifactDefect>();
|
||||
foreach (var defect in entry.Defects)
|
||||
{
|
||||
managedDefects.Add(await GetUniqueDefectAsync(defect));
|
||||
}
|
||||
entry.Defects = managedDefects;
|
||||
}
|
||||
|
||||
// Concatinate all of the text to be searchable by postgres
|
||||
grouping.GenerateSearchIndex();
|
||||
|
||||
// Add the new grouping and save changes.
|
||||
context.ChangeTracker.TrackGraph(grouping, node =>
|
||||
{
|
||||
// If the entity's key is set, EF should treat it as an existing, unchanged entity.
|
||||
if (node.Entry.IsKeySet)
|
||||
{
|
||||
node.Entry.State = EntityState.Unchanged;
|
||||
}
|
||||
// Otherwise, it's a new entity that needs to be inserted.
|
||||
else
|
||||
{
|
||||
node.Entry.State = EntityState.Added;
|
||||
}
|
||||
});
|
||||
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task UpdateGroupingAsync(ArtifactGrouping updatedGrouping)
|
||||
{
|
||||
await using var context = await _context.CreateDbContextAsync();
|
||||
|
||||
// 1. Retrieve the existing grouping object from the database, eagerly loading all related data.
|
||||
var existingGrouping = await context.ArtifactGroupings
|
||||
.Include(g => g.Category)
|
||||
.Include(g => g.IdentifierFields)
|
||||
.Include(g => g.Type)
|
||||
.Include(g => g.ViewCount) // Load ViewCount
|
||||
//.Include(g => g.BlogPosts) // BlogPosts not handled yet
|
||||
.Include(g => g.ChildArtifactEntries)
|
||||
.ThenInclude(e => e.StorageLocation)
|
||||
.Include(g => g.ChildArtifactEntries)
|
||||
.ThenInclude(e => e.Type)
|
||||
.Include(g => g.ChildArtifactEntries)
|
||||
.ThenInclude(e => e.Tags)
|
||||
.Include(g => g.ChildArtifactEntries)
|
||||
.ThenInclude(e => e.ListedNames)
|
||||
.Include(g => g.ChildArtifactEntries)
|
||||
.ThenInclude(e => e.Defects)
|
||||
.Where(g => g.Id == updatedGrouping.Id)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
if (existingGrouping == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. Manually copy over primitive properties.
|
||||
existingGrouping.Title = updatedGrouping.Title;
|
||||
existingGrouping.Description = updatedGrouping.Description;
|
||||
existingGrouping.IsPublicallyVisible = updatedGrouping.IsPublicallyVisible;
|
||||
existingGrouping.IdentifierFields = updatedGrouping.IdentifierFields;
|
||||
|
||||
// Handle one-to-many relationships (Type, Category).
|
||||
var existingGroupingType = await context.ArtifactTypes.FirstOrDefaultAsync(t => t.Name == updatedGrouping.Type.Name);
|
||||
if (existingGroupingType != null)
|
||||
{
|
||||
existingGrouping.Type = existingGroupingType;
|
||||
}
|
||||
else
|
||||
{
|
||||
existingGrouping.Type = updatedGrouping.Type;
|
||||
}
|
||||
|
||||
if (existingGrouping.Category.Name != updatedGrouping.Category.Name)
|
||||
{
|
||||
existingGrouping.Category = updatedGrouping.Category;
|
||||
context.Add(existingGrouping.Category);
|
||||
}
|
||||
|
||||
// Handle ViewCount (Added)
|
||||
if (updatedGrouping.ViewCount != null)
|
||||
{
|
||||
if (existingGrouping.ViewCount == null)
|
||||
{
|
||||
// Create a new ViewCount
|
||||
existingGrouping.ViewCount = new ArtifactGroupingViewCount
|
||||
{
|
||||
Grouping = existingGrouping,
|
||||
Views = updatedGrouping.ViewCount.Views
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
// Update existing ViewCount
|
||||
existingGrouping.ViewCount.Views = updatedGrouping.ViewCount.Views;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Handle BlogPosts update (requires model definition & de-duplication)
|
||||
// await DeDuplicateGroupingRelationsAsync(context, updatedGrouping);
|
||||
// existingGrouping.BlogPosts.Clear();
|
||||
// updatedGrouping.BlogPosts.ForEach(post => existingGrouping.BlogPosts.Add(post));
|
||||
|
||||
// 3. Synchronize the ChildArtifactEntries collection.
|
||||
var updatedEntryIds = updatedGrouping.ChildArtifactEntries.Select(e => e.Id).ToList();
|
||||
var entriesToRemove = existingGrouping.ChildArtifactEntries
|
||||
.Where(e => !updatedEntryIds.Contains(e.Id))
|
||||
.ToList();
|
||||
|
||||
foreach (var entryToRemove in entriesToRemove)
|
||||
{
|
||||
existingGrouping.ChildArtifactEntries.Remove(entryToRemove);
|
||||
}
|
||||
|
||||
foreach (var updatedEntry in updatedGrouping.ChildArtifactEntries)
|
||||
{
|
||||
// FIRST, de-duplicate all related entities on the incoming entry.
|
||||
await DeDuplicateEntryRelationsAsync(context, updatedEntry);
|
||||
|
||||
var existingEntry = existingGrouping.ChildArtifactEntries
|
||||
.FirstOrDefault(e => e.Id == updatedEntry.Id);
|
||||
|
||||
if (existingEntry != null)
|
||||
{
|
||||
// The entry exists, so manually update its properties.
|
||||
existingEntry.Title = updatedEntry.Title;
|
||||
existingEntry.Description = updatedEntry.Description;
|
||||
existingEntry.ArtifactNumber = updatedEntry.ArtifactNumber;
|
||||
existingEntry.IsPubliclyVisible = updatedEntry.IsPubliclyVisible;
|
||||
existingEntry.AssociatedDates = updatedEntry.AssociatedDates;
|
||||
existingEntry.FileTextContent = updatedEntry.FileTextContent;
|
||||
existingEntry.Quantity = updatedEntry.Quantity;
|
||||
existingEntry.Links = updatedEntry.Links;
|
||||
|
||||
// The relations on updatedEntry are already de-duplicated, so just assign them.
|
||||
existingEntry.StorageLocation = updatedEntry.StorageLocation;
|
||||
existingEntry.Type = updatedEntry.Type;
|
||||
|
||||
// For collections, clear the old ones and add the new de-duplicated ones.
|
||||
existingEntry.Tags.Clear();
|
||||
updatedEntry.Tags.ForEach(tag => existingEntry.Tags.Add(tag));
|
||||
|
||||
existingEntry.ListedNames.Clear();
|
||||
updatedEntry.ListedNames.ForEach(name => existingEntry.ListedNames.Add(name));
|
||||
|
||||
existingEntry.Defects.Clear();
|
||||
updatedEntry.Defects.ForEach(defect => existingEntry.Defects.Add(defect));
|
||||
}
|
||||
else
|
||||
{
|
||||
// The entry is new and its children are already de-duplicated, so just add it.
|
||||
existingGrouping.ChildArtifactEntries.Add(updatedEntry);
|
||||
}
|
||||
}
|
||||
|
||||
existingGrouping.GenerateSearchIndex();
|
||||
|
||||
// 4. Save all changes.
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
private async Task DeDuplicateEntryRelationsAsync(ApplicationDbContext context, ArtifactEntry entry)
|
||||
{
|
||||
// --- Handle One-to-Many Relationships ---
|
||||
var existingLocation = await context.ArtifactStorageLocations.FirstOrDefaultAsync(l => l.Location == entry.StorageLocation.Location);
|
||||
if (existingLocation != null)
|
||||
{
|
||||
entry.StorageLocation = existingLocation;
|
||||
}
|
||||
|
||||
var existingType = await context.ArtifactTypes.FirstOrDefaultAsync(t => t.Name == entry.Type.Name);
|
||||
if (existingType != null)
|
||||
{
|
||||
entry.Type = existingType;
|
||||
}
|
||||
|
||||
// --- Handle Many-to-Many Relationships ---
|
||||
|
||||
// De-duplicate Tags
|
||||
var processedTags = new List<ArtifactEntryTag>();
|
||||
foreach (var tag in entry.Tags)
|
||||
{
|
||||
var existingTag = await context.ArtifactEntryTags.FirstOrDefaultAsync(t => t.Name == tag.Name) ?? tag;
|
||||
processedTags.Add(existingTag);
|
||||
}
|
||||
entry.Tags = processedTags;
|
||||
|
||||
// De-duplicate ListedNames
|
||||
var processedNames = new List<ListedName>();
|
||||
if (entry.ListedNames != null)
|
||||
{
|
||||
foreach (var name in entry.ListedNames)
|
||||
{
|
||||
var existingName = await context.ArtifactAssociatedNames.FirstOrDefaultAsync(n => n.Value == name.Value) ?? name;
|
||||
processedNames.Add(existingName);
|
||||
}
|
||||
entry.ListedNames = processedNames;
|
||||
}
|
||||
|
||||
// De-duplicate Defects
|
||||
var processedDefects = new List<ArtifactDefect>();
|
||||
if (entry.Defects != null)
|
||||
{
|
||||
foreach (var defect in entry.Defects)
|
||||
{
|
||||
var existingDefect = await context.ArtifactDefects.FirstOrDefaultAsync(d => d.Description == defect.Description) ?? defect;
|
||||
processedDefects.Add(existingDefect);
|
||||
}
|
||||
entry.Defects = processedDefects;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A helper method to synchronize many-to-many collections.
|
||||
/// </summary>
|
||||
private async Task SyncCollectionAsync<TEntity, TKey>(
|
||||
DbContext context,
|
||||
ICollection<TEntity> existingItems,
|
||||
ICollection<TEntity> updatedItems,
|
||||
Func<TEntity, TKey> keySelector) where TEntity : class
|
||||
{
|
||||
var existingKeys = existingItems.Select(keySelector).ToHashSet();
|
||||
var updatedKeys = updatedItems.Select(keySelector).ToHashSet();
|
||||
|
||||
// 1. Remove items that are no longer in the updated collection
|
||||
var keysToRemove = existingKeys.Except(updatedKeys);
|
||||
var itemsToRemove = existingItems.Where(item => keysToRemove.Contains(keySelector(item))).ToList();
|
||||
foreach (var item in itemsToRemove)
|
||||
{
|
||||
existingItems.Remove(item);
|
||||
}
|
||||
|
||||
// 2. Identify keys for brand new items
|
||||
var keysToAdd = updatedKeys.Except(existingKeys).ToList();
|
||||
if (!keysToAdd.Any())
|
||||
{
|
||||
return; // Nothing to add
|
||||
}
|
||||
|
||||
// 3. Batch-fetch all entities from the DB that match the new keys.
|
||||
Dictionary<TKey, TEntity> existingDbItemsMap = [];
|
||||
if (typeof(TEntity) == typeof(ArtifactEntryTag))
|
||||
{
|
||||
var tagKeys = keysToAdd.Cast<string>().ToList();
|
||||
var tags = await context.Set<ArtifactEntryTag>()
|
||||
.Where(t => tagKeys.Contains(t.Name))
|
||||
.ToListAsync();
|
||||
existingDbItemsMap = tags.ToDictionary(t => (TKey)(object)t.Name) as Dictionary<TKey, TEntity>;
|
||||
}
|
||||
else if (typeof(TEntity) == typeof(ListedName))
|
||||
{
|
||||
var nameKeys = keysToAdd.Cast<string>().ToList();
|
||||
var names = await context.Set<ListedName>()
|
||||
.Where(n => nameKeys.Contains(n.Value))
|
||||
.ToListAsync();
|
||||
existingDbItemsMap = names.ToDictionary(n => (TKey)(object)n.Value) as Dictionary<TKey, TEntity>;
|
||||
}
|
||||
else if (typeof(TEntity) == typeof(ArtifactDefect))
|
||||
{
|
||||
var defectKeys = keysToAdd.Cast<string>().ToList();
|
||||
var defects = await context.Set<ArtifactDefect>()
|
||||
.Where(d => defectKeys.Contains(d.Description))
|
||||
.ToListAsync();
|
||||
existingDbItemsMap = defects.ToDictionary(d => (TKey)(object)d.Description) as Dictionary<TKey, TEntity>;
|
||||
}
|
||||
// TODO: Add support for other entity types like BlogPost or ArtifactEntry if needed
|
||||
|
||||
|
||||
// 4. Add the items, using the tracked entity from the DB if it exists.
|
||||
foreach (var updatedItem in updatedItems.Where(i => keysToAdd.Contains(keySelector(i))))
|
||||
{
|
||||
var key = keySelector(updatedItem);
|
||||
if (existingDbItemsMap.TryGetValue(key, out var dbItem))
|
||||
{
|
||||
// The item already exists in the DB, so add the tracked version.
|
||||
existingItems.Add(dbItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is a brand new item, so add the untracked one from the input.
|
||||
existingItems.Add(updatedItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task DeleteGroupingAsync(int id)
|
||||
{
|
||||
await using var context = await _context.CreateDbContextAsync();
|
||||
await context.ArtifactGroupings
|
||||
.Where(p => p.Id == id)
|
||||
.ExecuteDeleteAsync();
|
||||
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task DeleteGroupingAsync(ArtifactGrouping grouping)
|
||||
{
|
||||
await using var context = await _context.CreateDbContextAsync();
|
||||
context.ArtifactGroupings.Remove(grouping);
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task<List<ArtifactGrouping>> GetGroupingsPaged(int pageNumber, int resultsCount)
|
||||
{
|
||||
await using var context = await _context.CreateDbContextAsync();
|
||||
if (pageNumber < 1 || resultsCount < 1)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException($"Either page number or number of results was less than or equal to 0. {nameof(pageNumber)}={pageNumber} {nameof(resultsCount)}={resultsCount}");
|
||||
}
|
||||
|
||||
var totalCount = await context.ArtifactGroupings.CountAsync();
|
||||
|
||||
var items = await context.ArtifactGroupings
|
||||
.Include(g => g.ChildArtifactEntries)
|
||||
.Include(g => g.Category)
|
||||
.OrderBy(g => g.Id)
|
||||
.Skip((pageNumber - 1) * resultsCount)
|
||||
.Take(resultsCount)
|
||||
.ToListAsync();
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
public async Task<int> GetTotalCount()
|
||||
{
|
||||
await using var context = await _context.CreateDbContextAsync();
|
||||
return context.ArtifactGroupings.Count();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace OpenArchival.DataAccess;
|
||||
|
||||
public class ArtifactStorageLocationProvider : IArtifactStorageLocationProvider
|
||||
{
|
||||
private readonly IDbContextFactory<ApplicationDbContext> _dbFactory;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
[SetsRequiredMembers]
|
||||
public ArtifactStorageLocationProvider(IDbContextFactory<ApplicationDbContext> dbFactory, ILogger<ArtifactStorageLocationProvider> logger)
|
||||
{
|
||||
_dbFactory = dbFactory;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task CreateArtifactStorageLocationAsync(ArtifactStorageLocation location)
|
||||
{
|
||||
await using var context = await _dbFactory.CreateDbContextAsync();
|
||||
|
||||
context.ArtifactStorageLocations.Add(location);
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task UpdateArtifactStorageLocationAsync(ArtifactStorageLocation location)
|
||||
{
|
||||
await using var context = await _dbFactory.CreateDbContextAsync();
|
||||
|
||||
context.ArtifactStorageLocations.Update(location);
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task DeleteArtifactStorageLocationAsync(ArtifactStorageLocation location)
|
||||
{
|
||||
await using var context = await _dbFactory.CreateDbContextAsync();
|
||||
|
||||
context.ArtifactStorageLocations.Remove(location);
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task<List<ArtifactStorageLocation>?> GetArtifactStorageLocation(string locationName)
|
||||
{
|
||||
await using var context = await _dbFactory.CreateDbContextAsync();
|
||||
|
||||
return await context.ArtifactStorageLocations.Where(a => a.Location == locationName).ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<ArtifactStorageLocation?> GetArtifactStorageLocation(int id)
|
||||
{
|
||||
await using var context = await _dbFactory.CreateDbContextAsync();
|
||||
|
||||
return await context.ArtifactStorageLocations.Where(a => a.Id == id).FirstOrDefaultAsync();
|
||||
}
|
||||
|
||||
public async Task<List<ArtifactStorageLocation>?> GetAllArtifactStorageLocations()
|
||||
{
|
||||
await using var context = await _dbFactory.CreateDbContextAsync();
|
||||
|
||||
return await context.ArtifactStorageLocations.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<List<ArtifactStorageLocation>?> Search(string query)
|
||||
{
|
||||
await using var context = await _dbFactory.CreateDbContextAsync();
|
||||
|
||||
// STEP 1: Get the unique location STRINGS that match the search query.
|
||||
// This simple query is guaranteed to be translated correctly by EF Core.
|
||||
var uniqueMatchingNames = await context.ArtifactStorageLocations
|
||||
.Where(p => p.Location.ToLower().Contains(query.ToLower()))
|
||||
.Select(p => p.Location)
|
||||
.Distinct()
|
||||
.ToListAsync();
|
||||
|
||||
// If no names matched, return an empty list immediately.
|
||||
if (uniqueMatchingNames is null || !uniqueMatchingNames.Any())
|
||||
{
|
||||
return new List<ArtifactStorageLocation>();
|
||||
}
|
||||
|
||||
// STEP 2: Now, fetch the full objects that correspond to the unique names.
|
||||
var matchingLocations = await context.ArtifactStorageLocations
|
||||
.Where(p => uniqueMatchingNames.Contains(p.Location))
|
||||
.ToListAsync();
|
||||
|
||||
// STEP 3: Perform a final DistinctBy on the small in-memory list to ensure
|
||||
// a clean result set with one object per location name.
|
||||
var finalResults = matchingLocations
|
||||
.DistinctBy(p => p.Location)
|
||||
.OrderBy(p => p.Location) // Optional: Orders the final search results
|
||||
.ToList();
|
||||
|
||||
return finalResults;
|
||||
}
|
||||
|
||||
public async Task<List<ArtifactStorageLocation>?> Top(int count)
|
||||
{
|
||||
await using var context = await _dbFactory.CreateDbContextAsync();
|
||||
|
||||
// STEP 1: Get a unique, ordered list of the TOP N location *strings*.
|
||||
// This is a simple query that EF Core can always translate correctly.
|
||||
var uniqueLocationNames = await context.ArtifactStorageLocations
|
||||
.Select(p => p.Location)
|
||||
.Distinct()
|
||||
.OrderBy(locationName => locationName)
|
||||
.Take(count)
|
||||
.ToListAsync();
|
||||
|
||||
if (uniqueLocationNames is null || !uniqueLocationNames.Any())
|
||||
{
|
||||
return new List<ArtifactStorageLocation>();
|
||||
}
|
||||
|
||||
// STEP 2: Fetch all the full ArtifactStorageLocation objects that match the unique names.
|
||||
// We use the list from Step 1 to create a 'WHERE IN (...)' clause.
|
||||
var matchingLocations = await context.ArtifactStorageLocations
|
||||
.Where(p => uniqueLocationNames.Contains(p.Location))
|
||||
.ToListAsync();
|
||||
|
||||
// STEP 3: The previous query might fetch duplicates (e.g., two entries for "Box A").
|
||||
// We now perform the final DistinctBy in-memory, which is guaranteed to work.
|
||||
var finalResults = matchingLocations
|
||||
.DistinctBy(p => p.Location)
|
||||
.OrderBy(p => p.Location) // Re-apply ordering to the final list
|
||||
.ToList();
|
||||
|
||||
return finalResults;
|
||||
}
|
||||
}
|
||||
74
OpenArchival.DataAccess/Providers/ArtifactTypeProvider.cs
Normal file
74
OpenArchival.DataAccess/Providers/ArtifactTypeProvider.cs
Normal file
@@ -0,0 +1,74 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace OpenArchival.DataAccess;
|
||||
|
||||
public class ArtifactTypeProvider : IArtifactTypeProvider
|
||||
{
|
||||
private readonly IDbContextFactory<ApplicationDbContext> _dbFactory;
|
||||
private readonly ILogger<ArtifactTypeProvider> _logger;
|
||||
|
||||
[SetsRequiredMembers]
|
||||
public ArtifactTypeProvider(IDbContextFactory<ApplicationDbContext> dbFactory, ILogger<ArtifactTypeProvider> logger)
|
||||
{
|
||||
_dbFactory = dbFactory;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task CreateArtifactTypeAsync(ArtifactType artifactType)
|
||||
{
|
||||
await using var context = await _dbFactory.CreateDbContextAsync();
|
||||
context.ArtifactTypes.Add(artifactType);
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task UpdateArtifactTypeAsync(ArtifactType artifactType)
|
||||
{
|
||||
await using var context = await _dbFactory.CreateDbContextAsync();
|
||||
context.ArtifactTypes.Update(artifactType);
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task DeleteArtifactTypeAsync(ArtifactType artifactType)
|
||||
{
|
||||
await using var context = await _dbFactory.CreateDbContextAsync();
|
||||
context.ArtifactTypes.Remove(artifactType);
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task<List<ArtifactType>?> GetArtifactType(string name)
|
||||
{
|
||||
await using var context = await _dbFactory.CreateDbContextAsync();
|
||||
return await context.ArtifactTypes.Where(a => a.Name == name).ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<ArtifactType?> GetArtifactType(int id)
|
||||
{
|
||||
await using var context = await _dbFactory.CreateDbContextAsync();
|
||||
return await context.ArtifactTypes.FirstOrDefaultAsync(a => a.Id == id);
|
||||
}
|
||||
|
||||
public async Task<List<ArtifactType>?> GetAllArtifactTypes()
|
||||
{
|
||||
await using var context = await _dbFactory.CreateDbContextAsync();
|
||||
return await context.ArtifactTypes.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<List<ArtifactType>?> Search(string query)
|
||||
{
|
||||
await using var context = await _dbFactory.CreateDbContextAsync();
|
||||
return await context.ArtifactTypes
|
||||
.Where(p => p.Name.ToLower().Contains(query.ToLower()))
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<List<ArtifactType>?> Top(int count)
|
||||
{
|
||||
await using var context = await _dbFactory.CreateDbContextAsync();
|
||||
return await context.ArtifactTypes
|
||||
.OrderBy(p => p.Name)
|
||||
.Take(count)
|
||||
.ToListAsync();
|
||||
}
|
||||
}
|
||||
61
OpenArchival.DataAccess/Providers/FilePathListingProvider.cs
Normal file
61
OpenArchival.DataAccess/Providers/FilePathListingProvider.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace OpenArchival.DataAccess;
|
||||
|
||||
public class FilePathListingProvider : IFilePathListingProvider
|
||||
{
|
||||
private readonly ApplicationDbContext _context;
|
||||
private readonly ILogger<FilePathListingProvider> _logger;
|
||||
|
||||
[SetsRequiredMembers]
|
||||
public FilePathListingProvider(ApplicationDbContext context, ILogger<FilePathListingProvider> logger)
|
||||
{
|
||||
_context = context;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<FilePathListing?> GetFilePathListingAsync(int id)
|
||||
{
|
||||
return await _context.ArtifactFilePaths.Where(f => f.Id == id).FirstOrDefaultAsync();
|
||||
}
|
||||
|
||||
public async Task<FilePathListing?> GetFilePathListingByPathAsync(string path)
|
||||
{
|
||||
return await _context.ArtifactFilePaths.Where(f => f.Path == path).FirstOrDefaultAsync();
|
||||
}
|
||||
|
||||
public async Task CreateFilePathListingAsync(FilePathListing filePathListing)
|
||||
{
|
||||
_context.ArtifactFilePaths.Add(filePathListing);
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task UpdateFilePathListingAsync(FilePathListing filePathListing)
|
||||
{
|
||||
_context.ArtifactFilePaths.Update(filePathListing);
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task DeleteFilePathListingAsync(FilePathListing filePathListing)
|
||||
{
|
||||
_context.ArtifactFilePaths.Remove(filePathListing);
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task<bool> DeleteFilePathListingAsync(string originalFileName, string diskPath)
|
||||
{
|
||||
var listingToDelete = await _context.ArtifactFilePaths
|
||||
.Where(p => p.OriginalName == originalFileName)
|
||||
.Where(p => p.Path == diskPath)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
if (listingToDelete == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_context.RemoveRange(listingToDelete);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
namespace OpenArchival.DataAccess;
|
||||
|
||||
public interface IArchiveCategoryProvider
|
||||
{
|
||||
public Task CreateCategoryAsync(ArchiveCategory category);
|
||||
|
||||
public Task UpdateCategoryAsync(ArchiveCategory category);
|
||||
|
||||
public Task DeleteCategoryAsync(ArchiveCategory category);
|
||||
|
||||
public Task<ArchiveCategory?> GetArchiveCategory(int id);
|
||||
|
||||
|
||||
public Task<List<ArchiveCategory>?> GetArchiveCategory(string categoryName);
|
||||
|
||||
public Task<List<ArchiveCategory>?> GetAllArchiveCategories();
|
||||
|
||||
public Task<List<ArchiveCategory>?> Search(string query);
|
||||
|
||||
public Task<List<ArchiveCategory>?> Top(int count);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
namespace OpenArchival.DataAccess;
|
||||
|
||||
public interface IArchiveEntryTagProvider
|
||||
{
|
||||
public Task<ArtifactEntryTag?> GetEntryTagAsync(int id);
|
||||
|
||||
public Task<List<ArtifactEntryTag>?> GetEntryTagAsync(string name);
|
||||
|
||||
public Task UpdateEntryTagAsync(ArtifactEntryTag entryTag);
|
||||
|
||||
public Task CreateEntryTagAsync(ArtifactEntryTag entryTag);
|
||||
|
||||
public Task DeleteEntryTagAsync(ArtifactEntryTag entryTag);
|
||||
|
||||
public Task<List<ArtifactEntryTag>?> Search(string query);
|
||||
|
||||
public Task<List<ArtifactEntryTag>?> Top(int count);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace OpenArchival.DataAccess;
|
||||
|
||||
public interface IArtifactDefectProvider
|
||||
{
|
||||
public Task<ArtifactDefect?> GetDefectAsync(int id);
|
||||
public Task<List<ArtifactDefect>?> GetDefectAsync(string description);
|
||||
public Task UpdateDefectAsync(ArtifactDefect artifactDefect);
|
||||
public Task CreateDefectAsync(ArtifactDefect artifactDefect);
|
||||
public Task DeleteDefectAsync(ArtifactDefect artifactDefect);
|
||||
public Task<List<ArtifactDefect>?> Search(string query);
|
||||
public Task<List<ArtifactDefect>?> Top(int count);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace OpenArchival.DataAccess;
|
||||
|
||||
public interface IArtifactGroupingProvider
|
||||
{
|
||||
Task<ArtifactGrouping?> GetGroupingAsync(int id);
|
||||
Task<ArtifactGrouping?> GetGroupingAsync(string artifactGroupingIdentifier);
|
||||
Task CreateGroupingAsync(ArtifactGrouping grouping);
|
||||
Task UpdateGroupingAsync(ArtifactGrouping grouping);
|
||||
Task DeleteGroupingAsync(int id);
|
||||
Task DeleteGroupingAsync(ArtifactGrouping grouping);
|
||||
Task<List<ArtifactGrouping>> GetGroupingsPaged(int pageNumber, int resultsCount);
|
||||
public Task<int> GetTotalCount();
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace OpenArchival.DataAccess;
|
||||
|
||||
public interface IArtifactStorageLocationProvider
|
||||
{
|
||||
Task CreateArtifactStorageLocationAsync(ArtifactStorageLocation location);
|
||||
Task UpdateArtifactStorageLocationAsync(ArtifactStorageLocation location);
|
||||
Task DeleteArtifactStorageLocationAsync(ArtifactStorageLocation location);
|
||||
Task<List<ArtifactStorageLocation>?> GetArtifactStorageLocation(string locationName);
|
||||
Task<ArtifactStorageLocation?> GetArtifactStorageLocation(int id);
|
||||
Task<List<ArtifactStorageLocation>?> GetAllArtifactStorageLocations();
|
||||
Task<List<ArtifactStorageLocation>?> Search(string query);
|
||||
Task<List<ArtifactStorageLocation>?> Top(int count);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace OpenArchival.DataAccess;
|
||||
|
||||
public interface IArtifactTypeProvider
|
||||
{
|
||||
Task CreateArtifactTypeAsync(ArtifactType artifactType);
|
||||
Task UpdateArtifactTypeAsync(ArtifactType artifactType);
|
||||
Task DeleteArtifactTypeAsync(ArtifactType artifactType);
|
||||
Task<List<ArtifactType>?> GetArtifactType(string name);
|
||||
Task<ArtifactType?> GetArtifactType(int id);
|
||||
Task<List<ArtifactType>?> GetAllArtifactTypes();
|
||||
Task<List<ArtifactType>?> Search(string query);
|
||||
Task<List<ArtifactType>?> Top(int count);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace OpenArchival.DataAccess;
|
||||
|
||||
public interface IFilePathListingProvider
|
||||
{
|
||||
Task<FilePathListing?> GetFilePathListingAsync(int id);
|
||||
Task<FilePathListing?> GetFilePathListingByPathAsync(string path);
|
||||
Task CreateFilePathListingAsync(FilePathListing filePathListing);
|
||||
Task UpdateFilePathListingAsync(FilePathListing filePathListing);
|
||||
Task DeleteFilePathListingAsync(FilePathListing filePathListing);
|
||||
public Task<bool> DeleteFilePathListingAsync(string originalFileName, string diskPath);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace OpenArchival.DataAccess;
|
||||
|
||||
public interface IListedNameProvider
|
||||
{
|
||||
Task<ListedName?> GetAssociatedNameAsync(int id);
|
||||
Task<List<ListedName>?> GetAssociatedNamesAsync(string name);
|
||||
Task CreateAssociatedNameAsync(ListedName associatedName);
|
||||
Task UpdateAssociatedNameAsync(ListedName associatedName);
|
||||
Task DeleteAssociatedNameAsync(ListedName associatedName);
|
||||
public Task<List<ListedName>?> Search(string query);
|
||||
public Task<List<ListedName>?> Top(int count);
|
||||
}
|
||||
79
OpenArchival.DataAccess/Providers/ListedNameProvider.cs
Normal file
79
OpenArchival.DataAccess/Providers/ListedNameProvider.cs
Normal file
@@ -0,0 +1,79 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace OpenArchival.DataAccess;
|
||||
|
||||
public class ListedNameProvider : IListedNameProvider
|
||||
{
|
||||
private readonly IDbContextFactory<ApplicationDbContext> _dbFactory;
|
||||
private readonly ILogger<ListedNameProvider> _logger;
|
||||
|
||||
[SetsRequiredMembers]
|
||||
public ListedNameProvider(IDbContextFactory<ApplicationDbContext> context, ILogger<ListedNameProvider> logger)
|
||||
{
|
||||
_dbFactory = context;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<ListedName?> GetAssociatedNameAsync(int id)
|
||||
{
|
||||
await using var context = await _dbFactory.CreateDbContextAsync();
|
||||
|
||||
return await context.ArtifactAssociatedNames.Where(n => n.Id == id).FirstOrDefaultAsync();
|
||||
}
|
||||
|
||||
public async Task<List<ListedName>?> GetAssociatedNamesAsync(string name)
|
||||
{
|
||||
await using var context = await _dbFactory.CreateDbContextAsync();
|
||||
|
||||
return await context.ArtifactAssociatedNames
|
||||
.Where(n => n.Value == name)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task CreateAssociatedNameAsync(ListedName associatedName)
|
||||
{
|
||||
await using var context = await _dbFactory.CreateDbContextAsync();
|
||||
|
||||
context.ArtifactAssociatedNames.Add(associatedName);
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task UpdateAssociatedNameAsync(ListedName associatedName)
|
||||
{
|
||||
await using var context = await _dbFactory.CreateDbContextAsync();
|
||||
|
||||
context.ArtifactAssociatedNames.Update(associatedName);
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task DeleteAssociatedNameAsync(ListedName associatedName)
|
||||
{
|
||||
await using var context = await _dbFactory.CreateDbContextAsync();
|
||||
|
||||
context.ArtifactAssociatedNames.Remove(associatedName);
|
||||
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task<List<ListedName>?> Search(string query)
|
||||
{
|
||||
await using var context = await _dbFactory.CreateDbContextAsync();
|
||||
var lowerCaseQuery = query.ToLower();
|
||||
|
||||
return await context.ArtifactAssociatedNames
|
||||
.Where(p => p.Value.ToLower().Contains(lowerCaseQuery))
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<List<ListedName>?> Top(int count)
|
||||
{
|
||||
await using var context = await _dbFactory.CreateDbContextAsync();
|
||||
|
||||
return await context.ArtifactAssociatedNames
|
||||
.OrderBy(p => p.Value)
|
||||
.Take(count)
|
||||
.ToListAsync();
|
||||
}
|
||||
}
|
||||
9
OpenArchival.DataAccess/UserRoles.cs
Normal file
9
OpenArchival.DataAccess/UserRoles.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace OpenArchival.DataAccess;
|
||||
|
||||
public static class UserRoles
|
||||
{
|
||||
public const string Admin = "Admin";
|
||||
public const string Writer = "Writer";
|
||||
public const string User = "User";
|
||||
|
||||
}
|
||||
BIN
OpenArchival.DataAccess/bin/Debug/net9.0/Confi.dll
Normal file
BIN
OpenArchival.DataAccess/bin/Debug/net9.0/Confi.dll
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
OpenArchival.DataAccess/bin/Debug/net9.0/EntityFramework.dll
Normal file
BIN
OpenArchival.DataAccess/bin/Debug/net9.0/EntityFramework.dll
Normal file
Binary file not shown.
BIN
OpenArchival.DataAccess/bin/Debug/net9.0/Humanizer.dll
Normal file
BIN
OpenArchival.DataAccess/bin/Debug/net9.0/Humanizer.dll
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
OpenArchival.DataAccess/bin/Debug/net9.0/Mono.TextTemplating.dll
Normal file
BIN
OpenArchival.DataAccess/bin/Debug/net9.0/Mono.TextTemplating.dll
Normal file
Binary file not shown.
Binary file not shown.
BIN
OpenArchival.DataAccess/bin/Debug/net9.0/Npgsql.dll
Normal file
BIN
OpenArchival.DataAccess/bin/Debug/net9.0/Npgsql.dll
Normal file
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"runtimeOptions": {
|
||||
"tfm": "net9.0",
|
||||
"frameworks": [
|
||||
{
|
||||
"name": "Microsoft.NETCore.App",
|
||||
"version": "9.0.0"
|
||||
},
|
||||
{
|
||||
"name": "Microsoft.AspNetCore.App",
|
||||
"version": "9.0.0"
|
||||
}
|
||||
],
|
||||
"configProperties": {
|
||||
"System.GC.Server": true,
|
||||
"System.Reflection.NullabilityInfoContext.IsSupported": true,
|
||||
"System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{"Version":1,"ManifestType":"Build","Endpoints":[]}
|
||||
BIN
OpenArchival.DataAccess/bin/Debug/net9.0/Persic.EF.Postgres.dll
Normal file
BIN
OpenArchival.DataAccess/bin/Debug/net9.0/Persic.EF.Postgres.dll
Normal file
Binary file not shown.
BIN
OpenArchival.DataAccess/bin/Debug/net9.0/Persic.EF.dll
Normal file
BIN
OpenArchival.DataAccess/bin/Debug/net9.0/Persic.EF.dll
Normal file
Binary file not shown.
BIN
OpenArchival.DataAccess/bin/Debug/net9.0/System.CodeDom.dll
Normal file
BIN
OpenArchival.DataAccess/bin/Debug/net9.0/System.CodeDom.dll
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user