Skip to content

Commit

Permalink
Polished QuestionBank Editor
Browse files Browse the repository at this point in the history
- `Books`: Added a book store which loads book lists from `*.csv` files and each book have a unique code. They can be searched by their code, title & authors.
- QuestionEntry`: added new properties and `Clone` method. `Importance` is autogenerated from number of times asked when `HasPreviouslyBeenAsked` is `False`. `ProbableCases` is now a `List<string>`
- FindFlyout: A flyout control which can be used to find text. Currently being used for QBank Editor data.
- Added the ability to duplicate entries in both `DataGrid` and `ListEditorControl`
- Save button now saves all topics instead of the selected ones.
- Certain controls are enabled only when required.
  • Loading branch information
ShankarBUS committed Nov 22, 2023
1 parent 0cfa22c commit f16e3db
Show file tree
Hide file tree
Showing 18 changed files with 906 additions and 166 deletions.
29 changes: 16 additions & 13 deletions src/Symptum.Core/Subjects/Books/Book.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,30 @@ namespace Symptum.Core.Subjects.Books
{
public class Book : ObservableObject
{
public static readonly Dictionary<string, Book> BookStore = new()
{
};
#region Properties

public static readonly List<Book> Books = [.. BookStore.Values];
private string _code = string.Empty;

#region Properties
public string Code
{
get => _code;
set => SetProperty(ref _code, value);
}

private string _title;
private string _title = string.Empty;

public string Title
{
get => _title;
set => SetProperty(ref _title, value);
}

private string _author;
private string _authors = string.Empty;

public string Author
public string Authors
{
get => _author;
set => SetProperty(ref _author, value);
get => _authors;
set => SetProperty(ref _authors, value);
}

#endregion
Expand All @@ -34,15 +36,16 @@ public Book()
{
}

public Book(string title, string author)
public Book(string code, string title, string author)
{
_code = code;
_title = title;
_author = author;
_authors = author;
}

public override string ToString()
{
return Title + " by " + Author;
return Title + " by " + Authors + " (" + Code + ")";
}
}
}
10 changes: 5 additions & 5 deletions src/Symptum.Core/Subjects/Books/BookLocation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace Symptum.Core.Subjects.Books
{
public class BookLocation : ObservableObject
{
private static readonly string _bookNameId = "n";
private static readonly string _bookCodeId = "n";
private static readonly string _bookEditionId = "ed";
private static readonly string _bookVolumeId = "vol";

Expand Down Expand Up @@ -83,11 +83,11 @@ private static (Book? book, int edition, int volume) ParseBookString(string book
var col = HttpUtility.ParseQueryString(bookString);
if (col != null && col.Count > 0)
{
string? bookName = col[_bookNameId];
string? bookCode = col[_bookCodeId];
string? bookEdition = col[_bookEditionId];
string? bookVolume = col[_bookVolumeId];
if (!string.IsNullOrEmpty(bookName) && Book.BookStore.TryGetValue(bookName, out Book? _book))
book = _book;
if (!string.IsNullOrEmpty(bookCode))
book = BookStore.Books.FirstOrDefault(x => x.Code == bookCode);
if (int.TryParse(bookEdition, out int edNo))
edition = edNo;
if (int.TryParse(bookVolume, out int volNo))
Expand All @@ -100,7 +100,7 @@ private static (Book? book, int edition, int volume) ParseBookString(string book
public override string ToString()
{
var col = HttpUtility.ParseQueryString(string.Empty);
col.Add(_bookNameId, Book.BookStore.FirstOrDefault(x => x.Value == _book).Key ?? string.Empty);
col.Add(_bookCodeId, _book?.Code ?? string.Empty);
col.Add(_bookEditionId, _edition.ToString());
col.Add(_bookVolumeId, _volume.ToString());
return col.ToString() + ParserHelper.BookLocationDelimiter + _pageNumber.ToString();
Expand Down
44 changes: 44 additions & 0 deletions src/Symptum.Core/Subjects/Books/BookStore.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using CsvHelper;
using Symptum.Core.Subjects.QuestionBank;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Symptum.Core.Subjects.Books
{
public class BookStore
{
public static ObservableCollection<Book> Books { get; private set; } = [];

public static void SaveBooks(string path)
{
using var writer = new StreamWriter(path);
using var csv = new CsvWriter(writer, CultureInfo.InvariantCulture);
csv.WriteHeader<Book>();
csv.NextRecord();
foreach (var book in Books)
{
csv.WriteRecord(book);
csv.NextRecord();
}
}

public static void LoadBooks(string path)
{
if (!File.Exists(path) || Path.GetExtension(path).ToLower() != ".csv") return;

using var reader = new StreamReader(path);
using var csv = new CsvReader(reader, CultureInfo.InvariantCulture);
var books = csv.GetRecords<Book>();
foreach (var book in books)
{
Books.Add(book);
}
}
}
}
86 changes: 0 additions & 86 deletions src/Symptum.Core/Subjects/QuestionBank/QuestionBankManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,91 +7,5 @@ public class QuestionBankManager
{
public QuestionBankManager()
{ }

public static QuestionBankTopic GetTestQuestionBankTopic()
{
QuestionBankTopic topic = new("Abdomen")
{
QuestionEntries =
[
new QuestionEntry()
{
Id = QuestionId.Parse("S_AN_12.2"),
Title = "Blood and Nerve Supply of Liver",
Description = "Write the blood supply and nerve supply of Liver. Blood Supply (3), Nerve Supply (2)",
//BookLocations = ["anat?n=vsr&ed=8&vol=2#200"],
ProbableCases = "Mmbu",
YearsAsked = new List<DateOnly>()
{
DateOnly.FromDateTime(DateTime.Parse("2020-3")),
DateOnly.FromDateTime(DateTime.Parse("2018-2")),
DateOnly.FromDateTime(DateTime.Parse("2015-4")),
},
ReferenceLinks = new List<Uri>()
{
new("symptum://subjects/anat/notes/liver#blood_supply"),
new("symptum://subjects/anat/notes/liver#nerve_supply")
}
},
new QuestionEntry()
{
Id = QuestionId.Parse("E_AN_9.5"),
Title = "Define boundaries and contents of posterior mediastinum.\r\nDescribe the formation, course & relations, termination and tributaries of Azygos vein",
Description = "Definition, Boundaries and contents of posterior mediastinum\r\nFormation, course, Relations, Termination, Tributaries of Azygos vein",
//BookLocations = ["anat?n=vsr&ed=8&vol=1#130"],
ProbableCases = "Malak",
YearsAsked = new List<DateOnly>()
{
DateOnly.FromDateTime(DateTime.Parse("2022-2"))
},
ReferenceLinks = new List<Uri>()
{
new("symptum://subjects/anat/notes/posterior_mediastinum"),
new("symptum://subjects/anat/notes/azygousvein")
}
}
]
};

return topic;
}

public static void Test()
{
var topic = GetTestQuestionBankTopic();

topic.SaveAsCSV("D:\\test.csv");

QuestionBank questionBank = new(SubjectList.Anatomy)
{
QuestionBankPapers =
[
new QuestionBankPaper("Paper 1")
{
Topics =
[
topic,
]
}
]
};

string jsonString = JsonSerializer.Serialize(questionBank, new JsonSerializerOptions() { WriteIndented = true });
File.WriteAllText("D:\\test.json", jsonString);
}

public static void Test2()
{
string jsonString = File.ReadAllText("D:\\test.json");
QuestionBank questionBank = JsonSerializer.Deserialize<QuestionBank>(jsonString);
Debug.WriteLine(questionBank.Subject.ToString());
}

public static void Test3()
{
var topic = QuestionBankTopic.ReadTopicFromCSV("D:\\test.csv");
string jsonString = JsonSerializer.Serialize(topic, new JsonSerializerOptions() { WriteIndented = true });
File.WriteAllText("D:\\test3.json", jsonString);
}
}
}
53 changes: 51 additions & 2 deletions src/Symptum.Core/Subjects/QuestionBank/QuestionEntry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using CsvHelper.TypeConversion;
using Symptum.Core.Subjects.Books;
using Symptum.Core.TypeConversion;
using System.Runtime.CompilerServices;

namespace Symptum.Core.Subjects.QuestionBank
{
Expand Down Expand Up @@ -35,6 +36,22 @@ public string Description
set => SetProperty(ref description, value);
}

private bool hasPreviouslyBeenAsked;

public bool HasPreviouslyBeenAsked
{
get => hasPreviouslyBeenAsked;
set => SetProperty(ref hasPreviouslyBeenAsked, value);
}

private int importance = 0;

public int Importance
{
get => importance;
set => SetProperty(ref importance, value);
}

private List<DateOnly> yearsAsked;

[TypeConverter(typeof(DateOnlyListConverter))]
Expand All @@ -53,9 +70,10 @@ public List<BookLocation> BookLocations
set => SetProperty(ref bookLocations, value);
}

private string probableCases;
private List<string> probableCases;

public string ProbableCases
[TypeConverter(typeof(StringListConverter))]
public List<string> ProbableCases
{
get => probableCases;
set => SetProperty(ref probableCases, value);
Expand All @@ -75,5 +93,36 @@ public List<Uri> ReferenceLinks
public QuestionEntry()
{
}

public QuestionEntry Clone()
{
return new QuestionEntry()
{
Id = new() { QuestionType = id.QuestionType, SubjectCode = id.SubjectCode, CompetencyNumbers = id.CompetencyNumbers },
Title = Title,
Description = Description,
HasPreviouslyBeenAsked = HasPreviouslyBeenAsked,
Importance = Importance,
YearsAsked = CloneList(YearsAsked),
BookLocations = CloneList(BookLocations, x => new() { Book = x.Book, Edition = x.Edition, Volume = x.Volume, PageNumber = x.PageNumber }),
ProbableCases = CloneList(probableCases),
ReferenceLinks = CloneList(referenceLinks)
};
}

private List<T> CloneList<T>(List<T> values, Func<T, T>? function = null)
{
List<T> results = [];
if (values != null)
{
foreach (T item in values)
{
T result = function != null ? function(item) : item;
results.Add(result);
}
}

return results;
}
}
}
21 changes: 18 additions & 3 deletions src/Symptum.Editor/App.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
using Microsoft.UI.Xaml;

// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.
using Symptum.Core.Subjects.Books;
using Symptum.Core.Subjects.QuestionBank;
using System;
using System.Diagnostics;
using System.IO;

namespace Symptum.Editor
{
Expand All @@ -25,10 +27,23 @@ public App()
/// <param name="args">Details about the launch request and process.</param>
protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args)
{
LoadAllBookLists(AppDomain.CurrentDomain.BaseDirectory + "\\Books\\");
m_window = new MainWindow();
m_window.Activate();
}

private void LoadAllBookLists(string workPath)
{
if (!Directory.Exists(workPath)) return;

DirectoryInfo directoryInfo = new(workPath);
var csvfiles = directoryInfo.GetFiles("*.csv");
foreach (var csvfile in csvfiles)
{
BookStore.LoadBooks(csvfile.FullName);
}
}

private Window m_window;
}
}
10 changes: 9 additions & 1 deletion src/Symptum.Editor/Controls/BookLocationPicker.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,21 @@
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<StackPanel Grid.ColumnSpan="2" Orientation="Horizontal" Margin="0,0,0,10" Spacing="10">
<ComboBox x:Name="bookSelector" Header="Book" MaxWidth="130" />
<AutoSuggestBox x:Name="bookSelector" Grid.Row="2" Header="Book"
QueryIcon="Find" PlaceholderText="Select a book" MaxWidth="130">
<AutoSuggestBox.ItemTemplate>
<DataTemplate>
<TextBlock ToolTipService.ToolTip="{Binding }" Text="{Binding }" />
</DataTemplate>
</AutoSuggestBox.ItemTemplate>
</AutoSuggestBox>
<NumberBox x:Name="editionSelector" Header="Edition" Minimum="0" SpinButtonPlacementMode="Compact" />
<NumberBox x:Name="volumeSelector" Header="Volume" Minimum="0" SpinButtonPlacementMode="Compact" />
<NumberBox x:Name="pageNoSelector" Header="Page Number" Minimum="0" SpinButtonPlacementMode="Compact" />
Expand Down
Loading

0 comments on commit f16e3db

Please sign in to comment.