Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,28 @@

All notable changes to PaperRoute Tracker will be documented here.

## [Unreleased] — v0.7 Deadline Center

v0.7.0-rc.1 is available on the Preview channel. Development is tracked in [PR #78](https://github.com/JUhalt/PaperRoute-Tracker/pull/78) and the [v0.7 milestone](https://github.com/JUhalt/PaperRoute-Tracker/milestone/9).

### Added

- The **Deadlines** page replaces Reminders ([#28](https://github.com/JUhalt/PaperRoute-Tracker/issues/28)): one list of what needs action, grouped as Overdue, Today, Next 7 days, Later, No date, and Done (folded away). Counts across the top; chips for Revisions, Follow-ups, Reminders, and Preparation; and a filter box. Revisions show progress from the reviewer comments on their decision. Work without a date stays visible: a revision with no deadline, and an unsubmitted packet whose checklist has required items open.
- Each row acts on the record that owns it: **Open** lands on the submission (at its reviewer responses for a revision), **Postpone...** offers tomorrow, a week, two weeks, or any date, **Done** completes a reminder, **Clear Follow-up...** removes only the follow-up date, and **Open Readiness** opens Readiness & Packets. Enter runs a row's first action. Nothing is copied, so Deadlines and the manuscript page always agree.
- The rail's Deadlines badge counts what is overdue or due today, and **View in Deadlines →** sits beside Needs Attention on the Board. **Ctrl+4** opens Deadlines.
- **Check for Publications...** ([#61](https://github.com/JUhalt/PaperRoute-Tracker/issues/61)) asks Crossref, only when you choose, whether manuscripts you are tracking have been published: by DOI, by a preprint's link to its published version, by title, and optionally in an ORCID record. Each possible match offers **Review Match**, **Mark Published...**, and **Ignore**; unreviewed matches wait on the Deadlines page. From Import & Export, or a manuscript page's **⋯** menu.
- **Mark Published...** lists exactly what it will change, then records a normal lifecycle event: the Published shelf and stage from the publication date, a history entry with the journal and DOI, and empty publication fields filled. Submissions and decisions are left as recorded.
- **Fill Blanks from Crossref...** fills empty publication fields for manuscripts with a DOI after a preview of every change. It never replaces a value.

### Storage

- **Schema 7** remembers publication matches, so an ignored match stays ignored. The upgrade validates the library and changes only the schema marker; manuscript data, managed files, and backups are left byte-for-byte. v0.6 cannot open a library after v0.7 has used it; keep a backup made before upgrading.

### Fixed

- Revision deadlines now name their journal on the Deadlines page and in calendar exports.
- The **First deadline** date in Add Manuscript shows in full at 150% display scaling ([#76](https://github.com/JUhalt/PaperRoute-Tracker/issues/76)).

## [0.6.0] - 2026-09-27

PaperRoute v0.6.0 — **Workspace** — was released on September 27, 2026 from [PR #69](https://github.com/JUhalt/PaperRoute-Tracker/pull/69), after the v0.6.0-rc.1 Preview. Certification is recorded in [#74](https://github.com/JUhalt/PaperRoute-Tracker/issues/74). v0.6 uses the same Schema 6 library as v0.5 and migrates no data.
Expand Down
29 changes: 29 additions & 0 deletions ManualCertification/V04Demo/BoardDemo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,35 @@ internal static void CreateSamples(string sessionRoot, bool empty = false)
"We explain why the preregistered analysis is retained.", "Study 2")
});

// Deadlines: follow-ups, reminders (one done), and an unsubmitted
// packet whose checklist is still open.
var submitted = manuscripts[1];
submitted.Submissions.Single().FollowUpDate = today.AddDays(24);
var underReview = manuscripts[2];
underReview.Submissions.Last().FollowUpDate = today.AddDays(-4);
underReview.PublicationMatches.Add(new PublicationMatch
{
Doi = "10.5555/demo.attention", Title = "Attention capture by salient distractors under working-memory load",
Journal = "Fictional Journal of Perception & Performance", PublishedDate = today.AddDays(-2), Source = PublicationMatchSource.Title
});
var draft = manuscripts[4];
draft.Reminders.Add(new ManuscriptReminder { Title = "Send the revised draft to coauthors", DueDate = today });
draft.Reminders.Add(new ManuscriptReminder { Title = "Ask the librarian about the search strategy", DueDate = today.AddDays(5) });
draft.Reminders.Add(new ManuscriptReminder { Title = "Book the ethics amendment", DueDate = today.AddDays(-6), IsCompleted = true, CompletedDate = today.AddDays(-2) });
var readiness = new ManuscriptReadiness { JournalName = "Fictional Nurse Education Review" };
readiness.Items.AddRange(new[]
{
new ReadinessItemState { Title = "Cover letter", IsRequired = true, Status = ReadinessItemStatus.Complete },
new ReadinessItemState { Title = "Ethics statement", IsRequired = true, Status = ReadinessItemStatus.Complete },
new ReadinessItemState { Title = "Data availability statement", IsRequired = true, Status = ReadinessItemStatus.Unresolved },
new ReadinessItemState { Title = "Structured abstract", IsRequired = true, Status = ReadinessItemStatus.Unresolved }
});
draft.ReadinessProfiles.Add(readiness);
var draftVersion = new ManuscriptVersion { Label = "Submission draft", CreatedDate = today.AddDays(-3), Notes = "Fictional version with no file." };
draft.Versions.Add(draftVersion);
draft.SubmissionPackets.Add(new SubmissionPacket { ReadinessProfileId = readiness.Id, JournalName = readiness.JournalName, Label = "Initial submission", ManuscriptVersionId = draftVersion.Id });
manuscripts[0].Reminders.Add(new ManuscriptReminder { Title = "Outline the introduction", DueDate = today.AddDays(12) });

var library = new AuthorLibraryData();
library.Journals.Add(openPsychology);
new AuthorLibraryRepository().Save(empty ? new AuthorLibraryData() : library);
Expand Down
13 changes: 11 additions & 2 deletions ManualCertification/V04Demo/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace PaperRoute.V04Demo;
internal static class Program
{
private const string Usage = "PaperRoute workflow manual demo\n\n" +
"Surfaces: vault (default), readiness, packet, packet-new, file, file-new, notes, submission, responses, workflow, board\n" +
"Surfaces: vault (default), readiness, packet, packet-new, file, file-new, notes, submission, responses, workflow, board, publications, fill\n" +
"Options: --minimum, --primary, --empty (vault/readiness/board), --integrity (populated vault only), --dark or --system, --help\n\n" +
"Default surfaces discard manuscript changes when the window closes.\n" +
"workflow and board save only in a new disposable temporary session.\n" +
Expand All @@ -27,7 +27,7 @@ private static void Main(string[] args)
return;
}

var surfaces = new[] { "vault", "readiness", "packet", "packet-new", "file", "file-new", "notes", "submission", "responses", "workflow", "board" };
var surfaces = new[] { "vault", "readiness", "packet", "packet-new", "file", "file-new", "notes", "submission", "responses", "workflow", "board", "publications", "fill" };
var positional = args.Where(argument => !argument.StartsWith("--")).ToArray();
var surface = positional.FirstOrDefault()?.ToLowerInvariant() ?? "vault";
var minimum = args.Contains("--minimum", StringComparer.OrdinalIgnoreCase);
Expand Down Expand Up @@ -78,6 +78,15 @@ private static void Main(string[] args)
return;
}

if (surface == "publications" || surface == "fill")
{
using var check = PublicationDemo.Create(surface);
check.Text += " [DEMO - synthetic answers; nothing saved]";
ConfigureDisplayEvidence(check, primary);
check.ShowDialog();
return;
}

if (surface == "workflow")
{
// Configure before constructing ANY sample, repository, or form.
Expand Down
65 changes: 65 additions & 0 deletions ManualCertification/V04Demo/PublicationDemo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
using ManuscriptPipeline.Forms;
using ManuscriptPipeline.Models;
using ManuscriptPipeline.Services;

namespace PaperRoute.V04Demo;

// The publication check and Fill Blanks (#61) on fictional records, with
// synthetic answers instead of Crossref and ORCID. Nothing is saved.
internal static class PublicationDemo
{
internal static Form Create(string surface)
{
var today = DateTime.Today;
var library = new List<Manuscript>
{
Sample("DEMO Anchoring effects in clinical risk estimates: a preregistered replication", PaperStage.UnderReview, "Fictional Open Psychology", today.AddDays(-140)),
Sample("DEMO Measurement invariance of a short grit scale across four countries", PaperStage.Accepted, "Fictional Assessment Quarterly", today.AddDays(-300)),
Sample("DEMO Attention capture by salient distractors under working-memory load", PaperStage.Submitted, "Fictional Journal of Perception & Performance", today.AddDays(-20)),
Sample("DEMO Pilot notes on reading fluency", PaperStage.Idea, "", today.AddDays(-5))
};
library[1].Metadata.Doi = "10.5555/demo.grit";
var source = new SyntheticSource(library, today);

if (surface == "fill")
{
library[1].Metadata.PublicationJournal = "Fictional Assessment Quarterly";
return new FillBlanksForm(library, source, () => true) { Pause = TimeSpan.Zero };
}
return new PublicationCheckForm(library, null, "0000-0002-1825-0097", source, () => true, today) { Pause = TimeSpan.FromMilliseconds(400) };
}

private static Manuscript Sample(string title, PaperStage stage, string journal, DateTime submitted)
{
var manuscript = new Manuscript { Title = title, CurrentStage = stage, TargetJournal = journal, Location = ManuscriptLocation.Pipeline };
if (stage >= PaperStage.Submitted)
manuscript.Submissions.Add(new JournalSubmission { JournalName = journal, SubmittedDate = submitted });
return manuscript;
}

private sealed class SyntheticSource(List<Manuscript> library, DateTime today) : IPublicationSource
{
public Task<CrossrefMetadataSuggestion?> LookupDoiAsync(string doi, CancellationToken cancellationToken) =>
Task.FromResult<CrossrefMetadataSuggestion?>(doi == "10.5555/demo.grit"
? new CrossrefMetadataSuggestion
{
Doi = doi, Title = library[1].Title.Replace("DEMO ", ""), Journal = "Fictional Assessment Quarterly",
Publisher = "Fictional Society Press", PublishedDate = today.AddDays(-9), Volume = "33", Issue = "4", Pages = "512-529",
Url = "https://example.org/fictional/grit", WorkType = "journal-article",
AbstractText = "A fictional abstract for the Fill Blanks preview."
}
: null);

public Task<List<CrossrefMetadataSuggestion>> SearchTitleAsync(string title, CancellationToken cancellationToken) =>
Task.FromResult(title == library[0].Title
? new List<CrossrefMetadataSuggestion>
{
new() { Doi = "10.5555/demo.anchoring", Title = "Anchoring effects in clinical risk estimates: A preregistered replication",
Journal = "Fictional Open Psychology", PublishedDate = today.AddDays(-4), WorkType = "journal-article", Volume = "12", Pages = "e1043" }
}
: new List<CrossrefMetadataSuggestion>());

public Task<List<OrcidWorkSuggestion>> OrcidWorksAsync(string orcid, CancellationToken cancellationToken) =>
Task.FromResult(new List<OrcidWorkSuggestion>());
}
}
Loading