feat: import leftover student fields into notes and show them on the roster
Store leftover CSV columns on hidden student notes, move catalog import to /events/import, and persist Students index columns from Chapter Settings. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using Core.Entities;
|
||||
using Core.Notes;
|
||||
using Core.Services;
|
||||
using Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
@@ -46,8 +47,9 @@ public class NotesService : INotesService
|
||||
}
|
||||
|
||||
return await query
|
||||
.OrderBy(n => n.Title != null && n.Title.StartsWith("#") ? 1 : 0) // Non-page notes first (0), page notes last (1)
|
||||
.ThenByDescending(n => n.UpdatedAt) // Within each group, order by most recently updated
|
||||
.Where(n => n.Title == null || !n.Title.StartsWith("#Student:"))
|
||||
.OrderBy(n => n.Title != null && n.Title.StartsWith("#") ? 1 : 0)
|
||||
.ThenByDescending(n => n.UpdatedAt)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
@@ -67,6 +69,84 @@ public class NotesService : INotesService
|
||||
.FirstOrDefaultAsync();
|
||||
}
|
||||
|
||||
public async Task<Note?> GetStudentNoteAsync(int studentId)
|
||||
{
|
||||
var title = _noteNamingService.GetStudentNoteTitle(studentId);
|
||||
return await _context.Notes
|
||||
.AsNoTracking()
|
||||
.Where(n => n.Title == title && !n.IsDeleted)
|
||||
.FirstOrDefaultAsync();
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyDictionary<int, Note>> GetStudentNotesAsync(IEnumerable<int> studentIds)
|
||||
{
|
||||
var ids = studentIds.Distinct().ToList();
|
||||
if (ids.Count == 0)
|
||||
return new Dictionary<int, Note>();
|
||||
|
||||
var titles = ids.Select(_noteNamingService.GetStudentNoteTitle).ToList();
|
||||
var notes = await _context.Notes
|
||||
.AsNoTracking()
|
||||
.Where(n => titles.Contains(n.Title) && !n.IsDeleted)
|
||||
.ToListAsync();
|
||||
|
||||
Dictionary<int, Note> byStudentId = [];
|
||||
foreach (var note in notes)
|
||||
{
|
||||
if (_noteNamingService.TryParseStudentNoteId(note.Title, out var studentId))
|
||||
byStudentId[studentId] = note;
|
||||
}
|
||||
|
||||
return byStudentId;
|
||||
}
|
||||
|
||||
public async Task SoftDeleteStudentNotesAsync(IEnumerable<int> studentIds, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var ids = studentIds.Distinct().ToList();
|
||||
if (ids.Count == 0)
|
||||
return;
|
||||
|
||||
var titles = ids.Select(_noteNamingService.GetStudentNoteTitle).ToList();
|
||||
var notes = await _context.Notes
|
||||
.Where(n => titles.Contains(n.Title) && !n.IsDeleted)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
if (notes.Count == 0)
|
||||
return;
|
||||
|
||||
var userEmail = GetCurrentUserEmail();
|
||||
var now = DateTime.UtcNow;
|
||||
foreach (var note in notes)
|
||||
{
|
||||
_context.NoteHistories.Add(new NoteHistory
|
||||
{
|
||||
NoteId = note.Id,
|
||||
Title = note.Title,
|
||||
Content = note.Content,
|
||||
ModifiedBy = userEmail,
|
||||
ModifiedAt = now,
|
||||
ChangeType = "Soft Deleted"
|
||||
});
|
||||
note.IsDeleted = true;
|
||||
note.UpdatedAt = now;
|
||||
note.LastModifiedBy = userEmail;
|
||||
}
|
||||
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
_logger.LogInformation("Soft-deleted {Count} student note(s)", notes.Count);
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyList<string>> GetImportedFieldNamesAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
var contents = await _context.Notes
|
||||
.AsNoTracking()
|
||||
.Where(n => !n.IsDeleted && n.Title != null && n.Title.StartsWith("#Student:"))
|
||||
.Select(n => n.Content)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
return ImportedFieldsTable.DistinctFieldNames(contents);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<NoteHistory>> GetNoteHistoryAsync(int noteId)
|
||||
{
|
||||
return await _context.NoteHistories
|
||||
@@ -253,9 +333,9 @@ public class NotesService : INotesService
|
||||
{
|
||||
return await _context.Notes
|
||||
.AsNoTracking()
|
||||
.Where(n => n.IsDeleted)
|
||||
.OrderBy(n => n.Title != null && n.Title.StartsWith("#") ? 1 : 0) // Non-page notes first (0), page notes last (1)
|
||||
.ThenByDescending(n => n.UpdatedAt) // Within each group, order by most recently updated
|
||||
.Where(n => n.IsDeleted && (n.Title == null || !n.Title.StartsWith("#Student:")))
|
||||
.OrderBy(n => n.Title != null && n.Title.StartsWith("#") ? 1 : 0)
|
||||
.ThenByDescending(n => n.UpdatedAt)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user