From f63e74634ecaca0bfd392d32180984ff3051338a Mon Sep 17 00:00:00 2001 From: Shankar Date: Sat, 16 Dec 2023 00:41:24 +0530 Subject: [PATCH 1/7] Decoupled Editors - Each IResource can have its own editor page - Markdown generation for QBanks - Code Cleanup and Refactoring --- src/{Symptum.Editor => }/.editorconfig | 0 .../Helpers/ObservableCollectionHelper.cs | 20 + src/Symptum.Core/Helpers/ParserHelper.cs | 13 +- src/Symptum.Core/IUniqueId.cs | 8 + .../Management/Deployment/IPackage.cs | 27 +- .../Management/Navigation/INavigable.cs | 12 + .../Management/Navigation/INavigatable.cs | 13 - .../Navigation/NavigationManager.cs | 11 +- .../Management/Navigation/NavigationType.cs | 17 +- .../Management/Resource/ContentFileType.cs | 9 - .../Management/Resource/IContent.cs | 23 - .../Management/Resource/ResourceManager.cs | 7 - .../Management/Resources/AuthorInfo.cs | 8 + .../Management/Resources/ContentFileType.cs | 8 + .../Management/Resources/IContent.cs | 22 + .../Management/Resources/IResource.cs | 29 + .../Management/Resources/NavigableResource.cs | 223 ++++++++ .../Management/Resources/PackageResource.cs | 50 ++ .../Management/Resources/ResourceManager.cs | 20 + src/Symptum.Core/Subjects/Books/Book.cs | 71 ++- .../Subjects/Books/BookLocation.cs | 169 +++--- src/Symptum.Core/Subjects/Books/BookStore.cs | 49 +- .../Subjects/QuestionBank/Question.cs | 23 - .../Subjects/QuestionBank/QuestionBank.cs | 23 - .../QuestionBank/QuestionBankManager.cs | 8 - .../QuestionBank/QuestionBankPaper.cs | 19 - .../QuestionBank/QuestionBankTopic.cs | 66 --- .../Subjects/QuestionBank/QuestionEntry.cs | 128 ----- .../Subjects/QuestionBank/QuestionId.cs | 111 ---- .../Subjects/QuestionBank/QuestionType.cs | 9 - .../Subjects/QuestionBanks/QuestionBank.cs | 45 ++ .../QuestionBanks/QuestionBankManager.cs | 8 + .../QuestionBanks/QuestionBankPaper.cs | 42 ++ .../QuestionBanks/QuestionBankTopic.cs | 81 +++ .../Subjects/QuestionBanks/QuestionEntry.cs | 161 ++++++ .../Subjects/QuestionBanks/QuestionId.cs | 128 +++++ .../Subjects/QuestionBanks/QuestionType.cs | 8 + src/Symptum.Core/Subjects/Subject.cs | 69 +++ src/Symptum.Core/Subjects/SubjectList.cs | 35 +- src/Symptum.Core/Subjects/SubjectMap.cs | 53 +- src/Symptum.Core/TypeConversion/Converters.cs | 195 ++++--- .../Symptum.Editor.Shared/AppHead.xaml.cs | 2 +- src/Symptum.Editor/Symptum.Editor/App.cs | 9 +- .../Common/DefaultIconSources.cs | 6 + .../Controls/AddNewItemDialog.xaml | 13 + .../Controls/AddNewItemDialog.xaml.cs | 9 + .../Controls/BookLocationPicker.xaml.cs | 2 +- .../Symptum.Editor/Controls/EditorResult.cs | 10 + .../Symptum.Editor/Controls/FindFlyout.xaml | 7 +- .../Controls/FindFlyout.xaml.cs | 42 +- .../Controls/ListEditorControl.xaml | 4 +- .../Controls/ListEditorControl.xaml.cs | 2 +- .../Controls/QuestionEditorDialog.xaml | 20 +- .../Controls/QuestionEditorDialog.xaml.cs | 30 +- .../NavigableResourceTemplateSelector.cs | 18 + .../NavigableResourceTemplatesHandler.cs | 31 + .../TreeViewNavigableResourceTemplates.xaml | 29 + ...TreeViewNavigableResourceTemplates.xaml.cs | 9 + .../Controls/Templating/TypedDataTemplate.cs | 15 + .../Controls/TopicEditorDialog.xaml | 2 +- .../Controls/TopicEditorDialog.xaml.cs | 36 +- .../BooleanToVisibilityConverter.cs | 29 + .../Converters/UriToStringConverter.cs | 2 +- .../EditorPages/EditorPagesManager.cs | 68 +++ .../Symptum.Editor/EditorPages/IEditorPage.cs | 18 + .../EditorPages/QuestionTopicEditorPage.xaml | 103 ++++ .../QuestionTopicEditorPage.xaml.cs | 362 ++++++++++++ .../Symptum.Editor/Helpers/MarkdownHelper.cs | 147 +++++ .../Symptum.Editor/Helpers/ResourceHelper.cs | 118 ++++ .../Symptum.Editor/MainPage.xaml | 172 +++--- .../Symptum.Editor/MainPage.xaml.cs | 528 ++++++------------ .../Symptum.Editor/Symptum.Editor.csproj | 12 + 72 files changed, 2602 insertions(+), 1274 deletions(-) rename src/{Symptum.Editor => }/.editorconfig (100%) create mode 100644 src/Symptum.Core/Helpers/ObservableCollectionHelper.cs create mode 100644 src/Symptum.Core/IUniqueId.cs create mode 100644 src/Symptum.Core/Management/Navigation/INavigable.cs delete mode 100644 src/Symptum.Core/Management/Navigation/INavigatable.cs delete mode 100644 src/Symptum.Core/Management/Resource/ContentFileType.cs delete mode 100644 src/Symptum.Core/Management/Resource/IContent.cs delete mode 100644 src/Symptum.Core/Management/Resource/ResourceManager.cs create mode 100644 src/Symptum.Core/Management/Resources/AuthorInfo.cs create mode 100644 src/Symptum.Core/Management/Resources/ContentFileType.cs create mode 100644 src/Symptum.Core/Management/Resources/IContent.cs create mode 100644 src/Symptum.Core/Management/Resources/IResource.cs create mode 100644 src/Symptum.Core/Management/Resources/NavigableResource.cs create mode 100644 src/Symptum.Core/Management/Resources/PackageResource.cs create mode 100644 src/Symptum.Core/Management/Resources/ResourceManager.cs delete mode 100644 src/Symptum.Core/Subjects/QuestionBank/Question.cs delete mode 100644 src/Symptum.Core/Subjects/QuestionBank/QuestionBank.cs delete mode 100644 src/Symptum.Core/Subjects/QuestionBank/QuestionBankManager.cs delete mode 100644 src/Symptum.Core/Subjects/QuestionBank/QuestionBankPaper.cs delete mode 100644 src/Symptum.Core/Subjects/QuestionBank/QuestionBankTopic.cs delete mode 100644 src/Symptum.Core/Subjects/QuestionBank/QuestionEntry.cs delete mode 100644 src/Symptum.Core/Subjects/QuestionBank/QuestionId.cs delete mode 100644 src/Symptum.Core/Subjects/QuestionBank/QuestionType.cs create mode 100644 src/Symptum.Core/Subjects/QuestionBanks/QuestionBank.cs create mode 100644 src/Symptum.Core/Subjects/QuestionBanks/QuestionBankManager.cs create mode 100644 src/Symptum.Core/Subjects/QuestionBanks/QuestionBankPaper.cs create mode 100644 src/Symptum.Core/Subjects/QuestionBanks/QuestionBankTopic.cs create mode 100644 src/Symptum.Core/Subjects/QuestionBanks/QuestionEntry.cs create mode 100644 src/Symptum.Core/Subjects/QuestionBanks/QuestionId.cs create mode 100644 src/Symptum.Core/Subjects/QuestionBanks/QuestionType.cs create mode 100644 src/Symptum.Core/Subjects/Subject.cs create mode 100644 src/Symptum.Editor/Symptum.Editor/Common/DefaultIconSources.cs create mode 100644 src/Symptum.Editor/Symptum.Editor/Controls/AddNewItemDialog.xaml create mode 100644 src/Symptum.Editor/Symptum.Editor/Controls/AddNewItemDialog.xaml.cs create mode 100644 src/Symptum.Editor/Symptum.Editor/Controls/EditorResult.cs create mode 100644 src/Symptum.Editor/Symptum.Editor/Controls/Templating/NavigableResourceTemplateSelector.cs create mode 100644 src/Symptum.Editor/Symptum.Editor/Controls/Templating/NavigableResourceTemplatesHandler.cs create mode 100644 src/Symptum.Editor/Symptum.Editor/Controls/Templating/TreeViewNavigableResourceTemplates.xaml create mode 100644 src/Symptum.Editor/Symptum.Editor/Controls/Templating/TreeViewNavigableResourceTemplates.xaml.cs create mode 100644 src/Symptum.Editor/Symptum.Editor/Controls/Templating/TypedDataTemplate.cs create mode 100644 src/Symptum.Editor/Symptum.Editor/Converters/BooleanToVisibilityConverter.cs create mode 100644 src/Symptum.Editor/Symptum.Editor/EditorPages/EditorPagesManager.cs create mode 100644 src/Symptum.Editor/Symptum.Editor/EditorPages/IEditorPage.cs create mode 100644 src/Symptum.Editor/Symptum.Editor/EditorPages/QuestionTopicEditorPage.xaml create mode 100644 src/Symptum.Editor/Symptum.Editor/EditorPages/QuestionTopicEditorPage.xaml.cs create mode 100644 src/Symptum.Editor/Symptum.Editor/Helpers/MarkdownHelper.cs create mode 100644 src/Symptum.Editor/Symptum.Editor/Helpers/ResourceHelper.cs diff --git a/src/Symptum.Editor/.editorconfig b/src/.editorconfig similarity index 100% rename from src/Symptum.Editor/.editorconfig rename to src/.editorconfig diff --git a/src/Symptum.Core/Helpers/ObservableCollectionHelper.cs b/src/Symptum.Core/Helpers/ObservableCollectionHelper.cs new file mode 100644 index 0000000..70a35dc --- /dev/null +++ b/src/Symptum.Core/Helpers/ObservableCollectionHelper.cs @@ -0,0 +1,20 @@ +using System.Collections.ObjectModel; +using Symptum.Core.Management.Resources; + +namespace Symptum.Core.Helpers; + +public static class ObservableCollectionHelper +{ + private static Dictionary> collections = []; + + public static void ObserveCollection(this NavigableResource navigableResource, ObservableCollection collection) + { + if (collection == null) return; + collection.CollectionChanged += Collection_CollectionChanged; + collections[navigableResource] = collection; + } + + private static void Collection_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) + { + } +} diff --git a/src/Symptum.Core/Helpers/ParserHelper.cs b/src/Symptum.Core/Helpers/ParserHelper.cs index 0a3c995..ec6fefc 100644 --- a/src/Symptum.Core/Helpers/ParserHelper.cs +++ b/src/Symptum.Core/Helpers/ParserHelper.cs @@ -1,11 +1,10 @@ -namespace Symptum.Core.Helpers +namespace Symptum.Core.Helpers; + +public class ParserHelper { - public class ParserHelper - { - public static readonly string QuestionIdDelimiter = "_"; + public static readonly string QuestionIdDelimiter = "_"; - public static readonly string ListDelimiter = ";"; + public static readonly string ListDelimiter = ";"; - public static readonly string BookLocationDelimiter = "#"; - } + public static readonly string BookLocationDelimiter = "#"; } \ No newline at end of file diff --git a/src/Symptum.Core/IUniqueId.cs b/src/Symptum.Core/IUniqueId.cs new file mode 100644 index 0000000..f5069b7 --- /dev/null +++ b/src/Symptum.Core/IUniqueId.cs @@ -0,0 +1,8 @@ +namespace Symptum.Core; + +// Will be used for classes which function as Identifiers. Eg: Question Entries, Reference Values +// Will be helpful for navigation, bookmarking, etc. +public interface IUniqueId +{ + static abstract IUniqueId? Parse(string? idText); +} diff --git a/src/Symptum.Core/Management/Deployment/IPackage.cs b/src/Symptum.Core/Management/Deployment/IPackage.cs index 8b105c9..7c1d2c4 100644 --- a/src/Symptum.Core/Management/Deployment/IPackage.cs +++ b/src/Symptum.Core/Management/Deployment/IPackage.cs @@ -1,19 +1,22 @@ -namespace Symptum.Core.Management.Deployment +using Symptum.Core.Management.Resources; + +namespace Symptum.Core.Management.Deployment; + +public interface IPackage { - public interface IPackage - { - public string Name { get; set; } + string Title { get; set; } + + string Description { get; set; } - public string Description { get; set; } + Version Version { get; set; } - public Version Version { get; set; } + IList? Authors { get; set; } - public string Authors { get; set; } + IList? Contents { get; set; } - public object Content { get; set; } + IList? Dependencies { get; set; } - public string Dependencies { get; set; } + IList? DependencyIds { get; set; } - public IList Tags { get; set; } - } -} \ No newline at end of file + IList? Tags { get; set; } +} diff --git a/src/Symptum.Core/Management/Navigation/INavigable.cs b/src/Symptum.Core/Management/Navigation/INavigable.cs new file mode 100644 index 0000000..489aa2d --- /dev/null +++ b/src/Symptum.Core/Management/Navigation/INavigable.cs @@ -0,0 +1,12 @@ +namespace Symptum.Core.Management.Navigation; + +public interface INavigable +{ + //Uri Uri { get; set; } + + //bool IsNavigationHandled { get; set; } + + //Type PageType { get; set; } + + //NavigationType NavigationType { get; set; } +} \ No newline at end of file diff --git a/src/Symptum.Core/Management/Navigation/INavigatable.cs b/src/Symptum.Core/Management/Navigation/INavigatable.cs deleted file mode 100644 index 874828c..0000000 --- a/src/Symptum.Core/Management/Navigation/INavigatable.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Symptum.Core.Management.Navigation -{ - public interface INavigatable - { - public Uri Uri { get; set; } - - public bool IsNavigationHandled { get; set; } - - public Type PageType { get; set; } - - public NavigationType NavigationType { get; set; } - } -} \ No newline at end of file diff --git a/src/Symptum.Core/Management/Navigation/NavigationManager.cs b/src/Symptum.Core/Management/Navigation/NavigationManager.cs index fed4a45..b5aba94 100644 --- a/src/Symptum.Core/Management/Navigation/NavigationManager.cs +++ b/src/Symptum.Core/Management/Navigation/NavigationManager.cs @@ -1,8 +1,7 @@ -namespace Symptum.Core.Management.Navigation +namespace Symptum.Core.Management.Navigation; + +public class NavigationManager { - public class NavigationManager - { - public NavigationManager() - { } - } + public NavigationManager() + { } } \ No newline at end of file diff --git a/src/Symptum.Core/Management/Navigation/NavigationType.cs b/src/Symptum.Core/Management/Navigation/NavigationType.cs index a4794b4..869c616 100644 --- a/src/Symptum.Core/Management/Navigation/NavigationType.cs +++ b/src/Symptum.Core/Management/Navigation/NavigationType.cs @@ -1,11 +1,10 @@ -namespace Symptum.Core.Management.Navigation +namespace Symptum.Core.Management.Navigation; + +public enum NavigationType { - public enum NavigationType - { - MarkdownContent, - ListOfContents, - QuestionBank, - TopLevel, - SubLevel - } + TopLevel, + SubLevel, + QuestionBank, + ListOfContents, + MarkdownContent } \ No newline at end of file diff --git a/src/Symptum.Core/Management/Resource/ContentFileType.cs b/src/Symptum.Core/Management/Resource/ContentFileType.cs deleted file mode 100644 index 60b028d..0000000 --- a/src/Symptum.Core/Management/Resource/ContentFileType.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Symptum.Core.Management.Resource -{ - public enum ContentFileType - { - Markdown, - Csv, - Image - } -} \ No newline at end of file diff --git a/src/Symptum.Core/Management/Resource/IContent.cs b/src/Symptum.Core/Management/Resource/IContent.cs deleted file mode 100644 index 41adbc9..0000000 --- a/src/Symptum.Core/Management/Resource/IContent.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace Symptum.Core.Management.Resource -{ - public interface IContent - { - public string Title { get; set; } - - public string Description { get; set; } - - public string Authors { get; set; } - - public ContentFileType ContentFileType { get; set; } - - public DateOnly DateModified { get; set; } - - public IList References { get; set; } - - public IList Tags { get; set; } - - public IList SeeAlso { get; set; } - - public string Dependencies { get; set; } - } -} \ No newline at end of file diff --git a/src/Symptum.Core/Management/Resource/ResourceManager.cs b/src/Symptum.Core/Management/Resource/ResourceManager.cs deleted file mode 100644 index c60036e..0000000 --- a/src/Symptum.Core/Management/Resource/ResourceManager.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Symptum.Core.Management.Resource -{ - public class ResourceManager - { - public static Uri DefaultUri = new Uri("symptum://"); - } -} \ No newline at end of file diff --git a/src/Symptum.Core/Management/Resources/AuthorInfo.cs b/src/Symptum.Core/Management/Resources/AuthorInfo.cs new file mode 100644 index 0000000..eca359a --- /dev/null +++ b/src/Symptum.Core/Management/Resources/AuthorInfo.cs @@ -0,0 +1,8 @@ +namespace Symptum.Core.Management.Resources; + +public struct AuthorInfo +{ + string Name { get; set; } + + string Email { get; set; } +} diff --git a/src/Symptum.Core/Management/Resources/ContentFileType.cs b/src/Symptum.Core/Management/Resources/ContentFileType.cs new file mode 100644 index 0000000..627c0cb --- /dev/null +++ b/src/Symptum.Core/Management/Resources/ContentFileType.cs @@ -0,0 +1,8 @@ +namespace Symptum.Core.Management.Resources; + +public enum ContentFileType +{ + Markdown, + Csv, + Image +} \ No newline at end of file diff --git a/src/Symptum.Core/Management/Resources/IContent.cs b/src/Symptum.Core/Management/Resources/IContent.cs new file mode 100644 index 0000000..2f4c9b7 --- /dev/null +++ b/src/Symptum.Core/Management/Resources/IContent.cs @@ -0,0 +1,22 @@ +namespace Symptum.Core.Management.Resources; + +public interface IContent +{ + public string Title { get; set; } + + public string Description { get; set; } + + public string Authors { get; set; } + + public ContentFileType ContentFileType { get; set; } + + public DateOnly DateModified { get; set; } + + public IList References { get; set; } + + public IList Tags { get; set; } + + public IList SeeAlso { get; set; } + + public string Dependencies { get; set; } +} \ No newline at end of file diff --git a/src/Symptum.Core/Management/Resources/IResource.cs b/src/Symptum.Core/Management/Resources/IResource.cs new file mode 100644 index 0000000..a6768b2 --- /dev/null +++ b/src/Symptum.Core/Management/Resources/IResource.cs @@ -0,0 +1,29 @@ +namespace Symptum.Core.Management.Resources; + +public interface IResource +{ + // Will be used mostly for navigation, will be overlapping with Id in most cases but Id will be different in case of cross linking and embedding + // Eg: Micro/Pedia/CM.ImmunizationSchedule will be different Ids but Uri will be same : mi/sm/notes/immunology/immunizationschedule + Uri Uri { get; set; } // symptum://subjects/an/sm/notes/abdomen/liver; symptum://subjects/an/qbank/1/abdomen#S_AN_12.3.4 + + // Will be used for dependency & resource file resolution and naming of packages + string Id { get; set; } // AUTOGEN: {Parent.Id}.{Title} -> Subjects.Anatomy.StudyMaterials.Notes.Abdomen.Liver + + string Title { get; set; } // Liver + + IResource? ParentResource { get; } // Id: Subjects.Anatomy.StudyMaterials.Notes.Abdomen + + IList? ChildrenResources { get; set; } // null because end resource + + IList? Dependencies { get; set; } + + IList? DependencyIds { get; set; } + + void InitializeResource(IResource? parent); + + bool CanHandleChildResourceType(Type childResourceType); + + //IResource GetIResourceFromRelativeUri(Uri relativeUri); + + //IResource GetIResourceFromRelativeId(string relativeId); +} diff --git a/src/Symptum.Core/Management/Resources/NavigableResource.cs b/src/Symptum.Core/Management/Resources/NavigableResource.cs new file mode 100644 index 0000000..33769f5 --- /dev/null +++ b/src/Symptum.Core/Management/Resources/NavigableResource.cs @@ -0,0 +1,223 @@ +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.Text.Json.Serialization; +using CommunityToolkit.Mvvm.ComponentModel; +using Symptum.Core.Management.Navigation; + +namespace Symptum.Core.Management.Resources; + +public abstract class NavigableResource : ObservableObject, IResource, INavigable +{ + #region Properties + + #region IResource + + private Uri uri; + + public Uri Uri + { + get => uri; + set => SetProperty(ref uri, value); + } + + private string id = string.Empty; + + public string Id + { + get => id; + set => SetProperty(ref id, value); + } + + private string _title = string.Empty; + + public string Title + { + get => _title; + set => SetProperty(ref _title, value); + } + + private IResource? parentResource; + + [JsonIgnore] + public IResource? ParentResource + { + get => parentResource; + private set => SetProperty(ref parentResource, value); + } + + IList? IResource.ChildrenResources + { + get => childrenResources; + set + { + if (value == null) + ChildrenResources = null; + else + ChildrenResources = new(value); + } + } + + private ObservableCollection? childrenResources; + + [JsonIgnore] + public ObservableCollection? ChildrenResources + { + get => childrenResources; + set + { + //UnobserveCollection(childrenResources, true); + SetProperty(ref childrenResources, value); + //ObserveCollection(childrenResources); + } + } + + private IList? dependencies; + + [JsonIgnore] + public IList? Dependencies + { + get => dependencies; + set => SetProperty(ref dependencies, value); + } + + private IList? dependencyIds; + + [JsonPropertyName(nameof(Dependencies))] + public IList? DependencyIds + { + get => dependencyIds; + set => SetProperty(ref dependencyIds, value); + } + + #endregion + + private bool hasInitialized = false; + + public bool HasInitialized + { + get => hasInitialized; + private set => SetProperty(ref hasInitialized, value); + } + + #endregion + + void IResource.InitializeResource(IResource? parent) + { + ParentResource = parent; + OnInitializeResource(parent); + + // Temporary + if (childrenResources != null) + { + foreach (var child in childrenResources) + { + child.InitializeResource(this); + } + } + hasInitialized = true; + } + + protected abstract void OnInitializeResource(IResource? parent); + + //protected virtual void OnChildrenReset() + //{ + //} + + //protected virtual void OnChildrenAdded(IEnumerable addedItems) + //{ + //} + + //protected virtual void OnChildrenRemoved(IEnumerable removedItems) + //{ + //} + + public abstract bool CanHandleChildResourceType(Type childResourceType); + + protected void SetChildrenResources(ObservableCollection collection) where T : IResource + { + if (collection != null) + ChildrenResources = new(collection.OfType()); + else + ChildrenResources = []; + } + + protected void UnobserveCollection(ObservableCollection collection, bool isChildrenResources = false) where T : IResource + { + if (hasInitialized && !isChildrenResources) + childrenResources?.Clear(); + if (collection != null) + collection.CollectionChanged -= Collection_Changed; + } + + protected void ObserveCollection(ObservableCollection collection) where T : IResource + { + if (collection == null) return; + collection.CollectionChanged += Collection_Changed; + } + + private void Collection_Changed(object? sender, NotifyCollectionChangedEventArgs e) + { + if (!hasInitialized || childrenResources == null) return; + //bool isCR = sender == childrenResources; + + //if (!isCR) + //{ + switch (e.Action) + { + case NotifyCollectionChangedAction.Reset: + { + childrenResources.Clear(); + break; + } + case NotifyCollectionChangedAction.Add: + { + if (e.NewItems != null && e.NewItems.Count > 0) + { + foreach (var item in e.NewItems) + { + if (item is IResource resource) + childrenResources.Add(resource); + } + } + break; + } + case NotifyCollectionChangedAction.Remove: + { + if (e.OldItems != null && e.OldItems.Count > 0) + { + foreach (var item in e.OldItems) + { + if (item is IResource resource && childrenResources.Contains(resource)) + childrenResources.Remove(resource); + } + } + break; + } + } + //} + //else + // HandleChildrenChanged(e); + } + + //private void HandleChildrenChanged(NotifyCollectionChangedEventArgs e) + //{ + // switch (e.Action) + // { + // case System.Collections.Specialized.NotifyCollectionChangedAction.Reset: + // { + // OnChildrenReset(); + // break; + // } + // case System.Collections.Specialized.NotifyCollectionChangedAction.Add: + // { + // OnChildrenAdded(e.NewItems?.OfType()); + // break; + // } + // case System.Collections.Specialized.NotifyCollectionChangedAction.Remove: + // { + // OnChildrenRemoved(e.OldItems?.OfType()); + // break; + // } + // } + //} +} diff --git a/src/Symptum.Core/Management/Resources/PackageResource.cs b/src/Symptum.Core/Management/Resources/PackageResource.cs new file mode 100644 index 0000000..71e772d --- /dev/null +++ b/src/Symptum.Core/Management/Resources/PackageResource.cs @@ -0,0 +1,50 @@ +using Symptum.Core.Management.Deployment; + +namespace Symptum.Core.Management.Resources; + +public abstract class PackageResource : NavigableResource, IPackage +{ + #region Properties + + private string description = string.Empty; + + public string Description + { + get => description; + set => SetProperty(ref description, value); + } + + private Version version = new(0, 0, 0); + + public Version Version + { + get => version; + set => SetProperty(ref version, value); + } + + private IList? authors; + + public IList? Authors + { + get => authors; + set => SetProperty(ref authors, value); + } + + private IList? contents; + + public IList? Contents + { + get => contents; + set => SetProperty(ref contents, value); + } + + private IList? tags; + + public IList? Tags + { + get => tags; + set => SetProperty(ref tags, value); + } + + #endregion +} diff --git a/src/Symptum.Core/Management/Resources/ResourceManager.cs b/src/Symptum.Core/Management/Resources/ResourceManager.cs new file mode 100644 index 0000000..4b60e5f --- /dev/null +++ b/src/Symptum.Core/Management/Resources/ResourceManager.cs @@ -0,0 +1,20 @@ +namespace Symptum.Core.Management.Resources; + +public class ResourceManager +{ + private static string defaultUriScheme = "symptum://"; + + public static Uri DefaultUri = new(defaultUriScheme); + + public static Uri GetAbsoluteUri(string path) => new(defaultUriScheme + path); + + public static IList? ResolveDependencies(IResource resource, IList dependencyIds) + { + throw new NotImplementedException(); + } + + public static async Task?> ResolveDependenciesAsync(IResource resource, IList dependencyIds) + { + return await Task.Run(() => ResolveDependencies(resource, dependencyIds)); + } +} diff --git a/src/Symptum.Core/Subjects/Books/Book.cs b/src/Symptum.Core/Subjects/Books/Book.cs index cdc293c..9fffd59 100644 --- a/src/Symptum.Core/Subjects/Books/Book.cs +++ b/src/Symptum.Core/Subjects/Books/Book.cs @@ -1,51 +1,50 @@ using CommunityToolkit.Mvvm.ComponentModel; -namespace Symptum.Core.Subjects.Books +namespace Symptum.Core.Subjects.Books; + +public class Book : ObservableObject { - public class Book : ObservableObject - { - #region Properties + #region Properties - private string _code = string.Empty; + private string _code = string.Empty; - public string Code - { - get => _code; - set => SetProperty(ref _code, value); - } + public string Code + { + get => _code; + set => SetProperty(ref _code, value); + } - private string _title = string.Empty; + private string _title = string.Empty; - public string Title - { - get => _title; - set => SetProperty(ref _title, value); - } + public string Title + { + get => _title; + set => SetProperty(ref _title, value); + } - private string _authors = string.Empty; + private string _authors = string.Empty; - public string Authors - { - get => _authors; - set => SetProperty(ref _authors, value); - } + public string Authors + { + get => _authors; + set => SetProperty(ref _authors, value); + } - #endregion + #endregion - public Book() - { - } + public Book() + { + } - public Book(string code, string title, string author) - { - _code = code; - _title = title; - _authors = author; - } + public Book(string code, string title, string author) + { + _code = code; + _title = title; + _authors = author; + } - public override string ToString() - { - return Title + " by " + Authors + " (" + Code + ")"; - } + public override string ToString() + { + return Title + " by " + Authors + " (" + Code + ")"; } } \ No newline at end of file diff --git a/src/Symptum.Core/Subjects/Books/BookLocation.cs b/src/Symptum.Core/Subjects/Books/BookLocation.cs index 65ab605..48c7725 100644 --- a/src/Symptum.Core/Subjects/Books/BookLocation.cs +++ b/src/Symptum.Core/Subjects/Books/BookLocation.cs @@ -1,109 +1,108 @@ -using CommunityToolkit.Mvvm.ComponentModel; -using Symptum.Core.Helpers; using System.Web; +using CommunityToolkit.Mvvm.ComponentModel; +using Symptum.Core.Helpers; + +namespace Symptum.Core.Subjects.Books; -namespace Symptum.Core.Subjects.Books +public class BookLocation : ObservableObject { - public class BookLocation : ObservableObject - { - private static readonly string _bookCodeId = "n"; - private static readonly string _bookEditionId = "ed"; - private static readonly string _bookVolumeId = "vol"; + private static readonly string _bookCodeId = "n"; + private static readonly string _bookEditionId = "ed"; + private static readonly string _bookVolumeId = "vol"; - #region Properties + #region Properties - private Book _book; + private Book _book; - public Book Book - { - get => _book; - set => SetProperty(ref _book, value); - } + public Book Book + { + get => _book; + set => SetProperty(ref _book, value); + } - private int _edition; + private int _edition; - public int Edition - { - get => _edition; - set => SetProperty(ref _edition, value); - } + public int Edition + { + get => _edition; + set => SetProperty(ref _edition, value); + } - private int _volume; + private int _volume; - public int Volume - { - get => _volume; - set => SetProperty(ref _volume, value); - } + public int Volume + { + get => _volume; + set => SetProperty(ref _volume, value); + } - private int _pageNumber; + private int _pageNumber; - public int PageNumber - { - get => _pageNumber; - set => SetProperty(ref _pageNumber, value); - } + public int PageNumber + { + get => _pageNumber; + set => SetProperty(ref _pageNumber, value); + } - #endregion + #endregion - public BookLocation() - { - } + public BookLocation() + { + } - public static bool TryParse(string? text, out BookLocation? location) + public static bool TryParse(string? text, out BookLocation? location) + { + bool parsed = false; + location = null; + if (!string.IsNullOrEmpty(text)) { - bool parsed = false; - location = null; - if (!string.IsNullOrEmpty(text)) + var values = text.Split(ParserHelper.BookLocationDelimiter); + if (values.Length == 2) { - var values = text.Split(ParserHelper.BookLocationDelimiter); - if (values.Length == 2) - { - location = new BookLocation(); - - string bookString = values[0]; - (Book? book, int edition, int volume) = ParseBookString(bookString); - location.Book = book; - location.Edition = edition; - location.Volume = volume; - if (int.TryParse(values[1], out int pgNo)) - location.PageNumber = pgNo; - parsed = true; - } + location = new BookLocation(); + + string bookString = values[0]; + (Book? book, int edition, int volume) = ParseBookString(bookString); + location.Book = book; + location.Edition = edition; + location.Volume = volume; + if (int.TryParse(values[1], out int pgNo)) + location.PageNumber = pgNo; + parsed = true; } - - return parsed; } - private static (Book? book, int edition, int volume) ParseBookString(string bookString) - { - Book? book = null; - int edition = 0; - int volume = 0; - var col = HttpUtility.ParseQueryString(bookString); - if (col != null && col.Count > 0) - { - string? bookCode = col[_bookCodeId]; - string? bookEdition = col[_bookEditionId]; - string? bookVolume = col[_bookVolumeId]; - 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)) - volume = volNo; - } - - return (book, edition, volume); - } + return parsed; + } - public override string ToString() + private static (Book? book, int edition, int volume) ParseBookString(string bookString) + { + Book? book = null; + int edition = 0; + int volume = 0; + var col = HttpUtility.ParseQueryString(bookString); + if (col != null && col.Count > 0) { - var col = HttpUtility.ParseQueryString(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(); + string? bookCode = col[_bookCodeId]; + string? bookEdition = col[_bookEditionId]; + string? bookVolume = col[_bookVolumeId]; + 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)) + volume = volNo; } + + return (book, edition, volume); + } + + public override string ToString() + { + var col = HttpUtility.ParseQueryString(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(); } -} \ No newline at end of file +} diff --git a/src/Symptum.Core/Subjects/Books/BookStore.cs b/src/Symptum.Core/Subjects/Books/BookStore.cs index 6b4bdd8..5b7fa01 100644 --- a/src/Symptum.Core/Subjects/Books/BookStore.cs +++ b/src/Symptum.Core/Subjects/Books/BookStore.cs @@ -1,37 +1,36 @@ -using CsvHelper; using System.Collections.ObjectModel; using System.Globalization; +using CsvHelper; -namespace Symptum.Core.Subjects.Books +namespace Symptum.Core.Subjects.Books; + +public class BookStore { - public class BookStore - { - public static ObservableCollection Books { get; private set; } = []; + public static ObservableCollection Books { get; private set; } = []; - public static void SaveBooks(string path) + public static void SaveBooks(string path) + { + using var writer = new StreamWriter(path); + using var csv = new CsvWriter(writer, CultureInfo.InvariantCulture); + csv.WriteHeader(); + csv.NextRecord(); + foreach (var book in Books) { - using var writer = new StreamWriter(path); - using var csv = new CsvWriter(writer, CultureInfo.InvariantCulture); - csv.WriteHeader(); + csv.WriteRecord(book); csv.NextRecord(); - foreach (var book in Books) - { - csv.WriteRecord(book); - csv.NextRecord(); - } } + } - public static void LoadBooks(string csv) - { - if (string.IsNullOrEmpty(csv)) return; + public static void LoadBooks(string csv) + { + if (string.IsNullOrEmpty(csv)) return; - using var reader = new StringReader(csv); - using var csvReader = new CsvReader(reader, CultureInfo.InvariantCulture); - var books = csvReader.GetRecords(); - foreach (var book in books) - { - Books.Add(book); - } + using var reader = new StringReader(csv); + using var csvReader = new CsvReader(reader, CultureInfo.InvariantCulture); + var books = csvReader.GetRecords(); + foreach (var book in books) + { + Books.Add(book); } } -} \ No newline at end of file +} diff --git a/src/Symptum.Core/Subjects/QuestionBank/Question.cs b/src/Symptum.Core/Subjects/QuestionBank/Question.cs deleted file mode 100644 index caee22b..0000000 --- a/src/Symptum.Core/Subjects/QuestionBank/Question.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace Symptum.Core.Subjects.QuestionBank -{ - public class Question - { - public string Id; - - public bool IsChecked; - - public List Entries; - - public int GetNumberOfTimesAsked() - { - int count = 0; - - foreach (QuestionEntry entry in Entries) - { - count += entry.YearsAsked.Count; - } - - return count; - } - } -} \ No newline at end of file diff --git a/src/Symptum.Core/Subjects/QuestionBank/QuestionBank.cs b/src/Symptum.Core/Subjects/QuestionBank/QuestionBank.cs deleted file mode 100644 index 38d0c20..0000000 --- a/src/Symptum.Core/Subjects/QuestionBank/QuestionBank.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Collections.ObjectModel; - -namespace Symptum.Core.Subjects.QuestionBank -{ - public class QuestionBank - { - public SubjectList Subject { get; set; } - - public ObservableCollection QuestionBankPapers { get; set; } - - public QuestionBank() - { } - - public QuestionBank(SubjectList subject) - { - Subject = subject; - } - - public void LoadTopicFromCSV(string csvFile) - { - } - } -} \ No newline at end of file diff --git a/src/Symptum.Core/Subjects/QuestionBank/QuestionBankManager.cs b/src/Symptum.Core/Subjects/QuestionBank/QuestionBankManager.cs deleted file mode 100644 index 06be6b1..0000000 --- a/src/Symptum.Core/Subjects/QuestionBank/QuestionBankManager.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Symptum.Core.Subjects.QuestionBank -{ - public class QuestionBankManager - { - public QuestionBankManager() - { } - } -} \ No newline at end of file diff --git a/src/Symptum.Core/Subjects/QuestionBank/QuestionBankPaper.cs b/src/Symptum.Core/Subjects/QuestionBank/QuestionBankPaper.cs deleted file mode 100644 index 8decaa9..0000000 --- a/src/Symptum.Core/Subjects/QuestionBank/QuestionBankPaper.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Collections.ObjectModel; - -namespace Symptum.Core.Subjects.QuestionBank -{ - public class QuestionBankPaper - { - public QuestionBankPaper() - { } - - public QuestionBankPaper(string paperName) - { - PaperName = paperName; - } - - public string PaperName { get; set; } - - public ObservableCollection Topics { get; set; } - } -} \ No newline at end of file diff --git a/src/Symptum.Core/Subjects/QuestionBank/QuestionBankTopic.cs b/src/Symptum.Core/Subjects/QuestionBank/QuestionBankTopic.cs deleted file mode 100644 index 2739dbe..0000000 --- a/src/Symptum.Core/Subjects/QuestionBank/QuestionBankTopic.cs +++ /dev/null @@ -1,66 +0,0 @@ -using CommunityToolkit.Mvvm.ComponentModel; -using CsvHelper; -using System.Collections.ObjectModel; -using System.Globalization; -using System.Text.Json.Serialization; - -namespace Symptum.Core.Subjects.QuestionBank -{ - public class QuestionBankTopic : ObservableObject - { - #region Properties - - private string topicName = string.Empty; - - public string TopicName - { - get => topicName; - set => SetProperty(ref topicName, value); - } - - private ObservableCollection questionEntries; - - [JsonIgnore] - public ObservableCollection QuestionEntries - { - get => questionEntries; - set => SetProperty(ref questionEntries, value); - } - - #endregion - - public QuestionBankTopic() - { } - - public QuestionBankTopic(string topicName) - { - TopicName = topicName; - } - - public string ToCSV() - { - using var writer = new StringWriter(); - using var csv = new CsvWriter(writer, CultureInfo.InvariantCulture); - csv.WriteHeader(); - csv.NextRecord(); - foreach (var entry in QuestionEntries) - { - csv.WriteRecord(entry); - csv.NextRecord(); - } - return writer.ToString(); - } - - public static QuestionBankTopic CreateTopicFromCSV(string topicName, string csv) - { - if (string.IsNullOrEmpty(csv)) return null; - - QuestionBankTopic topic = new(topicName); - using var reader = new StringReader(csv); - using var csvReader = new CsvReader(reader, CultureInfo.InvariantCulture); - topic.QuestionEntries = new(csvReader.GetRecords().ToList()); - - return topic; - } - } -} \ No newline at end of file diff --git a/src/Symptum.Core/Subjects/QuestionBank/QuestionEntry.cs b/src/Symptum.Core/Subjects/QuestionBank/QuestionEntry.cs deleted file mode 100644 index 30c26e3..0000000 --- a/src/Symptum.Core/Subjects/QuestionBank/QuestionEntry.cs +++ /dev/null @@ -1,128 +0,0 @@ -using CommunityToolkit.Mvvm.ComponentModel; -using CsvHelper.Configuration.Attributes; -using CsvHelper.TypeConversion; -using Symptum.Core.Subjects.Books; -using Symptum.Core.TypeConversion; - -namespace Symptum.Core.Subjects.QuestionBank -{ - public class QuestionEntry : ObservableObject - { - #region Properties - - private QuestionId? id; - - [TypeConverter(typeof(QuestionIdConverter))] - public QuestionId? Id - { - get => id; - set => SetProperty(ref id, value); - } - - private string title = string.Empty; - - public string Title - { - get => title; - set => SetProperty(ref title, value); - } - - private List descriptions; - - [TypeConverter(typeof(StringListConverter))] - public List Descriptions - { - get => descriptions; - set => SetProperty(ref descriptions, 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 yearsAsked; - - [TypeConverter(typeof(DateOnlyListConverter))] - public List YearsAsked - { - get => yearsAsked; - set => SetProperty(ref yearsAsked, value); - } - - private List bookLocations; - - [TypeConverter(typeof(BookLocationListConverter))] - public List BookLocations - { - get => bookLocations; - set => SetProperty(ref bookLocations, value); - } - - private List probableCases; - - [TypeConverter(typeof(StringListConverter))] - public List ProbableCases - { - get => probableCases; - set => SetProperty(ref probableCases, value); - } - - private List referenceLinks; - - [TypeConverter(typeof(UriListConverter))] - public List ReferenceLinks - { - get => referenceLinks; - set => SetProperty(ref referenceLinks, value); - } - - #endregion - - public QuestionEntry() - { - } - - public QuestionEntry Clone() - { - return new QuestionEntry() - { - Id = new() { QuestionType = id.QuestionType, SubjectCode = id.SubjectCode, CompetencyNumbers = id.CompetencyNumbers }, - Title = Title, - Descriptions = CloneList(Descriptions), - 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 CloneList(List values, Func? function = null) - { - List results = []; - if (values != null) - { - foreach (T item in values) - { - T result = function != null ? function(item) : item; - results.Add(result); - } - } - - return results; - } - } -} \ No newline at end of file diff --git a/src/Symptum.Core/Subjects/QuestionBank/QuestionId.cs b/src/Symptum.Core/Subjects/QuestionBank/QuestionId.cs deleted file mode 100644 index 7e2df8d..0000000 --- a/src/Symptum.Core/Subjects/QuestionBank/QuestionId.cs +++ /dev/null @@ -1,111 +0,0 @@ -using CommunityToolkit.Mvvm.ComponentModel; -using Symptum.Core.Helpers; -using System.Text; - -namespace Symptum.Core.Subjects.QuestionBank -{ - /// - /// Represents a wrapper class for the unique identifier of a question entry.
- /// The id is in the format of "QT_SC_CN", where "QT" = , "SC" = Subject Code(), "CN" = Competency Number of the question. - ///
- public class QuestionId : ObservableObject - { - private static Dictionary questionTypes = new() - { - { "E", QuestionType.Essay }, - { "S", QuestionType.ShortNote }, - { "M", QuestionType.MCQ } - }; - - #region Properties - - private QuestionType questionType; - - public QuestionType QuestionType - { - get => questionType; - set - { - if (SetProperty(ref questionType, value)) - UpdateIdString(); - } - } - - private SubjectList subjectCode; - - public SubjectList SubjectCode - { - get => subjectCode; - set - { - if (SetProperty(ref subjectCode, value)) - UpdateIdString(); - } - } - - private string competencyNumbers = string.Empty; - - public string CompetencyNumbers - { - get => competencyNumbers; - set - { - if (SetProperty(ref competencyNumbers, value)) - UpdateIdString(); - } - } - - private string idString = string.Empty; - - public string IdString - { - get => idString; - private set => SetProperty(ref idString, value); - } - - #endregion - - public QuestionId() - { - UpdateIdString(); - } - - public static QuestionId? Parse(string? idText) - { - QuestionId questionId = new(); - - if (string.IsNullOrEmpty(idText)) return questionId; - - var values = idText.Split(ParserHelper.QuestionIdDelimiter); - if (values.Length == 3) - { - if (questionTypes.TryGetValue(values[0], out QuestionType type)) - { - questionId.QuestionType = type; - } - if (SubjectMap.SubjectCodes.TryGetValue(values[1], out SubjectList subject)) - { - questionId.SubjectCode = subject; - } - - questionId.CompetencyNumbers = values[2]; - } - - return questionId; - } - - private void UpdateIdString() - { - IdString = ToString(); - } - - public override string ToString() - { - StringBuilder sb = new(); - string qt = questionTypes.FirstOrDefault(x => x.Value == questionType).Key; - string sc = SubjectMap.SubjectCodes.FirstOrDefault(x => x.Value == subjectCode).Key; - sb.Append(qt).Append(ParserHelper.QuestionIdDelimiter).Append(sc).Append(ParserHelper.QuestionIdDelimiter).Append(competencyNumbers); - return sb.ToString(); - } - } -} \ No newline at end of file diff --git a/src/Symptum.Core/Subjects/QuestionBank/QuestionType.cs b/src/Symptum.Core/Subjects/QuestionBank/QuestionType.cs deleted file mode 100644 index b242ebe..0000000 --- a/src/Symptum.Core/Subjects/QuestionBank/QuestionType.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Symptum.Core.Subjects.QuestionBank -{ - public enum QuestionType - { - Essay, - ShortNote, - MCQ - } -} \ No newline at end of file diff --git a/src/Symptum.Core/Subjects/QuestionBanks/QuestionBank.cs b/src/Symptum.Core/Subjects/QuestionBanks/QuestionBank.cs new file mode 100644 index 0000000..68c4980 --- /dev/null +++ b/src/Symptum.Core/Subjects/QuestionBanks/QuestionBank.cs @@ -0,0 +1,45 @@ +using System.Collections.ObjectModel; +using Symptum.Core.Management.Resources; + +namespace Symptum.Core.Subjects.QuestionBanks; + +public class QuestionBank : NavigableResource +{ + public QuestionBank() + { } + + #region Properties + + private SubjectList _subjectCode; + + public SubjectList SubjectCode + { + get => _subjectCode; + set => SetProperty(ref _subjectCode, value); + } + + private ObservableCollection questionBankPapers; + + public ObservableCollection QuestionBankPapers + { + get => questionBankPapers; + set + { + UnobserveCollection(questionBankPapers); + SetProperty(ref questionBankPapers, value); + ObserveCollection(questionBankPapers); + } + } + + #endregion + + protected override void OnInitializeResource(IResource? parent) + { + SetChildrenResources(questionBankPapers); + } + + public override bool CanHandleChildResourceType(Type childResourceType) + { + return childResourceType == typeof(QuestionBankPaper); + } +} diff --git a/src/Symptum.Core/Subjects/QuestionBanks/QuestionBankManager.cs b/src/Symptum.Core/Subjects/QuestionBanks/QuestionBankManager.cs new file mode 100644 index 0000000..14966b1 --- /dev/null +++ b/src/Symptum.Core/Subjects/QuestionBanks/QuestionBankManager.cs @@ -0,0 +1,8 @@ +namespace Symptum.Core.Subjects.QuestionBanks; + +public class QuestionBankManager +{ + public QuestionBankManager() + { + } +} \ No newline at end of file diff --git a/src/Symptum.Core/Subjects/QuestionBanks/QuestionBankPaper.cs b/src/Symptum.Core/Subjects/QuestionBanks/QuestionBankPaper.cs new file mode 100644 index 0000000..3b2498f --- /dev/null +++ b/src/Symptum.Core/Subjects/QuestionBanks/QuestionBankPaper.cs @@ -0,0 +1,42 @@ +using System.Collections.ObjectModel; +using Symptum.Core.Management.Resources; + +namespace Symptum.Core.Subjects.QuestionBanks; + +public class QuestionBankPaper : NavigableResource +{ + public QuestionBankPaper() + { } + + public QuestionBankPaper(string title) + { + Title = title; + } + + #region Properties + + private ObservableCollection topics; + + public ObservableCollection Topics + { + get => topics; + set + { + UnobserveCollection(topics); + SetProperty(ref topics, value); + ObserveCollection(topics); + } + } + + #endregion + + protected override void OnInitializeResource(IResource? parent) + { + SetChildrenResources(topics); + } + + public override bool CanHandleChildResourceType(Type childResourceType) + { + return childResourceType == typeof(QuestionBankTopic); + } +} diff --git a/src/Symptum.Core/Subjects/QuestionBanks/QuestionBankTopic.cs b/src/Symptum.Core/Subjects/QuestionBanks/QuestionBankTopic.cs new file mode 100644 index 0000000..bd35cb4 --- /dev/null +++ b/src/Symptum.Core/Subjects/QuestionBanks/QuestionBankTopic.cs @@ -0,0 +1,81 @@ +using System.Collections.ObjectModel; +using System.Globalization; +using System.Text.Json.Serialization; +using CsvHelper; +using Symptum.Core.Management.Resources; + +namespace Symptum.Core.Subjects.QuestionBanks; + +public class QuestionBankTopic : NavigableResource +{ + public QuestionBankTopic() + { } + + public QuestionBankTopic(string title) + { + Title = title; + } + + #region Properties + + private ObservableCollection? questionEntries; + + [JsonIgnore] + public ObservableCollection? QuestionEntries + { + get => questionEntries; + set => SetProperty(ref questionEntries, value); + } + + #endregion + + protected override void OnInitializeResource(IResource? parent) + { + ChildrenResources = null; + } + + public override bool CanHandleChildResourceType(Type childResourceType) + { + return false; + } + + public string ToCSV() + { + using var writer = new StringWriter(); + using var csv = new CsvWriter(writer, CultureInfo.InvariantCulture); + csv.WriteHeader(); + csv.NextRecord(); + if (QuestionEntries != null) + { + foreach (var entry in QuestionEntries) + { + csv.WriteRecord(entry); + csv.NextRecord(); + } + } + + return writer.ToString(); + } + + public static QuestionBankTopic CreateTopicFromCSV(string topicName, string csv) + { + if (string.IsNullOrEmpty(csv)) return null; + + QuestionBankTopic topic = new(topicName) + { + QuestionEntries = LoadQuestionEntriesFromCSV(csv) + }; + + return topic; + } + + public static ObservableCollection? LoadQuestionEntriesFromCSV(string csv) + { + if (string.IsNullOrEmpty(csv)) return null; + + using var reader = new StringReader(csv); + using var csvReader = new CsvReader(reader, CultureInfo.InvariantCulture); + ObservableCollection questionEntries = new(csvReader.GetRecords().ToList()); + return questionEntries; + } +} diff --git a/src/Symptum.Core/Subjects/QuestionBanks/QuestionEntry.cs b/src/Symptum.Core/Subjects/QuestionBanks/QuestionEntry.cs new file mode 100644 index 0000000..7badb8a --- /dev/null +++ b/src/Symptum.Core/Subjects/QuestionBanks/QuestionEntry.cs @@ -0,0 +1,161 @@ +using CommunityToolkit.Mvvm.ComponentModel; +using CsvHelper.Configuration.Attributes; +using CsvHelper.TypeConversion; +using Symptum.Core.Subjects.Books; +using Symptum.Core.TypeConversion; + +namespace Symptum.Core.Subjects.QuestionBanks; + +public class QuestionEntry : ObservableObject, IComparable, IComparable +{ + #region Properties + + private QuestionId? id; + + [TypeConverter(typeof(QuestionIdConverter))] + public QuestionId? Id + { + get => id; + set => SetProperty(ref id, value); + } + + private string title = string.Empty; + + public string Title + { + get => title; + set => SetProperty(ref title, value); + } + + private List? descriptions; + + [TypeConverter(typeof(StringListConverter))] + public List? Descriptions + { + get => descriptions; + set => SetProperty(ref descriptions, 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? yearsAsked; + + [TypeConverter(typeof(DateOnlyListConverter))] + public List? YearsAsked + { + get => yearsAsked; + set => SetProperty(ref yearsAsked, value); + } + + private List? bookLocations; + + [TypeConverter(typeof(BookLocationListConverter))] + public List? BookLocations + { + get => bookLocations; + set => SetProperty(ref bookLocations, value); + } + + private List? probableCases; + + [TypeConverter(typeof(StringListConverter))] + public List? ProbableCases + { + get => probableCases; + set => SetProperty(ref probableCases, value); + } + + private List? referenceLinks; + + [TypeConverter(typeof(UriListConverter))] + public List? ReferenceLinks + { + get => referenceLinks; + set => SetProperty(ref referenceLinks, value); + } + + #endregion + + public QuestionEntry() + { + } + + public QuestionEntry Clone() + { + return new QuestionEntry() + { + Id = new() { QuestionType = id.QuestionType, SubjectCode = id.SubjectCode, CompetencyNumbers = id.CompetencyNumbers }, + Title = Title, + Descriptions = CloneList(Descriptions), + 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 CloneList(List? values, Func? function = null) + { + List results = []; + if (values != null) + { + foreach (T item in values) + { + T result = function != null ? function(item) : item; + results.Add(result); + } + } + + return results; + } + + public int CompareTo(QuestionEntry? other) + { + if (ReferenceEquals(this, other)) + return 0; + else if (other == null) + return 1; + + // Compare the QuestionType first + int? cmp = Id?.QuestionType.CompareTo(other?.Id?.QuestionType); + if (cmp != null && cmp != 0) + return cmp.Value; + + // Then Importance is compared + cmp = Importance.CompareTo(other?.Importance); + if (cmp != null && cmp != 0) + return -cmp.Value; + + // Then we compare questions with same Importance and QuestionType with Title + cmp = Title.CompareTo(other?.Title); + if (cmp != null && cmp != 0) + return cmp.Value; + + return 0; + } + + public int CompareTo(object? obj) + { + if (obj != null && obj.GetType() != GetType()) + { + throw new ArgumentException(string.Format("Object must be of type {0}", GetType())); + } + return CompareTo(obj as QuestionEntry); + } +} diff --git a/src/Symptum.Core/Subjects/QuestionBanks/QuestionId.cs b/src/Symptum.Core/Subjects/QuestionBanks/QuestionId.cs new file mode 100644 index 0000000..b955b1a --- /dev/null +++ b/src/Symptum.Core/Subjects/QuestionBanks/QuestionId.cs @@ -0,0 +1,128 @@ +using System.Text; +using CommunityToolkit.Mvvm.ComponentModel; +using Symptum.Core.Helpers; + +namespace Symptum.Core.Subjects.QuestionBanks; + +/// +/// Represents a wrapper class for the unique identifier of a question entry.
+/// The id is in the format of "QT_SC_CN", where "QT" = , "SC" = Subject Code(), "CN" = Competency Number of the question. +///
+public class QuestionId : ObservableObject, IUniqueId, IEquatable +{ + private static Dictionary questionTypes = new() + { + { "E", QuestionType.Essay }, + { "S", QuestionType.ShortNote }, + { "M", QuestionType.MCQ } + }; + + #region Properties + + private QuestionType questionType; + + public QuestionType QuestionType + { + get => questionType; + set + { + if (SetProperty(ref questionType, value)) + UpdateIdString(); + } + } + + private SubjectList subjectCode; + + public SubjectList SubjectCode + { + get => subjectCode; + set + { + if (SetProperty(ref subjectCode, value)) + UpdateIdString(); + } + } + + private string competencyNumbers = string.Empty; + + public string CompetencyNumbers + { + get => competencyNumbers; + set + { + if (SetProperty(ref competencyNumbers, value)) + UpdateIdString(); + } + } + + private string idString = string.Empty; + + public string IdString + { + get => idString; + private set => SetProperty(ref idString, value); + } + + #endregion + + public QuestionId() + { + UpdateIdString(); + } + + static IUniqueId? IUniqueId.Parse(string? idText) => Parse(idText); + + public static QuestionId? Parse(string? idText) + { + QuestionId questionId = new(); + + if (string.IsNullOrEmpty(idText)) return questionId; + + var values = idText.Split(ParserHelper.QuestionIdDelimiter); + if (values.Length == 3) + { + if (questionTypes.TryGetValue(values[0], out QuestionType type)) + { + questionId.QuestionType = type; + } + if (SubjectMap.SubjectCodes.TryGetValue(values[1], out SubjectList subject)) + { + questionId.SubjectCode = subject; + } + + questionId.CompetencyNumbers = values[2]; + } + + return questionId; + } + + private void UpdateIdString() + { + IdString = ToString(); + } + + public override string ToString() + { + StringBuilder sb = new(); + string qt = questionTypes.FirstOrDefault(x => x.Value == questionType).Key; + string sc = SubjectMap.SubjectCodes.FirstOrDefault(x => x.Value == subjectCode).Key; + sb.Append(qt).Append(ParserHelper.QuestionIdDelimiter).Append(sc).Append(ParserHelper.QuestionIdDelimiter).Append(competencyNumbers); + return sb.ToString(); + } + + public bool Equals(QuestionId? other) + { + if (other == null) + return false; + + return other.QuestionType == QuestionType && other.SubjectCode == SubjectCode && other.CompetencyNumbers == CompetencyNumbers; + } + + public override bool Equals(object? obj) + { + if (obj == null) + return false; + + return Equals(obj as QuestionId); + } +} diff --git a/src/Symptum.Core/Subjects/QuestionBanks/QuestionType.cs b/src/Symptum.Core/Subjects/QuestionBanks/QuestionType.cs new file mode 100644 index 0000000..7744c27 --- /dev/null +++ b/src/Symptum.Core/Subjects/QuestionBanks/QuestionType.cs @@ -0,0 +1,8 @@ +namespace Symptum.Core.Subjects.QuestionBanks; + +public enum QuestionType +{ + Essay, + ShortNote, + MCQ +} \ No newline at end of file diff --git a/src/Symptum.Core/Subjects/Subject.cs b/src/Symptum.Core/Subjects/Subject.cs new file mode 100644 index 0000000..ce65a9f --- /dev/null +++ b/src/Symptum.Core/Subjects/Subject.cs @@ -0,0 +1,69 @@ +using Symptum.Core.Management.Resources; +using Symptum.Core.Subjects.QuestionBanks; + +namespace Symptum.Core.Subjects; + +public class Subject : PackageResource +{ + public Subject() + { } + + public Subject(SubjectList subjectCode) + { + SubjectCode = subjectCode; + } + + #region Properties + + private SubjectList _subjectCode; + + public SubjectList SubjectCode + { + get => _subjectCode; + set => SetProperty(ref _subjectCode, value); + } + + private QuestionBank questionBank; + + public QuestionBank QuestionBank + { + get => questionBank; + set + { + UpdateChildrenResources(value); + SetProperty(ref questionBank, value); + } + } + + #endregion + + protected override void OnInitializeResource(IResource? parent) + { + ChildrenResources = [questionBank]; + foreach (IResource resource in ChildrenResources) + { + resource.InitializeResource(this); + } + } + + public override bool CanHandleChildResourceType(Type childResourceType) + { + return childResourceType == typeof(QuestionBank); + } + + private void UpdateChildrenResources(QuestionBank value) + { + if (ChildrenResources != null) + { + if (value != null) + { + if (ChildrenResources.Contains(questionBank)) + ChildrenResources.Remove(questionBank); + + ChildrenResources.Add(value); + } + else + ChildrenResources.Remove(questionBank); + } + } +} \ No newline at end of file diff --git a/src/Symptum.Core/Subjects/SubjectList.cs b/src/Symptum.Core/Subjects/SubjectList.cs index 7388e13..d386541 100644 --- a/src/Symptum.Core/Subjects/SubjectList.cs +++ b/src/Symptum.Core/Subjects/SubjectList.cs @@ -1,20 +1,19 @@ -namespace Symptum.Core.Subjects +namespace Symptum.Core.Subjects; + +public enum SubjectList { - public enum SubjectList - { - Anatomy, - Physiology, - Biochemistry, - Pharmacology, - Pathology, - Microbiology, - OtoRhinoLaryngology, - Ophthalmology, - ForensicMedicine, - CommunityMedicine, - GeneralMedicine, - GeneralSurgery, - Pediatrics, - ObstetricsAndGynaecology - } + Anatomy, + Physiology, + Biochemistry, + Pharmacology, + Pathology, + Microbiology, + OtoRhinoLaryngology, + Ophthalmology, + ForensicMedicine, + CommunityMedicine, + GeneralMedicine, + GeneralSurgery, + Pediatrics, + ObstetricsAndGynaecology } \ No newline at end of file diff --git a/src/Symptum.Core/Subjects/SubjectMap.cs b/src/Symptum.Core/Subjects/SubjectMap.cs index 2cb25af..65f592f 100644 --- a/src/Symptum.Core/Subjects/SubjectMap.cs +++ b/src/Symptum.Core/Subjects/SubjectMap.cs @@ -1,35 +1,34 @@ using System.Collections.ObjectModel; -namespace Symptum.Core.Subjects +namespace Symptum.Core.Subjects; + +public class SubjectMap { - public class SubjectMap - { - public SubjectMap() - { } + public SubjectMap() + { } - static SubjectMap() + static SubjectMap() + { + Dictionary codes = new() { - Dictionary codes = new() - { - { "AN", SubjectList.Anatomy }, - { "PY", SubjectList.Physiology }, - { "BI", SubjectList.Biochemistry }, - { "PH", SubjectList.Pharmacology }, - { "PA", SubjectList.Pathology }, - { "MI", SubjectList.Microbiology }, - { "EN", SubjectList.OtoRhinoLaryngology }, - { "OP", SubjectList.Ophthalmology }, - { "FM", SubjectList.ForensicMedicine }, - { "CM", SubjectList.CommunityMedicine }, - { "IM", SubjectList.GeneralMedicine }, - { "SU", SubjectList.GeneralSurgery }, - { "PE", SubjectList.Pediatrics }, - { "OG", SubjectList.ObstetricsAndGynaecology }, - }; + { "AN", SubjectList.Anatomy }, + { "PY", SubjectList.Physiology }, + { "BI", SubjectList.Biochemistry }, + { "PH", SubjectList.Pharmacology }, + { "PA", SubjectList.Pathology }, + { "MI", SubjectList.Microbiology }, + { "EN", SubjectList.OtoRhinoLaryngology }, + { "OP", SubjectList.Ophthalmology }, + { "FM", SubjectList.ForensicMedicine }, + { "CM", SubjectList.CommunityMedicine }, + { "IM", SubjectList.GeneralMedicine }, + { "SU", SubjectList.GeneralSurgery }, + { "PE", SubjectList.Pediatrics }, + { "OG", SubjectList.ObstetricsAndGynaecology }, + }; - SubjectCodes = new(codes); - } - - public static readonly ReadOnlyDictionary SubjectCodes; + SubjectCodes = new(codes); } + + public static readonly ReadOnlyDictionary SubjectCodes; } \ No newline at end of file diff --git a/src/Symptum.Core/TypeConversion/Converters.cs b/src/Symptum.Core/TypeConversion/Converters.cs index 6c61ccd..525ab85 100644 --- a/src/Symptum.Core/TypeConversion/Converters.cs +++ b/src/Symptum.Core/TypeConversion/Converters.cs @@ -1,147 +1,146 @@ -using CsvHelper; +using System.Text; +using CsvHelper; using CsvHelper.Configuration; using CsvHelper.TypeConversion; using Symptum.Core.Helpers; using Symptum.Core.Subjects.Books; -using Symptum.Core.Subjects.QuestionBank; -using System.Text; - -namespace Symptum.Core.TypeConversion -{ - #region Question Bank +using Symptum.Core.Subjects.QuestionBanks; - // These are not named as "...ToStringConverter" because they are used for CSV and are essentially converting to and from string. - // Another reason is to differentiate them from XAML Type Converters used for Data Binding. Where there is a convention of adding "...ToStringConverter" to their names. +namespace Symptum.Core.TypeConversion; - public class QuestionIdConverter : DefaultTypeConverter - { - public override object? ConvertFromString(string? text, IReaderRow row, MemberMapData memberMapData) - { - return QuestionId.Parse(text); - } +#region Question Bank - public override string? ConvertToString(object? value, IWriterRow row, MemberMapData memberMapData) - { - if (value is QuestionId id) - return id.ToString(); - else return string.Empty; - } - } +// These are not named as "...ToStringConverter" because they are used for CSV and are essentially converting to and from string. +// Another reason is to differentiate them from XAML Type Converters used for Data Binding. Where there is a convention of adding "...ToStringConverter" to their names. - public class DateOnlyListConverter : ListConverter +public class QuestionIdConverter : DefaultTypeConverter +{ + public override object? ConvertFromString(string? text, IReaderRow row, MemberMapData memberMapData) { - public override void ValidateData(string text, List list) => ListToStringConversion.ValidateDataForDate(text, list); - - public override string ElementToString(DateOnly data) => ListToStringConversion.ElementToStringForDateOnly(data); + return QuestionId.Parse(text); } - public class UriListConverter : ListConverter + public override string? ConvertToString(object? value, IWriterRow row, MemberMapData memberMapData) { - public override void ValidateData(string text, List list) => ListToStringConversion.ValidateDataForUri(text, list); + if (value is QuestionId id) + return id.ToString(); + else return string.Empty; } +} - public class StringListConverter : ListConverter - { - public override void ValidateData(string text, List list) => ListToStringConversion.ValidateDataForString(text, list); - } +public class DateOnlyListConverter : ListConverter +{ + public override void ValidateData(string text, List list) => ListToStringConversion.ValidateDataForDate(text, list); - public class BookLocationListConverter : ListConverter - { - public override void ValidateData(string text, List list) => ListToStringConversion.ValidateDataForBookLocation(text, list); - } + public override string ElementToString(DateOnly data) => ListToStringConversion.ElementToStringForDateOnly(data); +} - #endregion +public class UriListConverter : ListConverter +{ + public override void ValidateData(string text, List list) => ListToStringConversion.ValidateDataForUri(text, list); +} - public class ListToStringConversion - { - public static List? ConvertFromString(string? text, Action> validateData) - { - if (string.IsNullOrEmpty(text)) return null; +public class StringListConverter : ListConverter +{ + public override void ValidateData(string text, List list) => ListToStringConversion.ValidateDataForString(text, list); +} - List list = new(); +public class BookLocationListConverter : ListConverter +{ + public override void ValidateData(string text, List list) => ListToStringConversion.ValidateDataForBookLocation(text, list); +} - string[] values = text.Split(ParserHelper.ListDelimiter); +#endregion - foreach (var value in values) - { - validateData(value, list); - } +public class ListToStringConversion +{ + public static List? ConvertFromString(string? text, Action> validateData) + { + if (string.IsNullOrEmpty(text)) return null; - return list; - } + List list = []; + + string[] values = text.Split(ParserHelper.ListDelimiter); - public static string? ConvertToString(object? value, Func elementToString) + foreach (var value in values) { - var stringBuilder = new StringBuilder(); + validateData(value, list); + } - if (value is List values) - { - for (int i = 0; i < values.Count; i++) - { - var data = values[i]; - stringBuilder.Append(elementToString(data)); - if (i < values.Count - 1) stringBuilder.Append(ParserHelper.ListDelimiter); - } - } + return list; + } - return stringBuilder.ToString(); - } + public static string? ConvertToString(object? value, Func elementToString) + { + var stringBuilder = new StringBuilder(); - public static void ValidateDataForDate(string text, List list) + if (value is List values) { - if (DateOnly.TryParse(text, out DateOnly year)) + for (int i = 0; i < values.Count; i++) { - list.Add(year); + var data = values[i]; + stringBuilder.Append(elementToString(data)); + if (i < values.Count - 1) stringBuilder.Append(ParserHelper.ListDelimiter); } } - public static void ValidateDataForUri(string text, List list) - { - if (!string.IsNullOrEmpty(text)) - { - list.Add(new Uri(text)); - } - } + return stringBuilder.ToString(); + } - public static void ValidateDataForString(string text, List list) + public static void ValidateDataForDate(string text, List list) + { + if (DateOnly.TryParse(text, out DateOnly year)) { - if (!string.IsNullOrEmpty(text)) - { - list.Add(text); - } + list.Add(year); } + } - public static void ValidateDataForBookLocation(string text, List list) + public static void ValidateDataForUri(string text, List list) + { + if (!string.IsNullOrEmpty(text)) { - if (BookLocation.TryParse(text, out BookLocation? location)) - { - list.Add(location); - } + list.Add(new Uri(text)); } + } - public static string ElementToStringForDateOnly(DateOnly data) + public static void ValidateDataForString(string text, List list) + { + if (!string.IsNullOrEmpty(text)) { - return data.ToString("yyyy-MM"); + list.Add(text); } - - public static string ElementToStringDefault(T? data) => data?.ToString() ?? string.Empty; } - public class ListConverter : DefaultTypeConverter + public static void ValidateDataForBookLocation(string text, List list) { - public override object? ConvertFromString(string? text, IReaderRow row, MemberMapData memberMapData) + if (BookLocation.TryParse(text, out BookLocation? location)) { - return ListToStringConversion.ConvertFromString(text, ValidateData); + list.Add(location); } + } - public virtual void ValidateData(string text, List list) - { } + public static string ElementToStringForDateOnly(DateOnly data) + { + return data.ToString("yyyy-MM"); + } - public override string? ConvertToString(object? value, IWriterRow row, MemberMapData memberMapData) - { - return ListToStringConversion.ConvertToString(value, ElementToString); - } + public static string ElementToStringDefault(T? data) => data?.ToString() ?? string.Empty; +} - public virtual string ElementToString(T? data) => ListToStringConversion.ElementToStringDefault(data); +public class ListConverter : DefaultTypeConverter +{ + public override object? ConvertFromString(string? text, IReaderRow row, MemberMapData memberMapData) + { + return ListToStringConversion.ConvertFromString(text, ValidateData); } -} \ No newline at end of file + + public virtual void ValidateData(string text, List list) + { } + + public override string? ConvertToString(object? value, IWriterRow row, MemberMapData memberMapData) + { + return ListToStringConversion.ConvertToString(value, ElementToString); + } + + public virtual string ElementToString(T? data) => ListToStringConversion.ElementToStringDefault(data); +} diff --git a/src/Symptum.Editor/Symptum.Editor.Shared/AppHead.xaml.cs b/src/Symptum.Editor/Symptum.Editor.Shared/AppHead.xaml.cs index 836f8f6..385b974 100644 --- a/src/Symptum.Editor/Symptum.Editor.Shared/AppHead.xaml.cs +++ b/src/Symptum.Editor/Symptum.Editor.Shared/AppHead.xaml.cs @@ -12,7 +12,7 @@ public sealed partial class AppHead : App /// public AppHead() { - this.InitializeComponent(); + InitializeComponent(); } /// diff --git a/src/Symptum.Editor/Symptum.Editor/App.cs b/src/Symptum.Editor/Symptum.Editor/App.cs index c175816..3d5a5bf 100644 --- a/src/Symptum.Editor/Symptum.Editor/App.cs +++ b/src/Symptum.Editor/Symptum.Editor/App.cs @@ -1,9 +1,4 @@ -using System.Diagnostics; -using System.IO; -using Microsoft.UI.Xaml; using Symptum.Core.Subjects.Books; -using Uno.UI.Toolkit; -using Windows.UI.ViewManagement; namespace Symptum.Editor; @@ -35,7 +30,7 @@ protected override void OnLaunched(LaunchActivatedEventArgs args) if (MainWindow.Content is not Frame rootFrame) { // Create a Frame to act as the navigation context and navigate to the first page - rootFrame = new Frame(); + rootFrame = new(); // Place the frame in the current Window MainWindow.Content = rootFrame; @@ -55,7 +50,7 @@ protected override void OnLaunched(LaunchActivatedEventArgs args) MainWindow.Activate(); #if HAS_UNO - Windows.Graphics.Display.DisplayInformation.AutoRotationPreferences = Windows.Graphics.Display.DisplayOrientations.Landscape; + //Windows.Graphics.Display.DisplayInformation.AutoRotationPreferences = Windows.Graphics.Display.DisplayOrientations.Landscape; #endif } diff --git a/src/Symptum.Editor/Symptum.Editor/Common/DefaultIconSources.cs b/src/Symptum.Editor/Symptum.Editor/Common/DefaultIconSources.cs new file mode 100644 index 0000000..b1dbbba --- /dev/null +++ b/src/Symptum.Editor/Symptum.Editor/Common/DefaultIconSources.cs @@ -0,0 +1,6 @@ +namespace Symptum.Editor.Common; + +public static class DefaultIconSources +{ + public static readonly IconSource QuestionBankTopicIconSource = new SymbolIconSource() { Symbol = Symbol.List }; +} diff --git a/src/Symptum.Editor/Symptum.Editor/Controls/AddNewItemDialog.xaml b/src/Symptum.Editor/Symptum.Editor/Controls/AddNewItemDialog.xaml new file mode 100644 index 0000000..cea676d --- /dev/null +++ b/src/Symptum.Editor/Symptum.Editor/Controls/AddNewItemDialog.xaml @@ -0,0 +1,13 @@ + + + + + diff --git a/src/Symptum.Editor/Symptum.Editor/Controls/AddNewItemDialog.xaml.cs b/src/Symptum.Editor/Symptum.Editor/Controls/AddNewItemDialog.xaml.cs new file mode 100644 index 0000000..75ca029 --- /dev/null +++ b/src/Symptum.Editor/Symptum.Editor/Controls/AddNewItemDialog.xaml.cs @@ -0,0 +1,9 @@ +namespace Symptum.Editor.Controls; + +public sealed partial class AddNewItemDialog : ContentDialog +{ + public AddNewItemDialog() + { + InitializeComponent(); + } +} diff --git a/src/Symptum.Editor/Symptum.Editor/Controls/BookLocationPicker.xaml.cs b/src/Symptum.Editor/Symptum.Editor/Controls/BookLocationPicker.xaml.cs index 46ad8aa..b26d73b 100644 --- a/src/Symptum.Editor/Symptum.Editor/Controls/BookLocationPicker.xaml.cs +++ b/src/Symptum.Editor/Symptum.Editor/Controls/BookLocationPicker.xaml.cs @@ -35,7 +35,7 @@ public BookLocation BookLocation public BookLocationPicker() { - this.InitializeComponent(); + InitializeComponent(); bookSelector.ItemsSource = BookStore.Books; bookSelector.TextChanged += bookSelector_TextChanged; bookSelector.SuggestionChosen += bookSelector_SuggestionChosen; diff --git a/src/Symptum.Editor/Symptum.Editor/Controls/EditorResult.cs b/src/Symptum.Editor/Symptum.Editor/Controls/EditorResult.cs new file mode 100644 index 0000000..1560f3d --- /dev/null +++ b/src/Symptum.Editor/Symptum.Editor/Controls/EditorResult.cs @@ -0,0 +1,10 @@ +namespace Symptum.Editor.Controls; + +public enum EditorResult +{ + None, + Create, + Update, + Save, + Cancel +} diff --git a/src/Symptum.Editor/Symptum.Editor/Controls/FindFlyout.xaml b/src/Symptum.Editor/Symptum.Editor/Controls/FindFlyout.xaml index 7e0bf6b..7106db0 100644 --- a/src/Symptum.Editor/Symptum.Editor/Controls/FindFlyout.xaml +++ b/src/Symptum.Editor/Symptum.Editor/Controls/FindFlyout.xaml @@ -7,7 +7,7 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:controls="using:CommunityToolkit.WinUI.UI.Controls" mc:Ignorable="d" AllowFocusOnInteraction="True"> - + @@ -18,6 +18,7 @@ + + diff --git a/src/Symptum.Editor/Symptum.Editor/Controls/FindFlyout.xaml.cs b/src/Symptum.Editor/Symptum.Editor/Controls/FindFlyout.xaml.cs index 418ab83..018cf0e 100644 --- a/src/Symptum.Editor/Symptum.Editor/Controls/FindFlyout.xaml.cs +++ b/src/Symptum.Editor/Symptum.Editor/Controls/FindFlyout.xaml.cs @@ -16,14 +16,15 @@ public sealed partial class FindFlyout : Flyout public FindFlyout() { - this.InitializeComponent(); - this.Opened += FindFlyout_Opened; + InitializeComponent(); + Opened += FindFlyout_Opened; queryBox.ItemsSource = _queries; queryBox.TextChanged += (s, e) => QueryText = queryBox.Text; queryBox.QuerySubmitted += QueryBox_QuerySubmitted; fNextButton.Click += (s, e) => Find(FindDirection.Next); fPrevButton.Click += (s, e) => Find(FindDirection.Previous); fAllButton.Click += (s, e) => Find(FindDirection.All); + fClearButton.Click += (s, e) => Clear(); fContextComboBox.SelectionChanged += (s, e) => { if (e.AddedItems.Count > 0) @@ -37,6 +38,8 @@ public FindFlyout() public event EventHandler QuerySubmitted; + public event EventHandler QueryCleared; + #region Properties #region QueryText @@ -81,9 +84,9 @@ private static void OnFindContextsChanged(DependencyObject d, DependencyProperty } } - public IList FindContexts + public IList? FindContexts { - get => (IList)GetValue(FindContextsProperty); + get => (IList?)GetValue(FindContextsProperty); set => SetValue(FindContextsProperty, value); } @@ -177,17 +180,14 @@ public bool MatchWholeWord #endregion - public void ShowAt(FindOptions findOptions = null, DependencyObject placementTarget = null, FlyoutShowOptions showOptions = null) + public void ShowAt(FindOptions? findOptions = null, DependencyObject? placementTarget = null, FlyoutShowOptions? showOptions = null) { - if (showOptions == null) + showOptions ??= new() { - showOptions = new() - { - Position = new(), - ShowMode = FlyoutShowMode.Standard, - Placement = FlyoutPlacementMode.Bottom - }; - } + Position = new(), + ShowMode = FlyoutShowMode.Standard, + Placement = FlyoutPlacementMode.Bottom + }; if (findOptions != null) { @@ -204,7 +204,7 @@ private void QueryBox_QuerySubmitted(AutoSuggestBox sender, AutoSuggestBoxQueryS if (!string.IsNullOrEmpty(args.QueryText) && !_queries.Contains(args.QueryText)) _queries.Add(args.QueryText); QueryText = args.QueryText; - Find(FindDirection.Next); + Find(FindDirection.All); } private void FindFlyout_Opened(object sender, object e) @@ -214,6 +214,12 @@ private void FindFlyout_Opened(object sender, object e) private void Find(FindDirection findDirection) { + if (string.IsNullOrEmpty(QueryText) || string.IsNullOrWhiteSpace(QueryText)) + { + Clear(); + return; + } + FindFlyoutQuerySubmittedEventArgs args = new() { Context = SelectedContext, @@ -226,6 +232,12 @@ private void Find(FindDirection findDirection) RaiseQuerySubmitted(args); } + private void Clear() + { + QueryText = string.Empty; + QueryCleared?.Invoke(this, new()); + } + private void RaiseQuerySubmitted(FindFlyoutQuerySubmittedEventArgs e) { QuerySubmitted?.Invoke(this, e); @@ -253,7 +265,7 @@ public class FindOptions : ObservableObject { public string InitialQueryText { get; init; } = string.Empty; - public IList Contexts { get; init; } = null; + public IList? Contexts { get; init; } = null; public bool MatchCase { get; init; } = false; diff --git a/src/Symptum.Editor/Symptum.Editor/Controls/ListEditorControl.xaml b/src/Symptum.Editor/Symptum.Editor/Controls/ListEditorControl.xaml index 1feb535..09f809b 100644 --- a/src/Symptum.Editor/Symptum.Editor/Controls/ListEditorControl.xaml +++ b/src/Symptum.Editor/Symptum.Editor/Controls/ListEditorControl.xaml @@ -7,8 +7,8 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> - - + + diff --git a/src/Symptum.Editor/Symptum.Editor/Controls/ListEditorControl.xaml.cs b/src/Symptum.Editor/Symptum.Editor/Controls/ListEditorControl.xaml.cs index 04c094f..0b04589 100644 --- a/src/Symptum.Editor/Symptum.Editor/Controls/ListEditorControl.xaml.cs +++ b/src/Symptum.Editor/Symptum.Editor/Controls/ListEditorControl.xaml.cs @@ -28,7 +28,7 @@ public DataTemplate ItemTemplate public ListEditorControl() { - this.InitializeComponent(); + InitializeComponent(); listView.ItemsSource = itemsSource; listView.ItemTemplate = itemTemplate; } diff --git a/src/Symptum.Editor/Symptum.Editor/Controls/QuestionEditorDialog.xaml b/src/Symptum.Editor/Symptum.Editor/Controls/QuestionEditorDialog.xaml index 5c82130..f0bfaf6 100644 --- a/src/Symptum.Editor/Symptum.Editor/Controls/QuestionEditorDialog.xaml +++ b/src/Symptum.Editor/Symptum.Editor/Controls/QuestionEditorDialog.xaml @@ -10,7 +10,7 @@ xmlns:not_android="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:android="http://uno.ui/android" mc:Ignorable="d android" - PrimaryButtonText="Save" CloseButtonText="Cancel" PrimaryButtonStyle="{ThemeResource AccentButtonStyle}"> + CloseButtonText="Cancel" PrimaryButtonStyle="{ThemeResource AccentButtonStyle}"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Symptum.Editor/Symptum.Editor.Shared/Splash/splash_screen.svg b/src/Symptum.Editor/Symptum.Editor.Shared/Splash/splash_screen.svg index 9a7f5b1..a005f51 100644 --- a/src/Symptum.Editor/Symptum.Editor.Shared/Splash/splash_screen.svg +++ b/src/Symptum.Editor/Symptum.Editor.Shared/Splash/splash_screen.svg @@ -1,4 +1,107 @@ - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Symptum.Editor/Symptum.Editor.Shared/base.props b/src/Symptum.Editor/Symptum.Editor.Shared/base.props index cd24128..cfadf12 100644 --- a/src/Symptum.Editor/Symptum.Editor.Shared/base.props +++ b/src/Symptum.Editor/Symptum.Editor.Shared/base.props @@ -16,11 +16,11 @@ Link="AppHead.xaml.cs" /> diff --git a/src/Symptum.Editor/Symptum.Editor/App.cs b/src/Symptum.Editor/Symptum.Editor/App.cs index 3d5a5bf..349795f 100644 --- a/src/Symptum.Editor/Symptum.Editor/App.cs +++ b/src/Symptum.Editor/Symptum.Editor/App.cs @@ -64,6 +64,9 @@ private async Task LoadAllBookListsAsync() file = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Assets/Books/Second Year Books.csv")); content = await FileIO.ReadTextAsync(file); BookStore.LoadBooks(content); + file = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Assets/Books/Third Year Books.csv")); + content = await FileIO.ReadTextAsync(file); + BookStore.LoadBooks(content); } catch { } } From fd861c1ab1a9b8d361094ff9e19ca75305f951fe Mon Sep 17 00:00:00 2001 From: Shankar Date: Tue, 12 Mar 2024 12:39:15 +0530 Subject: [PATCH 6/7] Bump versions --- .../Symptum.Editor.Mobile/Symptum.Editor.Mobile.csproj | 2 +- src/Symptum.Editor/Symptum.Editor.Windows/Package.appxmanifest | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symptum.Editor/Symptum.Editor.Mobile/Symptum.Editor.Mobile.csproj b/src/Symptum.Editor/Symptum.Editor.Mobile/Symptum.Editor.Mobile.csproj index 31b5e11..4c76156 100644 --- a/src/Symptum.Editor/Symptum.Editor.Mobile/Symptum.Editor.Mobile.csproj +++ b/src/Symptum.Editor/Symptum.Editor.Mobile/Symptum.Editor.Mobile.csproj @@ -10,7 +10,7 @@ com.symptum.editor.dev DF0B4D0E-2900-429B-AE6F-9D00050C448A - 0.0.1 + 0.0.2 1 Android\AndroidManifest.xml diff --git a/src/Symptum.Editor/Symptum.Editor.Windows/Package.appxmanifest b/src/Symptum.Editor/Symptum.Editor.Windows/Package.appxmanifest index 5bef743..9ffbd49 100644 --- a/src/Symptum.Editor/Symptum.Editor.Windows/Package.appxmanifest +++ b/src/Symptum.Editor/Symptum.Editor.Windows/Package.appxmanifest @@ -9,7 +9,7 @@ + Version="0.0.2.0" /> Symptum Editor (Dev) From ef9a3b552cefe3ca2ccf27c03d3a53a1cfc465c4 Mon Sep 17 00:00:00 2001 From: Shankar Date: Tue, 12 Mar 2024 16:25:13 +0530 Subject: [PATCH 7/7] Create LICENSE --- LICENSE | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..cc76468 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) Symptum Community + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE.