Implemented the PlotDirector export/import completeness pass.

This commit is contained in:
Nick Beckley 2026-06-22 20:03:08 +01:00
parent 882a96fd01
commit 5f8ff0c0dd
6 changed files with 956 additions and 5 deletions

View File

@ -66,9 +66,11 @@ public sealed class ImportRepository(ISqlConnectionFactory connectionFactory) :
LocationTypes = (await result.ReadAsync<ProjectBackupLookupRow>()).ToList(),
LocationRelationshipTypes = (await result.ReadAsync<ProjectBackupLookupRow>()).ToList(),
PlotLineTypes = (await result.ReadAsync<ProjectBackupLookupRow>()).ToList(),
PlotImportance = (await result.ReadAsync<ProjectBackupLookupRow>()).ToList(),
ThreadTypes = (await result.ReadAsync<ProjectBackupLookupRow>()).ToList(),
ThreadStatuses = (await result.ReadAsync<ProjectBackupLookupRow>()).ToList(),
ThreadEventTypes = (await result.ReadAsync<ProjectBackupLookupRow>()).ToList(),
PlotEventTypes = (await result.ReadAsync<ProjectBackupLookupRow>()).ToList(),
AssetKinds = (await result.ReadAsync<ProjectBackupLookupRow>()).ToList(),
AssetStates = (await result.ReadAsync<ProjectBackupLookupRow>()).ToList(),
AssetEventTypes = (await result.ReadAsync<ProjectBackupLookupRow>()).ToList(),

View File

@ -17,7 +17,8 @@ public sealed class ProjectBackupRepository(ISqlConnectionFactory connectionFact
using var result = await connection.QueryMultipleAsync(
"dbo.ProjectBackup_Export",
new { ProjectID = projectId, UserID = userId },
commandType: CommandType.StoredProcedure);
commandType: CommandType.StoredProcedure,
commandTimeout: 180);
var project = await result.ReadSingleOrDefaultAsync<Project>();
var data = new ProjectBackupData
@ -50,7 +51,12 @@ public sealed class ProjectBackupRepository(ISqlConnectionFactory connectionFact
RelationshipEvents = (await result.ReadAsync<RelationshipEvent>()).ToList(),
Locations = (await result.ReadAsync<LocationItem>()).ToList(),
LocationRelationships = (await result.ReadAsync<LocationRelationship>()).ToList(),
FloorPlans = (await result.ReadAsync<FloorPlan>()).ToList(),
FloorPlanFloors = (await result.ReadAsync<FloorPlanFloor>()).ToList(),
FloorPlanBlocks = (await result.ReadAsync<FloorPlanBlock>()).ToList(),
FloorPlanTransitions = (await result.ReadAsync<FloorPlanTransition>()).ToList(),
SceneAssetLocations = (await result.ReadAsync<SceneAssetLocation>()).ToList(),
SceneFloorPlanOccupancy = (await result.ReadAsync<SceneFloorPlanOccupancy>()).ToList(),
SceneDependencies = (await result.ReadAsync<SceneDependency>()).ToList(),
ValidationRuns = (await result.ReadAsync<ProjectBackupValidationRun>()).ToList(),
ContinuityWarnings = (await result.ReadAsync<ContinuityWarning>()).ToList(),
@ -139,7 +145,12 @@ public sealed class ProjectBackupRepository(ISqlConnectionFactory connectionFact
nameof(data.RelationshipEvents),
nameof(data.Locations),
nameof(data.LocationRelationships),
nameof(data.FloorPlans),
nameof(data.FloorPlanFloors),
nameof(data.FloorPlanBlocks),
nameof(data.FloorPlanTransitions),
nameof(data.SceneAssetLocations),
nameof(data.SceneFloorPlanOccupancy),
nameof(data.SceneDependencies),
nameof(data.ValidationRuns),
nameof(data.ContinuityWarnings),

View File

@ -38,9 +38,11 @@ public sealed class ProjectBackupRelationshipImportLookups
public IReadOnlyList<ProjectBackupLookupRow> LocationTypes { get; init; } = [];
public IReadOnlyList<ProjectBackupLookupRow> LocationRelationshipTypes { get; init; } = [];
public IReadOnlyList<ProjectBackupLookupRow> PlotLineTypes { get; init; } = [];
public IReadOnlyList<ProjectBackupLookupRow> PlotImportance { get; init; } = [];
public IReadOnlyList<ProjectBackupLookupRow> ThreadTypes { get; init; } = [];
public IReadOnlyList<ProjectBackupLookupRow> ThreadStatuses { get; init; } = [];
public IReadOnlyList<ProjectBackupLookupRow> ThreadEventTypes { get; init; } = [];
public IReadOnlyList<ProjectBackupLookupRow> PlotEventTypes { get; init; } = [];
public IReadOnlyList<ProjectBackupLookupRow> AssetKinds { get; init; } = [];
public IReadOnlyList<ProjectBackupLookupRow> AssetStates { get; init; } = [];
public IReadOnlyList<ProjectBackupLookupRow> AssetEventTypes { get; init; } = [];

View File

@ -3,7 +3,7 @@ namespace PlotLine.Models;
public sealed class ProjectBackupExport
{
public string Format { get; init; } = "PlotDirector.ProjectBackup";
public string FormatVersion { get; init; } = "1.0";
public string FormatVersion { get; init; } = "1.1";
public DateTime ExportedAtUtc { get; init; }
public string SourceApplication { get; init; } = "PlotDirector";
public Project? Project { get; init; }
@ -41,7 +41,12 @@ public sealed class ProjectBackupData
public IReadOnlyList<RelationshipEvent> RelationshipEvents { get; init; } = [];
public IReadOnlyList<LocationItem> Locations { get; init; } = [];
public IReadOnlyList<LocationRelationship> LocationRelationships { get; init; } = [];
public IReadOnlyList<FloorPlan> FloorPlans { get; init; } = [];
public IReadOnlyList<FloorPlanFloor> FloorPlanFloors { get; init; } = [];
public IReadOnlyList<FloorPlanBlock> FloorPlanBlocks { get; init; } = [];
public IReadOnlyList<FloorPlanTransition> FloorPlanTransitions { get; init; } = [];
public IReadOnlyList<SceneAssetLocation> SceneAssetLocations { get; init; } = [];
public IReadOnlyList<SceneFloorPlanOccupancy> SceneFloorPlanOccupancy { get; init; } = [];
public IReadOnlyList<SceneDependency> SceneDependencies { get; init; } = [];
public IReadOnlyList<ProjectBackupValidationRun> ValidationRuns { get; init; } = [];
public IReadOnlyList<ContinuityWarning> ContinuityWarnings { get; init; } = [];

View File

@ -533,7 +533,7 @@ public sealed class ImportService(IImportRepository imports, ICurrentUserService
validation.Errors.Add("The project backup format is not recognised.");
}
if (!string.Equals(backup.FormatVersion, "1.0", StringComparison.Ordinal))
if (!SupportedProjectBackupVersions.Contains(backup.FormatVersion, StringComparer.Ordinal))
{
validation.Errors.Add($"Project backup format version \"{backup.FormatVersion}\" is not supported.");
}
@ -744,12 +744,15 @@ public sealed class ImportService(IImportRepository imports, ICurrentUserService
"SceneOutcomes", "PlotLines", "PlotThreads", "ThreadEvents", "AssetStates", "StoryAssets", "AssetEvents",
"AssetDependencies", "AssetCustodyEvents", "AssetCustodyEventCharacters", "Characters", "SceneCharacters",
"CharacterAttributeTypes", "CharacterAttributeEvents", "CharacterKnowledge", "CharacterRelationships",
"RelationshipEvents", "Locations", "LocationRelationships", "SceneAssetLocations", "SceneDependencies",
"ValidationRuns", "ContinuityWarnings", "SceneNotes", "SceneWorkflow", "SceneChecklistItems",
"RelationshipEvents", "Locations", "LocationRelationships", "FloorPlans", "FloorPlanFloors",
"FloorPlanBlocks", "FloorPlanTransitions", "SceneAssetLocations", "SceneFloorPlanOccupancy",
"SceneDependencies", "ValidationRuns", "ContinuityWarnings", "SceneNotes", "SceneWorkflow", "SceneChecklistItems",
"SceneAttachments", "ChapterGoals", "Scenarios", "ScenarioSceneOrder", "ScenarioValidationRuns",
"ScenarioWarnings"
];
private static readonly string[] SupportedProjectBackupVersions = ["1.0", "1.1"];
private static void RemapProjectBackupForImport(
ProjectBackupExport backup,
int projectId,
@ -792,9 +795,11 @@ public sealed class ImportService(IImportRepository imports, ICurrentUserService
var locationTypeIds = LookupByName(relationshipLookups.LocationTypes);
var locationRelationshipTypeIds = LookupByName(relationshipLookups.LocationRelationshipTypes);
var plotLineTypeIds = LookupByName(relationshipLookups.PlotLineTypes);
var plotImportanceIds = LookupByName(relationshipLookups.PlotImportance);
var threadTypeIds = LookupByName(relationshipLookups.ThreadTypes);
var threadStatusIds = LookupByName(relationshipLookups.ThreadStatuses);
var threadEventTypeIds = LookupByName(relationshipLookups.ThreadEventTypes);
var plotEventTypeIds = LookupByName(relationshipLookups.PlotEventTypes);
var assetKindIds = LookupByName(relationshipLookups.AssetKinds);
var assetStateIds = LookupByName(relationshipLookups.AssetStates);
var assetEventTypeIds = LookupByName(relationshipLookups.AssetEventTypes);
@ -929,6 +934,35 @@ public sealed class ImportService(IImportRepository imports, ICurrentUserService
location.LocationTypeID = ResolveNullableLookupID(locationTypeIds, location.LocationTypeID, location.LocationTypeName, $"Location \"{location.LocationName}\" type");
}
foreach (var floorPlan in backup.Data.FloorPlans)
{
floorPlan.FloorPlanID = NewId("FloorPlans", floorPlan.FloorPlanID);
floorPlan.ProjectID = projectId;
floorPlan.LocationID = ExistingOrNewNullable("Locations", floorPlan.LocationID);
}
foreach (var floor in backup.Data.FloorPlanFloors)
{
floor.FloorPlanFloorID = NewId("FloorPlanFloors", floor.FloorPlanFloorID);
floor.FloorPlanID = ExistingOrNew("FloorPlans", floor.FloorPlanID);
floor.LocationID = ExistingOrNewNullable("Locations", floor.LocationID);
}
foreach (var block in backup.Data.FloorPlanBlocks)
{
block.FloorPlanBlockID = NewId("FloorPlanBlocks", block.FloorPlanBlockID);
block.FloorPlanFloorID = ExistingOrNew("FloorPlanFloors", block.FloorPlanFloorID);
block.LocationID = ExistingOrNew("Locations", block.LocationID);
}
foreach (var transition in backup.Data.FloorPlanTransitions)
{
transition.FloorPlanTransitionID = NewId("FloorPlanTransitions", transition.FloorPlanTransitionID);
transition.FloorPlanFloorID = ExistingOrNew("FloorPlanFloors", transition.FloorPlanFloorID);
transition.FromLocationID = ExistingOrNew("Locations", transition.FromLocationID);
transition.ToLocationID = ExistingOrNew("Locations", transition.ToLocationID);
}
foreach (var chapter in backup.Data.Chapters)
{
chapter.ChapterID = NewId("Chapters", chapter.ChapterID);
@ -943,6 +977,8 @@ public sealed class ImportService(IImportRepository imports, ICurrentUserService
scene.ChapterID = ExistingOrNew("Chapters", scene.ChapterID);
scene.POVCharacterID = ExistingOrNewNullable("Characters", scene.POVCharacterID);
scene.PrimaryLocationID = ExistingOrNewNullable("Locations", scene.PrimaryLocationID);
scene.FloorPlanID = ExistingOrNewNullable("FloorPlans", scene.FloorPlanID);
scene.InitialFloorPlanFloorID = ExistingOrNewNullable("FloorPlanFloors", scene.InitialFloorPlanFloorID);
scene.TimeModeID = ResolveLookupID(timeModeIds, scene.TimeModeID, scene.TimeModeName, $"Scene \"{scene.SceneTitle}\" time mode");
scene.DurationUnitID = ResolveNullableLookupID(durationUnitIds, scene.DurationUnitID, scene.DurationUnitName, $"Scene \"{scene.SceneTitle}\" duration unit");
scene.TimeConfidenceID = ResolveLookupID(timeConfidenceIds, scene.TimeConfidenceID, scene.TimeConfidenceName, $"Scene \"{scene.SceneTitle}\" time confidence");
@ -996,11 +1032,13 @@ public sealed class ImportService(IImportRepository imports, ICurrentUserService
plotLine.ProjectID = projectId;
plotLine.BookID = ExistingOrNewNullable("Books", plotLine.BookID);
plotLine.PlotLineTypeID = ResolveLookupID(plotLineTypeIds, plotLine.PlotLineTypeID, plotLine.PlotLineTypeName, $"Plot line \"{plotLine.PlotLineName}\" type");
plotLine.PlotImportanceID = ResolveLookupID(plotImportanceIds, plotLine.PlotImportanceID, plotLine.PlotImportanceName, $"Plot line \"{plotLine.PlotLineName}\" importance");
}
foreach (var plotLine in backup.Data.PlotLines)
{
plotLine.ParentPlotLineID = ExistingOrNewNullable("PlotLines", plotLine.ParentPlotLineID);
plotLine.EmergesFromPlotLineID = ExistingOrNewNullable("PlotLines", plotLine.EmergesFromPlotLineID);
}
foreach (var thread in backup.Data.PlotThreads)
@ -1022,6 +1060,8 @@ public sealed class ImportService(IImportRepository imports, ICurrentUserService
threadEvent.PlotLineID = ExistingOrNew("PlotLines", threadEvent.PlotLineID);
threadEvent.SceneID = ExistingOrNew("Scenes", threadEvent.SceneID);
threadEvent.EventTypeID = ResolveLookupID(threadEventTypeIds, threadEvent.EventTypeID, threadEvent.EventTypeName, $"Thread event \"{threadEvent.EventTitle}\" type");
threadEvent.PlotEventTypeID = ResolveLookupID(plotEventTypeIds, threadEvent.PlotEventTypeID, threadEvent.PlotEventTypeName, $"Thread event \"{threadEvent.EventTitle}\" plot event type");
threadEvent.TargetPlotLineID = ExistingOrNewNullable("PlotLines", threadEvent.TargetPlotLineID);
}
foreach (var asset in backup.Data.StoryAssets)
@ -1149,6 +1189,17 @@ public sealed class ImportService(IImportRepository imports, ICurrentUserService
sceneAssetLocation.LocationID = ExistingOrNew("Locations", sceneAssetLocation.LocationID);
}
foreach (var occupancy in backup.Data.SceneFloorPlanOccupancy)
{
occupancy.SceneFloorPlanOccupancyID = NewId("SceneFloorPlanOccupancy", occupancy.SceneFloorPlanOccupancyID);
occupancy.SceneID = ExistingOrNew("Scenes", occupancy.SceneID);
occupancy.FloorPlanID = ExistingOrNew("FloorPlans", occupancy.FloorPlanID);
occupancy.FloorPlanFloorID = ExistingOrNewNullable("FloorPlanFloors", occupancy.FloorPlanFloorID);
occupancy.CharacterID = ExistingOrNewNullable("Characters", occupancy.CharacterID);
occupancy.StoryAssetID = ExistingOrNewNullable("StoryAssets", occupancy.StoryAssetID);
occupancy.LocationID = ExistingOrNew("Locations", occupancy.LocationID);
}
foreach (var dependency in backup.Data.SceneDependencies)
{
dependency.SceneDependencyID = NewId("SceneDependencies", dependency.SceneDependencyID);
@ -1261,6 +1312,11 @@ public sealed class ImportService(IImportRepository imports, ICurrentUserService
"relationship" or "characterrelationship" or "character relationship" => "CharacterRelationships",
"thread event" or "threadevent" => "ThreadEvents",
"asset event" or "assetevent" => "AssetEvents",
"floorplan" or "floor plan" => "FloorPlans",
"floorplanfloor" or "floor plan floor" => "FloorPlanFloors",
"floorplanblock" or "floor plan block" => "FloorPlanBlocks",
"floorplantransition" or "floor plan transition" => "FloorPlanTransitions",
"scenefloorplanoccupancy" or "scene floor plan occupancy" => "SceneFloorPlanOccupancy",
_ => null
};

View File

@ -0,0 +1,875 @@
SET ANSI_NULLS ON;
GO
SET QUOTED_IDENTIFIER ON;
GO
CREATE OR ALTER PROCEDURE dbo.ProjectBackup_Export
@ProjectID int,
@UserID int = NULL
AS
BEGIN
SET NOCOUNT ON;
IF @UserID IS NOT NULL
AND NOT EXISTS
(
SELECT 1
FROM dbo.ProjectUserAccess
WHERE ProjectID = @ProjectID
AND UserID = @UserID
AND AccessRole = N'Owner'
AND IsActive = 1
)
SET @ProjectID = -1;
SELECT ProjectID, ProjectName, Description, CreatedDate, UpdatedDate, IsArchived, ArchivedDate, ArchivedReason
FROM dbo.Projects
WHERE ProjectID = @ProjectID;
SELECT BookID, ProjectID, BookTitle, BookNumber, Description, SortOrder, CreatedDate, UpdatedDate,
IsArchived, ArchivedDate, ArchivedReason
FROM dbo.Books
WHERE ProjectID = @ProjectID
ORDER BY SortOrder, BookNumber, BookID;
SELECT c.ChapterID, c.BookID, c.ChapterNumber, c.ChapterTitle, c.POVCharacterID, c.Summary, c.SortOrder,
c.RevisionStatusID, rs.StatusName AS RevisionStatusName, c.CreatedDate, c.UpdatedDate,
c.IsArchived, c.ArchivedDate, c.ArchivedReason
FROM dbo.Chapters c
INNER JOIN dbo.Books b ON b.BookID = c.BookID
INNER JOIN dbo.RevisionStatuses rs ON rs.RevisionStatusID = c.RevisionStatusID
WHERE b.ProjectID = @ProjectID
ORDER BY b.SortOrder, c.SortOrder, c.ChapterID;
SELECT s.SceneID, s.ChapterID, s.SceneNumber, s.SceneTitle, s.Summary, s.POVCharacterID, s.PrimaryLocationID,
l.LocationName AS PrimaryLocationName, l.LocationName AS PrimaryLocationPath, s.SortOrder, s.TimeModeID, tm.TimeModeName,
s.StartDateTime, s.EndDateTime, s.DurationAmount, s.DurationUnitID, du.DurationUnitName,
s.RelativeTimeText, s.TimeConfidenceID, tc.TimeConfidenceName, s.ScenePurposeNotes, s.SceneOutcomeNotes,
s.RevisionStatusID, rs.StatusName AS RevisionStatusName, s.FloorPlanID, s.InitialFloorPlanFloorID, s.CreatedDate, s.UpdatedDate,
s.IsArchived, s.ArchivedDate, s.ArchivedReason
FROM dbo.Scenes s
INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID
INNER JOIN dbo.Books b ON b.BookID = c.BookID
INNER JOIN dbo.TimeModes tm ON tm.TimeModeID = s.TimeModeID
INNER JOIN dbo.TimeConfidences tc ON tc.TimeConfidenceID = s.TimeConfidenceID
INNER JOIN dbo.RevisionStatuses rs ON rs.RevisionStatusID = s.RevisionStatusID
LEFT JOIN dbo.DurationUnits du ON du.DurationUnitID = s.DurationUnitID
LEFT JOIN dbo.Locations l ON l.LocationID = s.PrimaryLocationID
WHERE b.ProjectID = @ProjectID
ORDER BY b.SortOrder, c.SortOrder, s.SortOrder, s.SceneID;
SELECT sp.SceneID, sp.ScenePurposeTypeID, spt.PurposeName, spt.SortOrder
FROM dbo.ScenePurposes sp
INNER JOIN dbo.ScenePurposeTypes spt ON spt.ScenePurposeTypeID = sp.ScenePurposeTypeID
INNER JOIN dbo.Scenes s ON s.SceneID = sp.SceneID
INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID
INNER JOIN dbo.Books b ON b.BookID = c.BookID
WHERE b.ProjectID = @ProjectID
ORDER BY sp.SceneID, spt.SortOrder;
SELECT so.SceneOutcomeID, so.SceneID, so.OutcomeDescription, so.OutcomeTypeID, so.CreatedDate, so.UpdatedDate
FROM dbo.SceneOutcomes so
INNER JOIN dbo.Scenes s ON s.SceneID = so.SceneID
INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID
INNER JOIN dbo.Books b ON b.BookID = c.BookID
WHERE b.ProjectID = @ProjectID
ORDER BY so.SceneID, so.SceneOutcomeID;
SELECT mt.MetricTypeID, mt.ProjectID, mt.MetricName, mt.Description, mt.MinValue, mt.MaxValue,
mt.DefaultValue, COALESCE(ptms.SortOrder, mt.SortOrder) AS SortOrder, mt.IsActive,
CAST(CASE WHEN mt.IsActive = 1 AND ptms.MetricTypeID IS NOT NULL THEN 1 ELSE 0 END AS bit) AS IsEnabledForProject,
CAST(CASE WHEN mt.ProjectID = @ProjectID THEN 1 ELSE 0 END AS bit) AS IsCustom,
STUFF((
SELECT N',' + gmp.PresetKey
FROM dbo.GenreMetricPresetMetrics gpmm
INNER JOIN dbo.GenreMetricPresets gmp ON gmp.GenreMetricPresetID = gpmm.GenreMetricPresetID
WHERE gpmm.MetricTypeID = mt.MetricTypeID
ORDER BY gmp.SortOrder, gmp.PresetName
FOR XML PATH(N''), TYPE).value(N'.', N'nvarchar(max)'), 1, 1, N'') AS GenrePresetKeys,
STUFF((
SELECT N', ' + gmp.PresetName
FROM dbo.GenreMetricPresetMetrics gpmm
INNER JOIN dbo.GenreMetricPresets gmp ON gmp.GenreMetricPresetID = gpmm.GenreMetricPresetID
WHERE gpmm.MetricTypeID = mt.MetricTypeID
ORDER BY gmp.SortOrder, gmp.PresetName
FOR XML PATH(N''), TYPE).value(N'.', N'nvarchar(max)'), 1, 2, N'') AS GenrePresetNames,
(
SELECT COUNT(1)
FROM dbo.SceneMetricValues smv
INNER JOIN dbo.Scenes s ON s.SceneID = smv.SceneID
INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID
INNER JOIN dbo.Books b ON b.BookID = c.BookID
WHERE smv.MetricTypeID = mt.MetricTypeID AND b.ProjectID = @ProjectID
) AS ValueCount
FROM dbo.SceneMetricTypes mt
LEFT JOIN dbo.ProjectTimelineMetricSettings ptms ON ptms.ProjectID = @ProjectID AND ptms.MetricTypeID = mt.MetricTypeID
WHERE mt.ProjectID IS NULL
OR mt.ProjectID = @ProjectID
OR EXISTS
(
SELECT 1
FROM dbo.SceneMetricValues smv
INNER JOIN dbo.Scenes s ON s.SceneID = smv.SceneID
INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID
INNER JOIN dbo.Books b ON b.BookID = c.BookID
WHERE smv.MetricTypeID = mt.MetricTypeID AND b.ProjectID = @ProjectID
)
ORDER BY COALESCE(ptms.SortOrder, mt.SortOrder), mt.MetricName;
SELECT smv.SceneID, smv.MetricTypeID, mt.MetricName, mt.Description, mt.MinValue, mt.MaxValue,
mt.DefaultValue, mt.SortOrder, mt.IsActive, smv.Value, smv.Notes
FROM dbo.SceneMetricValues smv
INNER JOIN dbo.SceneMetricTypes mt ON mt.MetricTypeID = smv.MetricTypeID
INNER JOIN dbo.Scenes s ON s.SceneID = smv.SceneID
INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID
INNER JOIN dbo.Books b ON b.BookID = c.BookID
WHERE b.ProjectID = @ProjectID
ORDER BY smv.SceneID, mt.SortOrder;
SELECT ProjectTimelineSettingsID, ProjectID, ShowSceneCards, ShowMetricShape, ShowPlotLines,
ShowStoryAssets, ShowCharacterAppearances, ShowWarnings, CreatedDate, UpdatedDate
FROM dbo.ProjectTimelineSettings
WHERE ProjectID = @ProjectID;
SELECT ptms.ProjectID, ptms.MetricTypeID, mt.MetricName, ptms.SortOrder, ptms.CreatedDate
FROM dbo.ProjectTimelineMetricSettings ptms
INNER JOIN dbo.SceneMetricTypes mt ON mt.MetricTypeID = ptms.MetricTypeID
WHERE ptms.ProjectID = @ProjectID
ORDER BY ptms.SortOrder;
SELECT TimelineViewPresetID, ProjectID, BookID, PresetName, FilterJson, VisibilityJson, FocusType, FocusID,
CreatedDate, UpdatedDate, IsArchived
FROM dbo.TimelineViewPresets
WHERE ProjectID = @ProjectID
ORDER BY PresetName;
SELECT pl.PlotLineID, pl.ProjectID, pl.BookID, b.BookTitle, pl.PlotLineName, pl.PlotLineTypeID,
plt.TypeName AS PlotLineTypeName, pl.PlotImportanceID, pi.ImportanceName AS PlotImportanceName,
pl.Description, pl.ParentPlotLineID, parent.PlotLineName AS ParentPlotLineName,
pl.EmergesFromPlotLineID, emerges.PlotLineName AS EmergesFromPlotLineName,
pl.SortOrder, pl.Colour, pl.IsVisibleOnTimeline, pl.CreatedDate, pl.UpdatedDate,
pl.IsArchived, pl.ArchivedDate, pl.ArchivedReason
FROM dbo.PlotLines pl
INNER JOIN dbo.PlotLineTypes plt ON plt.PlotLineTypeID = pl.PlotLineTypeID
INNER JOIN dbo.PlotImportance pi ON pi.PlotImportanceID = pl.PlotImportanceID
LEFT JOIN dbo.Books b ON b.BookID = pl.BookID
LEFT JOIN dbo.PlotLines parent ON parent.PlotLineID = pl.ParentPlotLineID
LEFT JOIN dbo.PlotLines emerges ON emerges.PlotLineID = pl.EmergesFromPlotLineID
WHERE pl.ProjectID = @ProjectID
ORDER BY pl.SortOrder, pl.PlotLineName;
SELECT pt.PlotThreadID, pt.PlotLineID, pl.ProjectID, pl.PlotLineName, pt.ThreadTitle, pt.ThreadTypeID,
tt.TypeName AS ThreadTypeName, pt.ThreadStatusID, ts.StatusName AS ThreadStatusName, pt.Importance,
pt.Summary, pt.IntroducedSceneID, pt.PlannedResolutionSceneID, pt.ActualResolutionSceneID,
pt.CreatedDate, pt.UpdatedDate, pt.IsArchived, pt.ArchivedDate, pt.ArchivedReason
FROM dbo.PlotThreads pt
INNER JOIN dbo.PlotLines pl ON pl.PlotLineID = pt.PlotLineID
INNER JOIN dbo.ThreadTypes tt ON tt.ThreadTypeID = pt.ThreadTypeID
INNER JOIN dbo.ThreadStatuses ts ON ts.ThreadStatusID = pt.ThreadStatusID
WHERE pl.ProjectID = @ProjectID
ORDER BY pl.SortOrder, pt.Importance DESC, pt.ThreadTitle;
SELECT te.ThreadEventID, te.PlotThreadID, pt.ThreadTitle, pl.PlotLineID, pl.PlotLineName, pl.Colour,
te.SceneID, te.EventTypeID, tet.TypeName AS EventTypeName, tet.MarkerText,
te.PlotEventTypeID, pet.TypeName AS PlotEventTypeName, te.TargetPlotLineID, target.PlotLineName AS TargetPlotLineName,
te.EventTitle, te.EventDescription, te.CreatedDate, te.UpdatedDate
FROM dbo.ThreadEvents te
INNER JOIN dbo.PlotThreads pt ON pt.PlotThreadID = te.PlotThreadID
INNER JOIN dbo.PlotLines pl ON pl.PlotLineID = pt.PlotLineID
INNER JOIN dbo.ThreadEventTypes tet ON tet.ThreadEventTypeID = te.EventTypeID
INNER JOIN dbo.PlotEventTypes pet ON pet.PlotEventTypeID = te.PlotEventTypeID
LEFT JOIN dbo.PlotLines target ON target.PlotLineID = te.TargetPlotLineID
WHERE pl.ProjectID = @ProjectID
ORDER BY te.SceneID, te.ThreadEventID;
SELECT sa.StoryAssetID, sa.ProjectID, sa.AssetName, sa.AssetKindID, ak.KindName, sa.Description,
sa.Importance, sa.CurrentStateID, ast.StateName AS CurrentStateName, sa.CurrentLocationID,
l.LocationName AS CurrentLocationName, l.LocationName AS CurrentLocationPath, sa.IsResolved,
sa.ShowInQuickAddBar, sa.ImagePath, sa.ThumbnailPath, sa.CreatedDate, sa.UpdatedDate, sa.IsArchived, sa.ArchivedDate, sa.ArchivedReason
FROM dbo.StoryAssets sa
INNER JOIN dbo.AssetKinds ak ON ak.AssetKindID = sa.AssetKindID
LEFT JOIN dbo.AssetStates ast ON ast.AssetStateID = sa.CurrentStateID
LEFT JOIN dbo.Locations l ON l.LocationID = sa.CurrentLocationID
WHERE sa.ProjectID = @ProjectID
ORDER BY sa.Importance DESC, sa.AssetName;
SELECT ast.AssetStateID, ast.ProjectID, ast.AssetKindID, ak.KindName, ast.StateName, ast.SortOrder, ast.Description,
ast.IsResolvedState, ast.IsActive
FROM dbo.AssetStates ast
LEFT JOIN dbo.AssetKinds ak ON ak.AssetKindID = ast.AssetKindID
WHERE ast.ProjectID IS NULL
OR ast.ProjectID = @ProjectID
OR EXISTS (SELECT 1 FROM dbo.StoryAssets sa WHERE sa.ProjectID = @ProjectID AND sa.CurrentStateID = ast.AssetStateID)
OR EXISTS (SELECT 1 FROM dbo.AssetEvents ae INNER JOIN dbo.StoryAssets sa ON sa.StoryAssetID = ae.StoryAssetID WHERE sa.ProjectID = @ProjectID AND (ae.FromStateID = ast.AssetStateID OR ae.ToStateID = ast.AssetStateID))
OR EXISTS (SELECT 1 FROM dbo.AssetDependencies ad INNER JOIN dbo.StoryAssets sa ON sa.StoryAssetID = ad.SourceAssetID WHERE sa.ProjectID = @ProjectID AND (ad.SourceRequiredStateID = ast.AssetStateID OR ad.TargetBlockedStateID = ast.AssetStateID))
ORDER BY ast.SortOrder, ast.StateName;
SELECT ae.AssetEventID, ae.StoryAssetID, sa.ProjectID, sa.AssetName, sa.AssetKindID, ak.KindName,
ae.SceneID, ae.AssetEventTypeID, aet.TypeName AS AssetEventTypeName, aet.MarkerText,
ae.FromStateID, fs.StateName AS FromStateName, ae.ToStateID, ts.StateName AS ToStateName,
ae.EventTitle, ae.EventDescription, b.BookTitle, c.ChapterNumber, s.SceneNumber, s.SceneTitle,
ae.CreatedDate, ae.UpdatedDate
FROM dbo.AssetEvents ae
INNER JOIN dbo.StoryAssets sa ON sa.StoryAssetID = ae.StoryAssetID
INNER JOIN dbo.AssetKinds ak ON ak.AssetKindID = sa.AssetKindID
INNER JOIN dbo.AssetEventTypes aet ON aet.AssetEventTypeID = ae.AssetEventTypeID
INNER JOIN dbo.Scenes s ON s.SceneID = ae.SceneID
INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID
INNER JOIN dbo.Books b ON b.BookID = c.BookID
LEFT JOIN dbo.AssetStates fs ON fs.AssetStateID = ae.FromStateID
LEFT JOIN dbo.AssetStates ts ON ts.AssetStateID = ae.ToStateID
WHERE sa.ProjectID = @ProjectID
ORDER BY b.SortOrder, c.SortOrder, s.SortOrder, ae.AssetEventID;
SELECT ad.AssetDependencyID, ad.SourceAssetID, source.AssetName AS SourceAssetName, ad.TargetAssetID,
target.AssetName AS TargetAssetName, ad.AssetDependencyTypeID, adt.TypeName AS DependencyTypeName,
adt.IsValidatingDependency, ad.SourceRequiredStateID, srs.StateName AS SourceRequiredStateName,
ad.TargetBlockedStateID, tbs.StateName AS TargetBlockedStateName, ad.Description,
ad.CreatedDate, ad.UpdatedDate, ad.IsArchived
FROM dbo.AssetDependencies ad
INNER JOIN dbo.StoryAssets source ON source.StoryAssetID = ad.SourceAssetID
INNER JOIN dbo.StoryAssets target ON target.StoryAssetID = ad.TargetAssetID
INNER JOIN dbo.AssetDependencyTypes adt ON adt.AssetDependencyTypeID = ad.AssetDependencyTypeID
LEFT JOIN dbo.AssetStates srs ON srs.AssetStateID = ad.SourceRequiredStateID
LEFT JOIN dbo.AssetStates tbs ON tbs.AssetStateID = ad.TargetBlockedStateID
WHERE source.ProjectID = @ProjectID OR target.ProjectID = @ProjectID
ORDER BY source.AssetName, target.AssetName;
SELECT ace.AssetCustodyEventID, ace.StoryAssetID, sa.AssetName, ace.SceneID, ace.AssetCustodyEventTypeID,
acet.TypeName AS CustodyEventTypeName, ace.Description,
STRING_AGG(COALESCE(ch.CharacterName, acec.CharacterNameText), ', ') AS CustodianSummary,
b.BookTitle, c.ChapterNumber, s.SceneNumber, s.SceneTitle, ace.CreatedDate, ace.UpdatedDate
FROM dbo.AssetCustodyEvents ace
INNER JOIN dbo.StoryAssets sa ON sa.StoryAssetID = ace.StoryAssetID
INNER JOIN dbo.AssetCustodyEventTypes acet ON acet.AssetCustodyEventTypeID = ace.AssetCustodyEventTypeID
INNER JOIN dbo.Scenes s ON s.SceneID = ace.SceneID
INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID
INNER JOIN dbo.Books b ON b.BookID = c.BookID
LEFT JOIN dbo.AssetCustodyEventCharacters acec ON acec.AssetCustodyEventID = ace.AssetCustodyEventID
LEFT JOIN dbo.Characters ch ON ch.CharacterID = acec.CharacterID
WHERE sa.ProjectID = @ProjectID
GROUP BY ace.AssetCustodyEventID, ace.StoryAssetID, sa.AssetName, ace.SceneID, ace.AssetCustodyEventTypeID,
acet.TypeName, ace.Description, b.BookTitle, c.ChapterNumber, s.SceneNumber, s.SceneTitle, ace.CreatedDate, ace.UpdatedDate
ORDER BY MIN(b.SortOrder), MIN(c.SortOrder), MIN(s.SortOrder), ace.AssetCustodyEventID;
SELECT acec.AssetCustodyEventCharacterID, acec.AssetCustodyEventID, acec.CharacterID, ch.CharacterName,
acec.CharacterNameText, acec.CustodyRoleID, cr.RoleName AS CustodyRoleName
FROM dbo.AssetCustodyEventCharacters acec
INNER JOIN dbo.AssetCustodyEvents ace ON ace.AssetCustodyEventID = acec.AssetCustodyEventID
INNER JOIN dbo.StoryAssets sa ON sa.StoryAssetID = ace.StoryAssetID
INNER JOIN dbo.CustodyRoles cr ON cr.CustodyRoleID = acec.CustodyRoleID
LEFT JOIN dbo.Characters ch ON ch.CharacterID = acec.CharacterID
WHERE sa.ProjectID = @ProjectID
ORDER BY acec.AssetCustodyEventID, acec.AssetCustodyEventCharacterID;
SELECT CharacterID, ProjectID, CharacterName, ShortName, Sex, BirthDate, AgeAtSeriesStart,
AgeReferenceSceneID, Height, EyeColour, CharacterImportance, ShowInQuickAddBar,
DefaultDescription, ImagePath, ThumbnailPath, CreatedDate, UpdatedDate, IsArchived, ArchivedDate, ArchivedReason
FROM dbo.Characters
WHERE ProjectID = @ProjectID
ORDER BY CharacterImportance DESC, CharacterName;
SELECT sc.SceneCharacterID, sc.SceneID, sc.CharacterID, ch.ProjectID, ch.CharacterName, ch.ShortName,
ch.BirthDate, ch.AgeAtSeriesStart, sc.RoleInSceneTypeID, rist.TypeName AS RoleInSceneTypeName,
sc.PresenceTypeID, pt.TypeName AS PresenceTypeName, sc.LocationID, loc.LocationName,
loc.LocationName AS LocationPath, sc.EntryLocationID, entryLoc.LocationName AS EntryLocationName,
sc.ExitLocationID, exitLoc.LocationName AS ExitLocationName, sc.AppearanceNotes, sc.OutfitDescription,
sc.PhysicalCondition, sc.EmotionalState, sc.KnowledgeNotes, s.SceneNumber, s.SceneTitle,
c.ChapterNumber, b.BookTitle, sc.CreatedDate, sc.UpdatedDate
FROM dbo.SceneCharacters sc
INNER JOIN dbo.Characters ch ON ch.CharacterID = sc.CharacterID
INNER JOIN dbo.Scenes s ON s.SceneID = sc.SceneID
INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID
INNER JOIN dbo.Books b ON b.BookID = c.BookID
LEFT JOIN dbo.CharacterRoleInSceneTypes rist ON rist.CharacterRoleInSceneTypeID = sc.RoleInSceneTypeID
LEFT JOIN dbo.PresenceTypes pt ON pt.PresenceTypeID = sc.PresenceTypeID
LEFT JOIN dbo.Locations loc ON loc.LocationID = sc.LocationID
LEFT JOIN dbo.Locations entryLoc ON entryLoc.LocationID = sc.EntryLocationID
LEFT JOIN dbo.Locations exitLoc ON exitLoc.LocationID = sc.ExitLocationID
WHERE ch.ProjectID = @ProjectID
ORDER BY b.SortOrder, c.SortOrder, s.SortOrder, ch.CharacterName;
SELECT cat.CharacterAttributeTypeID, cat.ProjectID, cat.AttributeName, cat.DataType, cat.IsNormallyStable,
cat.SortOrder, cat.IsActive
FROM dbo.CharacterAttributeTypes cat
WHERE cat.ProjectID IS NULL
OR cat.ProjectID = @ProjectID
OR EXISTS (SELECT 1 FROM dbo.CharacterAttributeEvents cae INNER JOIN dbo.Characters ch ON ch.CharacterID = cae.CharacterID WHERE ch.ProjectID = @ProjectID AND cae.CharacterAttributeTypeID = cat.CharacterAttributeTypeID)
ORDER BY cat.SortOrder, cat.AttributeName;
SELECT cae.CharacterAttributeEventID, cae.CharacterID, ch.CharacterName, cae.CharacterAttributeTypeID,
cat.AttributeName, cae.SceneID, s.SceneNumber, s.SceneTitle, c.ChapterNumber, b.BookTitle,
cae.AttributeValue, cae.Description, cae.CreatedDate, cae.UpdatedDate
FROM dbo.CharacterAttributeEvents cae
INNER JOIN dbo.Characters ch ON ch.CharacterID = cae.CharacterID
INNER JOIN dbo.CharacterAttributeTypes cat ON cat.CharacterAttributeTypeID = cae.CharacterAttributeTypeID
INNER JOIN dbo.Scenes s ON s.SceneID = cae.SceneID
INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID
INNER JOIN dbo.Books b ON b.BookID = c.BookID
WHERE ch.ProjectID = @ProjectID
ORDER BY ch.CharacterName, b.SortOrder, c.SortOrder, s.SortOrder;
SELECT ck.CharacterKnowledgeID, ck.CharacterID, ch.CharacterName, ck.StoryAssetID, sa.AssetName,
ck.PlotThreadID, pt.ThreadTitle, ck.SceneID, s.SceneNumber, s.SceneTitle, c.ChapterNumber, b.BookTitle,
ck.KnowledgeStateID, ks.StateName AS KnowledgeStateName, ck.SourceEventID, ck.Description,
ck.CreatedDate, ck.UpdatedDate
FROM dbo.CharacterKnowledge ck
INNER JOIN dbo.Characters ch ON ch.CharacterID = ck.CharacterID
INNER JOIN dbo.KnowledgeStates ks ON ks.KnowledgeStateID = ck.KnowledgeStateID
INNER JOIN dbo.Scenes s ON s.SceneID = ck.SceneID
INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID
INNER JOIN dbo.Books b ON b.BookID = c.BookID
LEFT JOIN dbo.StoryAssets sa ON sa.StoryAssetID = ck.StoryAssetID
LEFT JOIN dbo.PlotThreads pt ON pt.PlotThreadID = ck.PlotThreadID
WHERE ch.ProjectID = @ProjectID
ORDER BY ch.CharacterName, b.SortOrder, c.SortOrder, s.SortOrder;
SELECT cr.CharacterRelationshipID, cr.ProjectID, cr.CharacterAID, a.CharacterName AS CharacterAName,
cr.CharacterBID, bch.CharacterName AS CharacterBName, cr.RelationshipTypeID, rt.TypeName AS RelationshipTypeName,
rt.RelationshipCategoryID, rc.CategoryName AS RelationshipCategoryName, rc.SortOrder AS CategorySortOrder,
rt.SortOrder AS RelationshipTypeSortOrder, cr.IsPermanent, cr.StartSceneID, cr.EndSceneID,
cr.IsKnownToReader, cr.Notes, cr.IsInitialRelationship, cr.InitialBookID, ib.BookTitle AS InitialBookTitle,
cr.ReaderInitiallyKnows, cr.InitialRelationshipStateID, irs.StateName AS InitialRelationshipStateName,
cr.InitialIntensity, cr.IsReciprocal, cr.CreatedDate, cr.UpdatedDate,
cr.IsArchived, cr.ArchivedDate, cr.ArchivedReason
FROM dbo.CharacterRelationships cr
INNER JOIN dbo.Characters a ON a.CharacterID = cr.CharacterAID
INNER JOIN dbo.Characters bch ON bch.CharacterID = cr.CharacterBID
INNER JOIN dbo.RelationshipTypes rt ON rt.RelationshipTypeID = cr.RelationshipTypeID
INNER JOIN dbo.RelationshipCategories rc ON rc.RelationshipCategoryID = rt.RelationshipCategoryID
LEFT JOIN dbo.Books ib ON ib.BookID = cr.InitialBookID
LEFT JOIN dbo.RelationshipStates irs ON irs.RelationshipStateID = cr.InitialRelationshipStateID
WHERE cr.ProjectID = @ProjectID
ORDER BY a.CharacterName, bch.CharacterName, cr.CharacterRelationshipID;
SELECT re.RelationshipEventID, re.CharacterRelationshipID, cr.ProjectID, cr.CharacterAID,
a.CharacterName AS CharacterAName, cr.CharacterBID, bch.CharacterName AS CharacterBName,
rt.TypeName AS RelationshipTypeName, rt.RelationshipCategoryID, rc.CategoryName AS RelationshipCategoryName,
rc.SortOrder AS CategorySortOrder, rt.SortOrder AS RelationshipTypeSortOrder, cr.IsReciprocal,
re.SceneID, s.SceneNumber, s.SceneTitle, c.ChapterNumber, bk.BookTitle, re.RelationshipStateID,
rs.StateName AS RelationshipStateName, re.Intensity, re.IsKnownToOtherCharacter, re.Description,
re.CreatedDate, re.UpdatedDate
FROM dbo.RelationshipEvents re
INNER JOIN dbo.CharacterRelationships cr ON cr.CharacterRelationshipID = re.CharacterRelationshipID
INNER JOIN dbo.Characters a ON a.CharacterID = cr.CharacterAID
INNER JOIN dbo.Characters bch ON bch.CharacterID = cr.CharacterBID
INNER JOIN dbo.RelationshipTypes rt ON rt.RelationshipTypeID = cr.RelationshipTypeID
INNER JOIN dbo.RelationshipCategories rc ON rc.RelationshipCategoryID = rt.RelationshipCategoryID
INNER JOIN dbo.RelationshipStates rs ON rs.RelationshipStateID = re.RelationshipStateID
INNER JOIN dbo.Scenes s ON s.SceneID = re.SceneID
INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID
INNER JOIN dbo.Books bk ON bk.BookID = c.BookID
WHERE cr.ProjectID = @ProjectID
ORDER BY bk.SortOrder, c.SortOrder, s.SortOrder, re.RelationshipEventID;
SELECT l.LocationID, l.ProjectID, l.ParentLocationID, parent.LocationName AS ParentLocationName,
l.LocationName, l.LocationTypeID, lt.TypeName AS LocationTypeName, l.Description,
l.LocationName AS LocationPath, CAST(0 AS int) AS Depth, l.ShowInQuickAddBar,
l.CreatedDate, l.UpdatedDate, l.IsArchived, l.ArchivedDate, l.ArchivedReason
FROM dbo.Locations l
LEFT JOIN dbo.Locations parent ON parent.LocationID = l.ParentLocationID
LEFT JOIN dbo.LocationTypes lt ON lt.LocationTypeID = l.LocationTypeID
WHERE l.ProjectID = @ProjectID
ORDER BY l.LocationName;
SELECT lr.LocationRelationshipID, lr.FromLocationID, fl.LocationName AS FromLocationName,
lr.ToLocationID, tl.LocationName AS ToLocationName, lr.LocationRelationshipTypeID,
lrt.TypeName AS RelationshipTypeName, lr.IsBidirectional, lr.Strength,
lr.CanHearNormalSpeech, lr.CanHearLoudNoise, lr.CanSeeMovement, lr.CanSeeClearly,
lr.CanTravelDirectly, lr.Notes, lr.CreatedDate, lr.UpdatedDate, lr.IsArchived
FROM dbo.LocationRelationships lr
INNER JOIN dbo.Locations fl ON fl.LocationID = lr.FromLocationID
INNER JOIN dbo.Locations tl ON tl.LocationID = lr.ToLocationID
INNER JOIN dbo.LocationRelationshipTypes lrt ON lrt.LocationRelationshipTypeID = lr.LocationRelationshipTypeID
WHERE fl.ProjectID = @ProjectID OR tl.ProjectID = @ProjectID
ORDER BY fl.LocationName, tl.LocationName;
SELECT fp.FloorPlanID, fp.ProjectID, fp.LocationID, lp.LocationName, COALESCE(NULLIF(lp.LocationPath, N''), lp.LocationName) AS LocationPath,
fp.Name, fp.Description, fp.CreatedDate, fp.ModifiedDate,
(SELECT COUNT(1) FROM dbo.FloorPlanFloors fpf WHERE fpf.FloorPlanID = fp.FloorPlanID) AS FloorCount,
(SELECT COUNT(1) FROM dbo.FloorPlanBlocks fpb INNER JOIN dbo.FloorPlanFloors fpf ON fpf.FloorPlanFloorID = fpb.FloorPlanFloorID WHERE fpf.FloorPlanID = fp.FloorPlanID) AS BlockCount
FROM dbo.FloorPlans fp
LEFT JOIN dbo.LocationPaths lp ON lp.LocationID = fp.LocationID
WHERE fp.ProjectID = @ProjectID
ORDER BY fp.Name, fp.FloorPlanID;
SELECT fpf.FloorPlanFloorID, fpf.FloorPlanID, fpf.LocationID, lp.LocationName, COALESCE(NULLIF(lp.LocationPath, N''), lp.LocationName) AS LocationPath,
fpf.Name, fpf.Description, fpf.SortOrder, fpf.GridWidth, fpf.GridHeight,
fpf.BackgroundImagePath, fpf.BackgroundImageOriginalFileName, fpf.BackgroundOpacity, fpf.BackgroundScale,
fpf.BackgroundOffsetX, fpf.BackgroundOffsetY, fpf.BackgroundLocked, fpf.CreatedDate, fpf.ModifiedDate
FROM dbo.FloorPlanFloors fpf
INNER JOIN dbo.FloorPlans fp ON fp.FloorPlanID = fpf.FloorPlanID
LEFT JOIN dbo.LocationPaths lp ON lp.LocationID = fpf.LocationID
WHERE fp.ProjectID = @ProjectID
ORDER BY fp.Name, fpf.SortOrder, fpf.Name, fpf.FloorPlanFloorID;
SELECT fpb.FloorPlanBlockID, fpb.FloorPlanFloorID, fpb.LocationID, lp.LocationName,
COALESCE(NULLIF(lp.LocationPath, N''), lp.LocationName) AS LocationPath,
fpb.X, fpb.Y, fpb.WidthCells, fpb.HeightCells, fpb.BlockType, fpb.LabelOverride, fpb.Notes,
fpb.CreatedDate, fpb.ModifiedDate
FROM dbo.FloorPlanBlocks fpb
INNER JOIN dbo.FloorPlanFloors fpf ON fpf.FloorPlanFloorID = fpb.FloorPlanFloorID
INNER JOIN dbo.FloorPlans fp ON fp.FloorPlanID = fpf.FloorPlanID
INNER JOIN dbo.LocationPaths lp ON lp.LocationID = fpb.LocationID
WHERE fp.ProjectID = @ProjectID
ORDER BY fp.Name, fpf.SortOrder, fpb.Y, fpb.X, lp.LocationName;
SELECT fpt.FloorPlanTransitionID, fpt.FloorPlanFloorID, fpt.FromLocationID,
fromPath.LocationName AS FromLocationName, COALESCE(NULLIF(fromPath.LocationPath, N''), fromPath.LocationName) AS FromLocationPath,
fpt.ToLocationID, toPath.LocationName AS ToLocationName, COALESCE(NULLIF(toPath.LocationPath, N''), toPath.LocationName) AS ToLocationPath,
fpt.TransitionType, fpt.Notes, fpt.IsLocked, fpt.DisplayLabel, fpt.PositionWithinLocation,
fpt.CreatedDate, fpt.ModifiedDate
FROM dbo.FloorPlanTransitions fpt
INNER JOIN dbo.FloorPlanFloors fpf ON fpf.FloorPlanFloorID = fpt.FloorPlanFloorID
INNER JOIN dbo.FloorPlans fp ON fp.FloorPlanID = fpf.FloorPlanID
INNER JOIN dbo.LocationPaths fromPath ON fromPath.LocationID = fpt.FromLocationID
INNER JOIN dbo.LocationPaths toPath ON toPath.LocationID = fpt.ToLocationID
WHERE fp.ProjectID = @ProjectID
ORDER BY fp.Name, fpf.SortOrder, fromPath.LocationName, toPath.LocationName, fpt.TransitionType;
SELECT sal.SceneAssetLocationID, sal.SceneID, sal.StoryAssetID, sa.AssetName, sal.LocationID,
l.LocationName, l.LocationName AS LocationPath, sal.Description, sal.CreatedDate, sal.UpdatedDate
FROM dbo.SceneAssetLocations sal
INNER JOIN dbo.StoryAssets sa ON sa.StoryAssetID = sal.StoryAssetID
INNER JOIN dbo.Locations l ON l.LocationID = sal.LocationID
WHERE sa.ProjectID = @ProjectID
ORDER BY sal.SceneID, sa.AssetName;
SELECT o.SceneFloorPlanOccupancyID, o.SceneID, o.FloorPlanID, o.FloorPlanFloorID, fpf.Name AS FloorName,
o.CharacterID, c.CharacterName, c.ShortName AS CharacterShortName, c.ImagePath AS CharacterImagePath, c.ThumbnailPath AS CharacterThumbnailPath,
o.StoryAssetID, sa.AssetName, sa.ImagePath AS AssetImagePath, sa.ThumbnailPath AS AssetThumbnailPath,
o.LocationID, lp.LocationName, COALESCE(NULLIF(lp.LocationPath, N''), lp.LocationName) AS LocationPath,
o.CreatedDate, o.ModifiedDate
FROM dbo.SceneFloorPlanOccupancy o
INNER JOIN dbo.Scenes s ON s.SceneID = o.SceneID
INNER JOIN dbo.Chapters chp ON chp.ChapterID = s.ChapterID
INNER JOIN dbo.Books b ON b.BookID = chp.BookID
INNER JOIN dbo.LocationPaths lp ON lp.LocationID = o.LocationID
LEFT JOIN dbo.FloorPlanFloors fpf ON fpf.FloorPlanFloorID = o.FloorPlanFloorID
LEFT JOIN dbo.Characters c ON c.CharacterID = o.CharacterID
LEFT JOIN dbo.StoryAssets sa ON sa.StoryAssetID = o.StoryAssetID
WHERE b.ProjectID = @ProjectID
ORDER BY b.SortOrder, chp.SortOrder, s.SortOrder, COALESCE(c.CharacterName, sa.AssetName);
SELECT sd.SceneDependencyID, ISNULL(sd.ProjectID, @ProjectID) AS ProjectID, sd.SourceSceneID, sd.TargetSceneID,
sd.SceneDependencyTypeID, sdt.TypeName AS SceneDependencyTypeName, sd.Description,
sd.IsHardDependency, CAST(NULL AS nvarchar(50)) AS DependencyDirection, sb.BookID AS SourceBookID, sb.BookTitle AS SourceBookTitle,
sc.ChapterID AS SourceChapterID, sc.ChapterNumber AS SourceChapterNumber, ss.SceneNumber AS SourceSceneNumber,
ss.SceneTitle AS SourceSceneTitle, tb.BookID AS TargetBookID, tb.BookTitle AS TargetBookTitle,
tc.ChapterID AS TargetChapterID, tc.ChapterNumber AS TargetChapterNumber, ts.SceneNumber AS TargetSceneNumber,
ts.SceneTitle AS TargetSceneTitle, sd.CreatedDate, sd.UpdatedDate, sd.IsArchived
FROM dbo.SceneDependencies sd
INNER JOIN dbo.SceneDependencyTypes sdt ON sdt.SceneDependencyTypeID = sd.SceneDependencyTypeID
INNER JOIN dbo.Scenes ss ON ss.SceneID = sd.SourceSceneID
INNER JOIN dbo.Chapters sc ON sc.ChapterID = ss.ChapterID
INNER JOIN dbo.Books sb ON sb.BookID = sc.BookID
INNER JOIN dbo.Scenes ts ON ts.SceneID = sd.TargetSceneID
INNER JOIN dbo.Chapters tc ON tc.ChapterID = ts.ChapterID
INNER JOIN dbo.Books tb ON tb.BookID = tc.BookID
WHERE sd.ProjectID = @ProjectID OR sb.ProjectID = @ProjectID OR tb.ProjectID = @ProjectID
ORDER BY sb.SortOrder, sc.SortOrder, ss.SortOrder, sd.SceneDependencyID;
SELECT ValidationRunID, ProjectID, BookID, StartedDate, CompletedDate, TriggeredBy, ScopeDescription
FROM dbo.ValidationRuns
WHERE ProjectID = @ProjectID
ORDER BY StartedDate DESC;
SELECT cw.ContinuityWarningID, cw.ValidationRunID, cw.ProjectID, cw.BookID, b.BookTitle, cw.ChapterID,
c.ChapterNumber, c.ChapterTitle, cw.SceneID, s.SceneNumber, s.SceneTitle, cw.EntityType, cw.EntityID,
cw.WarningTypeID, wt.TypeName AS WarningTypeName, cw.WarningSeverityID, ws.SeverityName,
cw.Message, cw.Details, cw.IsDismissed, cw.IsIntentional, cw.CreatedDate, cw.LastDetectedDate
FROM dbo.ContinuityWarnings cw
INNER JOIN dbo.WarningTypes wt ON wt.WarningTypeID = cw.WarningTypeID
INNER JOIN dbo.WarningSeverities ws ON ws.WarningSeverityID = cw.WarningSeverityID
LEFT JOIN dbo.Books b ON b.BookID = cw.BookID
LEFT JOIN dbo.Chapters c ON c.ChapterID = cw.ChapterID
LEFT JOIN dbo.Scenes s ON s.SceneID = cw.SceneID
WHERE cw.ProjectID = @ProjectID
ORDER BY cw.LastDetectedDate DESC, cw.ContinuityWarningID;
SELECT sn.SceneNoteID, sn.SceneID, sn.SceneNoteTypeID, snt.TypeName, sn.NoteTitle, sn.NoteText,
sn.SortOrder, sn.IsPinned, sn.IsResolved, sn.CreatedDate, sn.UpdatedDate
FROM dbo.SceneNotes sn
INNER JOIN dbo.SceneNoteTypes snt ON snt.SceneNoteTypeID = sn.SceneNoteTypeID
INNER JOIN dbo.Scenes s ON s.SceneID = sn.SceneID
INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID
INNER JOIN dbo.Books b ON b.BookID = c.BookID
WHERE b.ProjectID = @ProjectID
ORDER BY sn.SceneID, sn.SortOrder, sn.SceneNoteID;
SELECT sw.SceneWorkflowID, sw.SceneID, sw.DraftStatus, sw.EstimatedWordCount, sw.ActualWordCount,
sw.DraftedDate, sw.LastWorkedOn, sw.ReadyForDraft, sw.ReadyForRevision, sw.ReadyForPolish,
sw.IsBlocked, sw.BlockedReason, sw.Priority, sw.CreatedDate, sw.UpdatedDate
FROM dbo.SceneWorkflow sw
INNER JOIN dbo.Scenes s ON s.SceneID = sw.SceneID
INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID
INNER JOIN dbo.Books b ON b.BookID = c.BookID
WHERE b.ProjectID = @ProjectID
ORDER BY sw.SceneID;
SELECT sci.SceneChecklistItemID, sci.SceneID, sci.ItemText, sci.IsCompleted, sci.SortOrder,
sci.CreatedDate, sci.UpdatedDate
FROM dbo.SceneChecklistItems sci
INNER JOIN dbo.Scenes s ON s.SceneID = sci.SceneID
INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID
INNER JOIN dbo.Books b ON b.BookID = c.BookID
WHERE b.ProjectID = @ProjectID
ORDER BY sci.SceneID, sci.SortOrder, sci.SceneChecklistItemID;
SELECT sa.SceneAttachmentID, sa.SceneID, sa.SceneAttachmentTypeID, sat.TypeName, sa.Title,
sa.FilePath, sa.ExternalUrl, sa.Notes, sa.CreatedDate, sa.UpdatedDate
FROM dbo.SceneAttachments sa
INNER JOIN dbo.SceneAttachmentTypes sat ON sat.SceneAttachmentTypeID = sa.SceneAttachmentTypeID
INNER JOIN dbo.Scenes s ON s.SceneID = sa.SceneID
INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID
INNER JOIN dbo.Books b ON b.BookID = c.BookID
WHERE b.ProjectID = @ProjectID
ORDER BY sa.SceneID, sa.SceneAttachmentID;
SELECT cg.ChapterGoalID, cg.ChapterID, cg.GoalSummary, cg.EmotionalGoal, cg.ReaderTakeaway,
cg.MustInclude, cg.RisksOrConcerns, cg.CreatedDate, cg.UpdatedDate
FROM dbo.ChapterGoals cg
INNER JOIN dbo.Chapters c ON c.ChapterID = cg.ChapterID
INNER JOIN dbo.Books b ON b.BookID = c.BookID
WHERE b.ProjectID = @ProjectID
ORDER BY b.SortOrder, c.SortOrder;
SELECT sc.ScenarioID, sc.ProjectID, sc.BookID, sc.ScenarioName, sc.Description, sc.BaseCreatedDate,
sc.IsAppliedToMain, sc.IsArchived, sc.ArchivedDate, sc.ArchivedReason,
sc.CreatedDate, sc.UpdatedDate, p.ProjectName, b.BookTitle,
(SELECT COUNT(1) FROM dbo.ScenarioSceneOrder sso WHERE sso.ScenarioID = sc.ScenarioID) AS SceneCount,
(SELECT COUNT(1) FROM dbo.ScenarioWarnings sw WHERE sw.ScenarioID = sc.ScenarioID) AS WarningCount
FROM dbo.Scenarios sc
INNER JOIN dbo.Projects p ON p.ProjectID = sc.ProjectID
LEFT JOIN dbo.Books b ON b.BookID = sc.BookID
WHERE sc.ProjectID = @ProjectID
ORDER BY sc.IsArchived, sc.UpdatedDate DESC;
SELECT sso.ScenarioSceneOrderID, sso.ScenarioID, sso.SceneID, sso.ProposedChapterID, sso.ProposedSortOrder,
sso.Notes, sso.CreatedDate, sso.UpdatedDate
FROM dbo.ScenarioSceneOrder sso
INNER JOIN dbo.Scenarios sc ON sc.ScenarioID = sso.ScenarioID
WHERE sc.ProjectID = @ProjectID
ORDER BY sso.ScenarioID, sso.ProposedSortOrder;
SELECT svr.ScenarioValidationRunID, svr.ScenarioID, svr.StartedDate, svr.CompletedDate, svr.WarningCount
FROM dbo.ScenarioValidationRuns svr
INNER JOIN dbo.Scenarios sc ON sc.ScenarioID = svr.ScenarioID
WHERE sc.ProjectID = @ProjectID
ORDER BY svr.StartedDate DESC;
SELECT sw.ScenarioWarningID, sw.ScenarioValidationRunID, sw.ScenarioID, sw.SceneID, sw.EntityType,
sw.EntityID, sw.WarningTypeName, sw.SeverityName, sw.Message, sw.Details, sw.CreatedDate
FROM dbo.ScenarioWarnings sw
INNER JOIN dbo.Scenarios sc ON sc.ScenarioID = sw.ScenarioID
WHERE sc.ProjectID = @ProjectID
ORDER BY sw.CreatedDate DESC, sw.ScenarioWarningID;
SELECT GenreMetricPresetID, PresetKey, PresetName, Description, SortOrder, IsActive
FROM dbo.GenreMetricPresets
ORDER BY SortOrder, PresetName;
SELECT gpmm.GenreMetricPresetID, gmp.PresetKey, gmp.PresetName,
gpmm.MetricTypeID, mt.MetricName, gpmm.SortOrder
FROM dbo.GenreMetricPresetMetrics gpmm
INNER JOIN dbo.GenreMetricPresets gmp ON gmp.GenreMetricPresetID = gpmm.GenreMetricPresetID
INNER JOIN dbo.SceneMetricTypes mt ON mt.MetricTypeID = gpmm.MetricTypeID
WHERE EXISTS
(
SELECT 1
FROM dbo.SceneMetricTypes projectMetric
WHERE projectMetric.MetricTypeID = gpmm.MetricTypeID
AND
(
projectMetric.ProjectID IS NULL
OR projectMetric.ProjectID = @ProjectID
OR EXISTS
(
SELECT 1
FROM dbo.ProjectTimelineMetricSettings ptms
WHERE ptms.ProjectID = @ProjectID
AND ptms.MetricTypeID = projectMetric.MetricTypeID
)
)
)
ORDER BY gmp.SortOrder, gpmm.SortOrder, mt.MetricName;
SELECT pua.ProjectUserAccessID, pua.ProjectID, pua.UserID, u.Email, u.DisplayName,
pua.AccessRole, pua.InvitedByUserID, invitedBy.Email AS InvitedByEmail,
pua.InvitedDateUTC, pua.AcceptedDateUTC, pua.IsActive
FROM dbo.ProjectUserAccess pua
INNER JOIN dbo.AppUser u ON u.UserID = pua.UserID
LEFT JOIN dbo.AppUser invitedBy ON invitedBy.UserID = pua.InvitedByUserID
WHERE pua.ProjectID = @ProjectID
AND pua.IsActive = 1
ORDER BY CASE WHEN pua.AccessRole = N'Owner' THEN 0 ELSE 1 END, u.Email;
SELECT UserFileID, UserID, ProjectID, EntityType, EntityID, FileName, OriginalFileName, ContentType,
FileSizeBytes, StoragePath, UploadedDateUTC, DeletedDateUTC, IsDeleted
FROM dbo.UserFile
WHERE ProjectID = @ProjectID
ORDER BY IsDeleted, UploadedDateUTC, UserFileID;
END;
GO
CREATE OR ALTER PROCEDURE dbo.ProjectRestore_ApplyFromJson
@ProjectID int,
@SourceRestorePointID int = NULL,
@SafetyRestorePointID int = NULL,
@JsonData nvarchar(max)
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT ON;
IF ISJSON(@JsonData) <> 1
THROW 51001, 'Restore JSON is invalid.', 1;
IF JSON_VALUE(@JsonData, '$.Format') <> N'PlotDirector.ProjectBackup'
THROW 51002, 'Restore JSON format is not recognised.', 1;
IF JSON_VALUE(@JsonData, '$.FormatVersion') NOT IN (N'1.0', N'1.1')
THROW 51003, 'Restore JSON format version is not supported.', 1;
IF TRY_CONVERT(int, JSON_VALUE(@JsonData, '$.Project.ProjectID')) <> @ProjectID
THROW 51004, 'Restore point does not belong to this project.', 1;
DECLARE @RestoreTables table (TableName sysname NOT NULL PRIMARY KEY);
INSERT INTO @RestoreTables (TableName)
VALUES
(N'ProjectTimelineSettings'), (N'ProjectTimelineMetricSettings'), (N'TimelineViewPresets'),
(N'SceneMetricTypes'), (N'SceneMetricValues'), (N'Books'), (N'Chapters'), (N'Scenes'),
(N'ScenePurposes'), (N'SceneOutcomes'), (N'PlotLines'), (N'PlotThreads'), (N'ThreadEvents'),
(N'AssetStates'), (N'StoryAssets'), (N'AssetEvents'), (N'AssetDependencies'),
(N'AssetCustodyEvents'), (N'AssetCustodyEventCharacters'), (N'Characters'), (N'SceneCharacters'),
(N'CharacterAttributeTypes'), (N'CharacterAttributeEvents'), (N'CharacterKnowledge'),
(N'CharacterRelationships'), (N'RelationshipEvents'), (N'Locations'), (N'LocationRelationships'),
(N'FloorPlans'), (N'FloorPlanFloors'), (N'FloorPlanBlocks'), (N'FloorPlanTransitions'),
(N'SceneAssetLocations'), (N'SceneFloorPlanOccupancy'), (N'SceneDependencies'), (N'ValidationRuns'), (N'ContinuityWarnings'),
(N'SceneNotes'), (N'SceneWorkflow'), (N'SceneChecklistItems'), (N'SceneAttachments'),
(N'ChapterGoals'), (N'Scenarios'), (N'ScenarioSceneOrder'), (N'ScenarioValidationRuns'),
(N'ScenarioWarnings');
BEGIN TRANSACTION;
DECLARE @TableName sysname;
DECLARE DisableCursor CURSOR LOCAL FAST_FORWARD FOR SELECT TableName FROM @RestoreTables;
OPEN DisableCursor;
FETCH NEXT FROM DisableCursor INTO @TableName;
WHILE @@FETCH_STATUS = 0
BEGIN
DECLARE @DisableSql nvarchar(max) = N'ALTER TABLE dbo.' + QUOTENAME(@TableName) + N' NOCHECK CONSTRAINT ALL;';
EXEC (@DisableSql);
FETCH NEXT FROM DisableCursor INTO @TableName;
END
CLOSE DisableCursor;
DEALLOCATE DisableCursor;
DELETE sw FROM dbo.ScenarioWarnings sw INNER JOIN dbo.Scenarios sc ON sc.ScenarioID = sw.ScenarioID WHERE sc.ProjectID = @ProjectID;
DELETE svr FROM dbo.ScenarioValidationRuns svr INNER JOIN dbo.Scenarios sc ON sc.ScenarioID = svr.ScenarioID WHERE sc.ProjectID = @ProjectID;
DELETE sso FROM dbo.ScenarioSceneOrder sso INNER JOIN dbo.Scenarios sc ON sc.ScenarioID = sso.ScenarioID WHERE sc.ProjectID = @ProjectID;
DELETE FROM dbo.Scenarios WHERE ProjectID = @ProjectID;
DELETE FROM dbo.ContinuityWarnings WHERE ProjectID = @ProjectID;
DELETE FROM dbo.ValidationRuns WHERE ProjectID = @ProjectID;
DELETE sa FROM dbo.SceneAttachments sa INNER JOIN dbo.Scenes s ON s.SceneID = sa.SceneID INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID INNER JOIN dbo.Books b ON b.BookID = c.BookID WHERE b.ProjectID = @ProjectID;
DELETE sci FROM dbo.SceneChecklistItems sci INNER JOIN dbo.Scenes s ON s.SceneID = sci.SceneID INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID INNER JOIN dbo.Books b ON b.BookID = c.BookID WHERE b.ProjectID = @ProjectID;
DELETE sw FROM dbo.SceneWorkflow sw INNER JOIN dbo.Scenes s ON s.SceneID = sw.SceneID INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID INNER JOIN dbo.Books b ON b.BookID = c.BookID WHERE b.ProjectID = @ProjectID;
DELETE sn FROM dbo.SceneNotes sn INNER JOIN dbo.Scenes s ON s.SceneID = sn.SceneID INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID INNER JOIN dbo.Books b ON b.BookID = c.BookID WHERE b.ProjectID = @ProjectID;
DELETE cg FROM dbo.ChapterGoals cg INNER JOIN dbo.Chapters c ON c.ChapterID = cg.ChapterID INNER JOIN dbo.Books b ON b.BookID = c.BookID WHERE b.ProjectID = @ProjectID;
DELETE sfpo FROM dbo.SceneFloorPlanOccupancy sfpo
INNER JOIN dbo.Scenes s ON s.SceneID = sfpo.SceneID
INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID
INNER JOIN dbo.Books b ON b.BookID = c.BookID
WHERE b.ProjectID = @ProjectID;
DELETE sd FROM dbo.SceneDependencies sd
LEFT JOIN dbo.Scenes ss ON ss.SceneID = sd.SourceSceneID
LEFT JOIN dbo.Chapters sc ON sc.ChapterID = ss.ChapterID
LEFT JOIN dbo.Books sb ON sb.BookID = sc.BookID
LEFT JOIN dbo.Scenes ts ON ts.SceneID = sd.TargetSceneID
LEFT JOIN dbo.Chapters tc ON tc.ChapterID = ts.ChapterID
LEFT JOIN dbo.Books tb ON tb.BookID = tc.BookID
WHERE sd.ProjectID = @ProjectID OR sb.ProjectID = @ProjectID OR tb.ProjectID = @ProjectID;
DELETE re FROM dbo.RelationshipEvents re INNER JOIN dbo.CharacterRelationships cr ON cr.CharacterRelationshipID = re.CharacterRelationshipID WHERE cr.ProjectID = @ProjectID;
DELETE FROM dbo.CharacterRelationships WHERE ProjectID = @ProjectID;
DELETE ck FROM dbo.CharacterKnowledge ck INNER JOIN dbo.Characters ch ON ch.CharacterID = ck.CharacterID WHERE ch.ProjectID = @ProjectID;
DELETE cae FROM dbo.CharacterAttributeEvents cae INNER JOIN dbo.Characters ch ON ch.CharacterID = cae.CharacterID WHERE ch.ProjectID = @ProjectID;
DELETE sc FROM dbo.SceneCharacters sc INNER JOIN dbo.Characters ch ON ch.CharacterID = sc.CharacterID WHERE ch.ProjectID = @ProjectID;
DELETE acec FROM dbo.AssetCustodyEventCharacters acec INNER JOIN dbo.AssetCustodyEvents ace ON ace.AssetCustodyEventID = acec.AssetCustodyEventID INNER JOIN dbo.StoryAssets sa ON sa.StoryAssetID = ace.StoryAssetID WHERE sa.ProjectID = @ProjectID;
DELETE ace FROM dbo.AssetCustodyEvents ace INNER JOIN dbo.StoryAssets sa ON sa.StoryAssetID = ace.StoryAssetID WHERE sa.ProjectID = @ProjectID;
DELETE ad FROM dbo.AssetDependencies ad INNER JOIN dbo.StoryAssets sa ON sa.StoryAssetID = ad.SourceAssetID WHERE sa.ProjectID = @ProjectID;
DELETE ae FROM dbo.AssetEvents ae INNER JOIN dbo.StoryAssets sa ON sa.StoryAssetID = ae.StoryAssetID WHERE sa.ProjectID = @ProjectID;
DELETE sal FROM dbo.SceneAssetLocations sal INNER JOIN dbo.StoryAssets sa ON sa.StoryAssetID = sal.StoryAssetID WHERE sa.ProjectID = @ProjectID;
DELETE FROM dbo.StoryAssets WHERE ProjectID = @ProjectID;
DELETE fpt FROM dbo.FloorPlanTransitions fpt INNER JOIN dbo.FloorPlanFloors fpf ON fpf.FloorPlanFloorID = fpt.FloorPlanFloorID INNER JOIN dbo.FloorPlans fp ON fp.FloorPlanID = fpf.FloorPlanID WHERE fp.ProjectID = @ProjectID;
DELETE fpb FROM dbo.FloorPlanBlocks fpb INNER JOIN dbo.FloorPlanFloors fpf ON fpf.FloorPlanFloorID = fpb.FloorPlanFloorID INNER JOIN dbo.FloorPlans fp ON fp.FloorPlanID = fpf.FloorPlanID WHERE fp.ProjectID = @ProjectID;
DELETE fpf FROM dbo.FloorPlanFloors fpf INNER JOIN dbo.FloorPlans fp ON fp.FloorPlanID = fpf.FloorPlanID WHERE fp.ProjectID = @ProjectID;
DELETE FROM dbo.FloorPlans WHERE ProjectID = @ProjectID;
DELETE lr FROM dbo.LocationRelationships lr INNER JOIN dbo.Locations l ON l.LocationID = lr.FromLocationID WHERE l.ProjectID = @ProjectID;
DELETE FROM dbo.Locations WHERE ProjectID = @ProjectID;
DELETE te FROM dbo.ThreadEvents te INNER JOIN dbo.PlotThreads pt ON pt.PlotThreadID = te.PlotThreadID INNER JOIN dbo.PlotLines pl ON pl.PlotLineID = pt.PlotLineID WHERE pl.ProjectID = @ProjectID;
DELETE pt FROM dbo.PlotThreads pt INNER JOIN dbo.PlotLines pl ON pl.PlotLineID = pt.PlotLineID WHERE pl.ProjectID = @ProjectID;
DELETE FROM dbo.PlotLines WHERE ProjectID = @ProjectID;
DELETE FROM dbo.TimelineViewPresets WHERE ProjectID = @ProjectID;
DELETE FROM dbo.ProjectTimelineMetricSettings WHERE ProjectID = @ProjectID;
DELETE FROM dbo.ProjectTimelineSettings WHERE ProjectID = @ProjectID;
DELETE smv FROM dbo.SceneMetricValues smv INNER JOIN dbo.Scenes s ON s.SceneID = smv.SceneID INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID INNER JOIN dbo.Books b ON b.BookID = c.BookID WHERE b.ProjectID = @ProjectID;
DELETE FROM dbo.SceneMetricTypes WHERE ProjectID = @ProjectID;
DELETE FROM dbo.CharacterAttributeTypes WHERE ProjectID = @ProjectID;
DELETE FROM dbo.AssetStates WHERE ProjectID = @ProjectID;
DELETE sp FROM dbo.ScenePurposes sp INNER JOIN dbo.Scenes s ON s.SceneID = sp.SceneID INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID INNER JOIN dbo.Books b ON b.BookID = c.BookID WHERE b.ProjectID = @ProjectID;
DELETE so FROM dbo.SceneOutcomes so INNER JOIN dbo.Scenes s ON s.SceneID = so.SceneID INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID INNER JOIN dbo.Books b ON b.BookID = c.BookID WHERE b.ProjectID = @ProjectID;
DELETE s FROM dbo.Scenes s INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID INNER JOIN dbo.Books b ON b.BookID = c.BookID WHERE b.ProjectID = @ProjectID;
DELETE c FROM dbo.Chapters c INNER JOIN dbo.Books b ON b.BookID = c.BookID WHERE b.ProjectID = @ProjectID;
DELETE FROM dbo.Books WHERE ProjectID = @ProjectID;
DELETE FROM dbo.Characters WHERE ProjectID = @ProjectID;
UPDATE p
SET ProjectName = src.ProjectName,
Description = src.Description,
CreatedDate = src.CreatedDate,
UpdatedDate = SYSUTCDATETIME(),
IsArchived = src.IsArchived,
ArchivedDate = src.ArchivedDate,
ArchivedReason = src.ArchivedReason
FROM dbo.Projects p
CROSS APPLY OPENJSON(@JsonData, '$.Project')
WITH
(
ProjectID int '$.ProjectID',
ProjectName nvarchar(200) '$.ProjectName',
Description nvarchar(max) '$.Description',
CreatedDate datetime2 '$.CreatedDate',
IsArchived bit '$.IsArchived',
ArchivedDate datetime2 '$.ArchivedDate',
ArchivedReason nvarchar(1000) '$.ArchivedReason'
) src
WHERE p.ProjectID = @ProjectID AND src.ProjectID = @ProjectID;
EXEC dbo.ProjectRestore_InsertJsonTable N'SceneMetricTypes', N'$.Data.SceneMetricTypes', @JsonData, @ProjectID, 1;
EXEC dbo.ProjectRestore_InsertJsonTable N'AssetStates', N'$.Data.AssetStates', @JsonData, @ProjectID, 1;
EXEC dbo.ProjectRestore_InsertJsonTable N'CharacterAttributeTypes', N'$.Data.CharacterAttributeTypes', @JsonData, @ProjectID, 1;
EXEC dbo.ProjectRestore_InsertJsonTable N'Books', N'$.Data.Books', @JsonData, @ProjectID, 0;
EXEC dbo.ProjectRestore_InsertJsonTable N'Chapters', N'$.Data.Chapters', @JsonData, @ProjectID, 0;
EXEC dbo.ProjectRestore_InsertJsonTable N'Characters', N'$.Data.Characters', @JsonData, @ProjectID, 0;
EXEC dbo.ProjectRestore_InsertJsonTable N'Locations', N'$.Data.Locations', @JsonData, @ProjectID, 0;
EXEC dbo.ProjectRestore_InsertJsonTable N'FloorPlans', N'$.Data.FloorPlans', @JsonData, @ProjectID, 0;
EXEC dbo.ProjectRestore_InsertJsonTable N'FloorPlanFloors', N'$.Data.FloorPlanFloors', @JsonData, @ProjectID, 0;
EXEC dbo.ProjectRestore_InsertJsonTable N'FloorPlanBlocks', N'$.Data.FloorPlanBlocks', @JsonData, @ProjectID, 0;
EXEC dbo.ProjectRestore_InsertJsonTable N'FloorPlanTransitions', N'$.Data.FloorPlanTransitions', @JsonData, @ProjectID, 0;
EXEC dbo.ProjectRestore_InsertJsonTable N'Scenes', N'$.Data.Scenes', @JsonData, @ProjectID, 0;
EXEC dbo.ProjectRestore_InsertJsonTable N'ScenePurposes', N'$.Data.ScenePurposes', @JsonData, @ProjectID, 0;
EXEC dbo.ProjectRestore_InsertJsonTable N'SceneOutcomes', N'$.Data.SceneOutcomes', @JsonData, @ProjectID, 0;
EXEC dbo.ProjectRestore_InsertJsonTable N'SceneMetricValues', N'$.Data.SceneMetricValues', @JsonData, @ProjectID, 0;
EXEC dbo.ProjectRestore_InsertJsonTable N'ProjectTimelineSettings', N'$.Data.TimelineSettings', @JsonData, @ProjectID, 0;
EXEC dbo.ProjectRestore_InsertJsonTable N'ProjectTimelineMetricSettings', N'$.Data.TimelineMetricSettings', @JsonData, @ProjectID, 0;
EXEC dbo.ProjectRestore_InsertJsonTable N'TimelineViewPresets', N'$.Data.TimelineViewPresets', @JsonData, @ProjectID, 0;
EXEC dbo.ProjectRestore_InsertJsonTable N'PlotLines', N'$.Data.PlotLines', @JsonData, @ProjectID, 0;
EXEC dbo.ProjectRestore_InsertJsonTable N'PlotThreads', N'$.Data.PlotThreads', @JsonData, @ProjectID, 0;
EXEC dbo.ProjectRestore_InsertJsonTable N'ThreadEvents', N'$.Data.ThreadEvents', @JsonData, @ProjectID, 0;
EXEC dbo.ProjectRestore_InsertJsonTable N'StoryAssets', N'$.Data.StoryAssets', @JsonData, @ProjectID, 0;
EXEC dbo.ProjectRestore_InsertJsonTable N'AssetEvents', N'$.Data.AssetEvents', @JsonData, @ProjectID, 0;
EXEC dbo.ProjectRestore_InsertJsonTable N'AssetDependencies', N'$.Data.AssetDependencies', @JsonData, @ProjectID, 0;
EXEC dbo.ProjectRestore_InsertJsonTable N'AssetCustodyEvents', N'$.Data.AssetCustodyEvents', @JsonData, @ProjectID, 0;
EXEC dbo.ProjectRestore_InsertJsonTable N'AssetCustodyEventCharacters', N'$.Data.AssetCustodyCharacters', @JsonData, @ProjectID, 0;
EXEC dbo.ProjectRestore_InsertJsonTable N'SceneCharacters', N'$.Data.SceneCharacters', @JsonData, @ProjectID, 0;
EXEC dbo.ProjectRestore_InsertJsonTable N'CharacterAttributeEvents', N'$.Data.CharacterAttributeEvents', @JsonData, @ProjectID, 0;
EXEC dbo.ProjectRestore_InsertJsonTable N'CharacterKnowledge', N'$.Data.CharacterKnowledge', @JsonData, @ProjectID, 0;
EXEC dbo.ProjectRestore_InsertJsonTable N'CharacterRelationships', N'$.Data.CharacterRelationships', @JsonData, @ProjectID, 0;
EXEC dbo.ProjectRestore_InsertJsonTable N'RelationshipEvents', N'$.Data.RelationshipEvents', @JsonData, @ProjectID, 0;
EXEC dbo.ProjectRestore_InsertJsonTable N'LocationRelationships', N'$.Data.LocationRelationships', @JsonData, @ProjectID, 0;
EXEC dbo.ProjectRestore_InsertJsonTable N'SceneAssetLocations', N'$.Data.SceneAssetLocations', @JsonData, @ProjectID, 0;
EXEC dbo.ProjectRestore_InsertJsonTable N'SceneFloorPlanOccupancy', N'$.Data.SceneFloorPlanOccupancy', @JsonData, @ProjectID, 0;
EXEC dbo.ProjectRestore_InsertJsonTable N'SceneDependencies', N'$.Data.SceneDependencies', @JsonData, @ProjectID, 0;
EXEC dbo.ProjectRestore_InsertJsonTable N'ValidationRuns', N'$.Data.ValidationRuns', @JsonData, @ProjectID, 0;
EXEC dbo.ProjectRestore_InsertJsonTable N'ContinuityWarnings', N'$.Data.ContinuityWarnings', @JsonData, @ProjectID, 0;
EXEC dbo.ProjectRestore_InsertJsonTable N'SceneNotes', N'$.Data.SceneNotes', @JsonData, @ProjectID, 0;
EXEC dbo.ProjectRestore_InsertJsonTable N'SceneWorkflow', N'$.Data.SceneWorkflow', @JsonData, @ProjectID, 0;
EXEC dbo.ProjectRestore_InsertJsonTable N'SceneChecklistItems', N'$.Data.SceneChecklistItems', @JsonData, @ProjectID, 0;
EXEC dbo.ProjectRestore_InsertJsonTable N'SceneAttachments', N'$.Data.SceneAttachments', @JsonData, @ProjectID, 0;
EXEC dbo.ProjectRestore_InsertJsonTable N'ChapterGoals', N'$.Data.ChapterGoals', @JsonData, @ProjectID, 0;
EXEC dbo.ProjectRestore_InsertJsonTable N'Scenarios', N'$.Data.Scenarios', @JsonData, @ProjectID, 0;
EXEC dbo.ProjectRestore_InsertJsonTable N'ScenarioSceneOrder', N'$.Data.ScenarioSceneOrder', @JsonData, @ProjectID, 0;
EXEC dbo.ProjectRestore_InsertJsonTable N'ScenarioValidationRuns', N'$.Data.ScenarioValidationRuns', @JsonData, @ProjectID, 0;
EXEC dbo.ProjectRestore_InsertJsonTable N'ScenarioWarnings', N'$.Data.ScenarioWarnings', @JsonData, @ProjectID, 0;
DECLARE EnableCursor CURSOR LOCAL FAST_FORWARD FOR SELECT TableName FROM @RestoreTables;
OPEN EnableCursor;
FETCH NEXT FROM EnableCursor INTO @TableName;
WHILE @@FETCH_STATUS = 0
BEGIN
DECLARE @EnableSql nvarchar(max) = N'ALTER TABLE dbo.' + QUOTENAME(@TableName) + N' WITH CHECK CHECK CONSTRAINT ALL;';
EXEC (@EnableSql);
FETCH NEXT FROM EnableCursor INTO @TableName;
END
CLOSE EnableCursor;
DEALLOCATE EnableCursor;
IF @SourceRestorePointID IS NOT NULL AND @SafetyRestorePointID IS NOT NULL
BEGIN
INSERT INTO dbo.ProjectRestoreHistory (ProjectID, SourceRestorePointID, SafetyRestorePointID, PerformedBy, Notes)
VALUES (@ProjectID, @SourceRestorePointID, @SafetyRestorePointID, SUSER_SNAME(), N'Project restored from saved restore point.');
END;
COMMIT TRANSACTION;
END;
GO
CREATE OR ALTER PROCEDURE dbo.ProjectBackup_GetRelationshipImportLookups
AS
BEGIN
SET NOCOUNT ON;
SELECT RevisionStatusID AS LookupID, StatusName AS LookupName FROM dbo.RevisionStatuses ORDER BY SortOrder, StatusName;
SELECT TimeModeID AS LookupID, TimeModeName AS LookupName FROM dbo.TimeModes ORDER BY SortOrder, TimeModeName;
SELECT DurationUnitID AS LookupID, DurationUnitName AS LookupName FROM dbo.DurationUnits ORDER BY SortOrder, DurationUnitName;
SELECT TimeConfidenceID AS LookupID, TimeConfidenceName AS LookupName FROM dbo.TimeConfidences ORDER BY SortOrder, TimeConfidenceName;
SELECT LocationTypeID AS LookupID, TypeName AS LookupName FROM dbo.LocationTypes WHERE IsActive = 1 ORDER BY SortOrder, TypeName;
SELECT LocationRelationshipTypeID AS LookupID, TypeName AS LookupName FROM dbo.LocationRelationshipTypes WHERE IsActive = 1 ORDER BY SortOrder, TypeName;
SELECT PlotLineTypeID AS LookupID, TypeName AS LookupName FROM dbo.PlotLineTypes WHERE IsActive = 1 ORDER BY SortOrder, TypeName;
SELECT PlotImportanceID AS LookupID, ImportanceName AS LookupName FROM dbo.PlotImportance WHERE IsActive = 1 ORDER BY SortOrder, ImportanceName;
SELECT ThreadTypeID AS LookupID, TypeName AS LookupName FROM dbo.ThreadTypes WHERE IsActive = 1 ORDER BY SortOrder, TypeName;
SELECT ThreadStatusID AS LookupID, StatusName AS LookupName FROM dbo.ThreadStatuses WHERE IsActive = 1 ORDER BY SortOrder, StatusName;
SELECT ThreadEventTypeID AS LookupID, TypeName AS LookupName FROM dbo.ThreadEventTypes WHERE IsActive = 1 ORDER BY SortOrder, TypeName;
SELECT PlotEventTypeID AS LookupID, TypeName AS LookupName FROM dbo.PlotEventTypes WHERE IsActive = 1 ORDER BY SortOrder, TypeName;
SELECT AssetKindID AS LookupID, KindName AS LookupName FROM dbo.AssetKinds WHERE IsActive = 1 ORDER BY SortOrder, KindName;
SELECT AssetStateID AS LookupID, StateName AS LookupName FROM dbo.AssetStates WHERE IsActive = 1 AND ProjectID IS NULL ORDER BY SortOrder, StateName;
SELECT AssetEventTypeID AS LookupID, TypeName AS LookupName FROM dbo.AssetEventTypes WHERE IsActive = 1 ORDER BY SortOrder, TypeName;
SELECT AssetDependencyTypeID AS LookupID, TypeName AS LookupName FROM dbo.AssetDependencyTypes WHERE IsActive = 1 ORDER BY SortOrder, TypeName;
SELECT AssetCustodyEventTypeID AS LookupID, TypeName AS LookupName FROM dbo.AssetCustodyEventTypes WHERE IsActive = 1 ORDER BY SortOrder, TypeName;
SELECT CustodyRoleID AS LookupID, RoleName AS LookupName FROM dbo.CustodyRoles WHERE IsActive = 1 ORDER BY SortOrder, RoleName;
SELECT CharacterRoleInSceneTypeID AS LookupID, TypeName AS LookupName FROM dbo.CharacterRoleInSceneTypes WHERE IsActive = 1 ORDER BY SortOrder, TypeName;
SELECT PresenceTypeID AS LookupID, TypeName AS LookupName FROM dbo.PresenceTypes WHERE IsActive = 1 ORDER BY SortOrder, TypeName;
SELECT KnowledgeStateID AS LookupID, StateName AS LookupName FROM dbo.KnowledgeStates WHERE IsActive = 1 ORDER BY SortOrder, StateName;
SELECT SceneDependencyTypeID AS LookupID, TypeName AS LookupName FROM dbo.SceneDependencyTypes WHERE IsActive = 1 ORDER BY SortOrder, TypeName;
SELECT SceneNoteTypeID AS LookupID, TypeName AS LookupName FROM dbo.SceneNoteTypes WHERE IsActive = 1 ORDER BY SortOrder, TypeName;
SELECT SceneAttachmentTypeID AS LookupID, TypeName AS LookupName FROM dbo.SceneAttachmentTypes WHERE IsActive = 1 ORDER BY SortOrder, TypeName;
SELECT ScenePurposeTypeID AS LookupID, PurposeName AS LookupName FROM dbo.ScenePurposeTypes ORDER BY SortOrder, PurposeName;
SELECT MetricTypeID AS LookupID, MetricName AS LookupName FROM dbo.SceneMetricTypes WHERE ProjectID IS NULL ORDER BY SortOrder, MetricName;
SELECT rt.RelationshipTypeID, rt.TypeName, rt.IsPermanentDefault, rt.SortOrder, rt.IsActive,
rt.RelationshipCategoryID, rc.CategoryName AS RelationshipCategoryName, COALESCE(rc.SortOrder, 900) AS CategorySortOrder
FROM dbo.RelationshipTypes rt
LEFT JOIN dbo.RelationshipCategories rc ON rc.RelationshipCategoryID = rt.RelationshipCategoryID
WHERE rt.IsActive = 1
ORDER BY COALESCE(rc.SortOrder, 900), rt.SortOrder, rt.TypeName;
SELECT RelationshipStateID, StateName, SortOrder, IsActive
FROM dbo.RelationshipStates
WHERE IsActive = 1
ORDER BY SortOrder, StateName;
END;
GO