Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue9: Applicant details while selecting #48

Merged
merged 8 commits into from
Nov 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions Desktop/Converters/CollapsedIfNull.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Data;

namespace Carmen.Desktop.Converters
{
/// <summary>
/// Convert an object value to a Visibility based on whether or not it is null.
/// If ConverterParameter is set to a type, then the value must be this type to be considered not null.
/// </summary>
public class CollapsedIfNull : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null)
return Visibility.Collapsed;
if (parameter is Type type && !(value.GetType() == type || value.GetType().IsSubclassOf(type)))
return Visibility.Collapsed;
return Visibility.Visible;
}

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
=> throw new NotImplementedException();
}
}
8 changes: 4 additions & 4 deletions Desktop/Pages/SelectCast.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@
</TextBlock>
</Grid>
<ListBox x:Name="selectedList" Grid.Column="0" Grid.Row="1" Grid.RowSpan="7"
SelectionMode="Extended" MouseDoubleClick="selectedList_MouseDoubleClick"
SelectionMode="Extended" MouseDoubleClick="List_MouseDoubleClick"
HorizontalContentAlignment="Stretch"
ItemsSource="{Binding Source={StaticResource selectedApplicantsViewSource}}">
<ListBox.ItemContainerStyle>
Expand Down Expand Up @@ -345,7 +345,7 @@
<TextBlock Grid.Column="2" Text="{Binding Source={StaticResource allApplicantsViewSource}, Path=Count}" Margin="10 0" FontSize="18" VerticalAlignment="Center"/>
</Grid>
<ListBox x:Name="availableList" Grid.Column="2" Grid.Row="1" Grid.RowSpan="7"
SelectionMode="Extended" MouseDoubleClick="availableList_MouseDoubleClick"
SelectionMode="Extended" MouseDoubleClick="List_MouseDoubleClick"
HorizontalContentAlignment="Stretch"
ItemsSource="{Binding Source={StaticResource allApplicantsViewSource}}">
<ListBox.ItemContainerStyle>
Expand Down Expand Up @@ -476,7 +476,7 @@
<Button DockPanel.Dock="Bottom" Height="30" Margin="0 5 0 0" Click="DeleteSameCastSet_Click"
Content="Delete selected set"/>
<ListBox x:Name="selectedSameCastSetList"
SelectionMode="Extended" MouseDoubleClick="selectedSameCastSetList_MouseDoubleClick"
SelectionMode="Extended"
ItemsSource="{Binding ElementName=sameCastSetsList, Path=SelectedItem.Applicants}">
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
Expand Down Expand Up @@ -505,7 +505,7 @@
Text="Available applicants"
Visibility="{Binding ElementName=sameCastSetsList, Path=SelectedItem, Converter={StaticResource hideIfNull}}"/>
<ListBox x:Name="availableSameCastSetList" Grid.Column="4" Grid.Row="2" Grid.RowSpan="7"
SelectionMode="Extended" MouseDoubleClick="availableSameCastSetList_MouseDoubleClick"
SelectionMode="Extended"
ItemsSource="{Binding Source={StaticResource allApplicantsViewSource}}"
Visibility="{Binding ElementName=sameCastSetsList, Path=SelectedItem, Converter={StaticResource hideIfNull}}">
<ListBox.ItemContainerStyle>
Expand Down
92 changes: 61 additions & 31 deletions Desktop/Pages/SelectCast.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,13 @@ public partial class SelectCast : SubPage
private Tag[] tags => _tags
?? throw new ApplicationException($"Tried to used {nameof(tags)} before it was loaded.");

private ISelectionEngine? _engine;
private ISelectionEngine engine => _engine
?? throw new ApplicationException($"Tried to used {nameof(engine)} before it was loaded.");
private IAuditionEngine? _auditionEngine;
private IAuditionEngine auditionEngine => _auditionEngine
?? throw new ApplicationException($"Tried to used {nameof(auditionEngine)} before it was loaded.");

private ISelectionEngine? _selectionEngine;
private ISelectionEngine selectionEngine => _selectionEngine
?? throw new ApplicationException($"Tried to used {nameof(selectionEngine)} before it was loaded.");

private CastList? _castList;
private CastList castList => _castList
Expand Down Expand Up @@ -141,22 +145,22 @@ private async void Page_Loaded(object sender, RoutedEventArgs e)
using (loading.Segment(nameof(ISelectionEngine), "Selection engine"))
{
var show_root = context.ShowRoot;
IAuditionEngine audition_engine = ParseAuditionEngine() switch
_auditionEngine = ParseAuditionEngine() switch
{
nameof(NeuralAuditionEngine) => new NeuralAuditionEngine(criterias, NeuralEngineConfirm),
nameof(WeightedSumEngine) => new WeightedSumEngine(criterias),
_ => throw new ArgumentException($"Audition engine not handled: {ParseAuditionEngine()}")
};
applicantDescription.AuditionEngine = audition_engine;
_engine = suitabilityCalculator.SelectionEngine = ParseSelectionEngine() switch
applicantDescription.AuditionEngine = _auditionEngine;
_selectionEngine = suitabilityCalculator.SelectionEngine = ParseSelectionEngine() switch
{
nameof(HeuristicSelectionEngine) => new HeuristicSelectionEngine(audition_engine, alternativeCasts, show_root.CastNumberOrderBy, show_root.CastNumberOrderDirection),
nameof(ChunkedPairsSatEngine) => new ChunkedPairsSatEngine(audition_engine, alternativeCasts, show_root.CastNumberOrderBy, show_root.CastNumberOrderDirection, criterias),
nameof(TopPairsSatEngine) => new TopPairsSatEngine(audition_engine, alternativeCasts, show_root.CastNumberOrderBy, show_root.CastNumberOrderDirection, criterias),
nameof(ThreesACrowdSatEngine) => new ThreesACrowdSatEngine(audition_engine, alternativeCasts, show_root.CastNumberOrderBy, show_root.CastNumberOrderDirection, criterias),
nameof(HybridPairsSatEngine) => new HybridPairsSatEngine(audition_engine, alternativeCasts, show_root.CastNumberOrderBy, show_root.CastNumberOrderDirection, criterias),
nameof(RankDifferenceSatEngine) => new RankDifferenceSatEngine(audition_engine, alternativeCasts, show_root.CastNumberOrderBy, show_root.CastNumberOrderDirection, criterias),
nameof(BestPairsSatEngine) => new BestPairsSatEngine(audition_engine, alternativeCasts, show_root.CastNumberOrderBy, show_root.CastNumberOrderDirection, criterias),
nameof(HeuristicSelectionEngine) => new HeuristicSelectionEngine(_auditionEngine, alternativeCasts, show_root.CastNumberOrderBy, show_root.CastNumberOrderDirection),
nameof(ChunkedPairsSatEngine) => new ChunkedPairsSatEngine(_auditionEngine, alternativeCasts, show_root.CastNumberOrderBy, show_root.CastNumberOrderDirection, criterias),
nameof(TopPairsSatEngine) => new TopPairsSatEngine(_auditionEngine, alternativeCasts, show_root.CastNumberOrderBy, show_root.CastNumberOrderDirection, criterias),
nameof(ThreesACrowdSatEngine) => new ThreesACrowdSatEngine(_auditionEngine, alternativeCasts, show_root.CastNumberOrderBy, show_root.CastNumberOrderDirection, criterias),
nameof(HybridPairsSatEngine) => new HybridPairsSatEngine(_auditionEngine, alternativeCasts, show_root.CastNumberOrderBy, show_root.CastNumberOrderDirection, criterias),
nameof(RankDifferenceSatEngine) => new RankDifferenceSatEngine(_auditionEngine, alternativeCasts, show_root.CastNumberOrderBy, show_root.CastNumberOrderDirection, criterias),
nameof(BestPairsSatEngine) => new BestPairsSatEngine(_auditionEngine, alternativeCasts, show_root.CastNumberOrderBy, show_root.CastNumberOrderDirection, criterias),
_ => throw new ArgumentException($"Allocation engine not handled: {ParseSelectionEngine()}")
};
}
Expand Down Expand Up @@ -191,9 +195,9 @@ protected override async Task<bool> PreSaveChecks()
using (var processing = new LoadingOverlay(this).AsSegment(nameof(SelectCast) + nameof(PreSaveChecks), "Processing..."))
{
using (processing.Segment(nameof(ISelectionEngine.BalanceAlternativeCasts), "Balancing alternating casts"))
await engine.BalanceAlternativeCasts(applicants, context.SameCastSets.Local);
await selectionEngine.BalanceAlternativeCasts(applicants, context.SameCastSets.Local);
using (processing.Segment(nameof(ISelectionEngine.AllocateCastNumbers), "Allocating cast numbers"))
await engine.AllocateCastNumbers(applicants);
await selectionEngine.AllocateCastNumbers(applicants);
}
var updated_inconsistent_applicants = applicants
.Where(a => a.CastGroup is CastGroup cg && (a.CastNumber == null || cg.AlternateCasts != (a.AlternativeCast != null)));
Expand All @@ -211,7 +215,7 @@ protected override async Task<bool> PreSaveChecks()
}
}
using (new LoadingOverlay(this).AsSegment(nameof(IAuditionEngine) + nameof(IAuditionEngine.UserSelectedCast), "Learning...", "Cast selected by the user"))
await engine.UserSelectedCast(applicants.Where(a => a.IsAccepted), applicants.Where(a => !a.IsAccepted));
await selectionEngine.UserSelectedCast(applicants.Where(a => a.IsAccepted), applicants.Where(a => !a.IsAccepted));
return true;
}

Expand All @@ -230,28 +234,28 @@ private async void selectCastButton_Click(object sender, RoutedEventArgs e)
if (settings.SelectCastGroups == true)
ClearCastGroups();
if (settings.SelectCastGroups != false)
await engine.SelectCastGroups(applicants, castGroups);
await selectionEngine.SelectCastGroups(applicants, castGroups);
}
using (processing.Segment(nameof(ISelectionEngine.BalanceAlternativeCasts), "Balancing alternating casts"))
{
if (settings.BalanceAlternativeCasts == true)
ClearAlternativeCasts();
if (settings.BalanceAlternativeCasts != false)
await engine .BalanceAlternativeCasts(applicants, context.SameCastSets.Local);
await selectionEngine .BalanceAlternativeCasts(applicants, context.SameCastSets.Local);
}
using (processing.Segment(nameof(ISelectionEngine.AllocateCastNumbers), "Allocating cast numbers"))
{
if (settings.AllocateCastNumbers == true)
ClearCastNumbers();
if (settings.AllocateCastNumbers != false)
await engine.AllocateCastNumbers(applicants);
await selectionEngine.AllocateCastNumbers(applicants);
}
using (processing.Segment(nameof(ISelectionEngine.ApplyTags), "Applying tags"))
{
if (settings.ApplyTags == true)
ClearTags();
if (settings.ApplyTags != false)
await engine.ApplyTags(applicants, tags);
await selectionEngine.ApplyTags(applicants, tags);
}
}

Expand Down Expand Up @@ -435,11 +439,43 @@ private async Task RemoveFromList(IList list, Applicant[] applicants)
overlay.Dispose();
}

private void availableList_MouseDoubleClick(object sender, MouseButtonEventArgs e)
=> addButton_Click(sender, e);
private void List_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
var list_box = (ListBox)sender;
if (list_box.SelectedItem is Applicant applicant)
{
ShowDetailsWindow(new ApplicantForSelection(applicant, criterias));
e.Handled = true;
}
}

private void selectedList_MouseDoubleClick(object sender, MouseButtonEventArgs e)
=> removeButton_Click(sender, e);
Dictionary<Applicant, ApplicantDetailsWindow> detailsWindows = new();

void ShowDetailsWindow(ApplicantForSelection afs)
{
if (!detailsWindows.TryGetValue(afs.Applicant, out var window) || window.IsClosed)
{
window = new ApplicantDetailsWindow(connection, criterias, auditionEngine, afs)
{
Owner = Window.GetWindow(this)
};
detailsWindows[afs.Applicant] = window;
window.Show();
}
if (window.WindowState == WindowState.Minimized)
window.WindowState = WindowState.Normal;
window.Activate();
}

protected override void DisposeInternal()
{
foreach (var window in detailsWindows.Values)
{
window.Close();
}
detailsWindows.Clear();
base.DisposeInternal();
}

private void castStatusCombo_SelectionChanged(object sender, SelectionChangedEventArgs e)
=> ConfigureAllApplicantsFiltering();
Expand Down Expand Up @@ -491,12 +527,6 @@ private static void RemoveFromSameCastSet(SameCastSet set, Applicant[] applicant
}
}

private void availableSameCastSetList_MouseDoubleClick(object sender, MouseButtonEventArgs e)
=> addSameCastSetButton_Click(sender, e);

private void selectedSameCastSetList_MouseDoubleClick(object sender, MouseButtonEventArgs e)
=> removeSameCastSetButton_Click(sender, e);

private void selectionList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (suitabilityComparer != null)
Expand Down Expand Up @@ -566,7 +596,7 @@ private async void DetectSiblings_Click(object sender, RoutedEventArgs e)
{
IEnumerable<SameCastSet> new_same_cast_sets;
using (new LoadingOverlay(this).AsSegment(nameof(SelectCast) + nameof(PreSaveChecks), "Processing...", "Detecting siblings"))
new_same_cast_sets = await engine.DetectFamilies(applicants);
new_same_cast_sets = await selectionEngine.DetectFamilies(applicants);
var list = (IList)sameCastSetsViewSource.Source;
foreach (var new_same_cast_set in new_same_cast_sets)
list.Add(new_same_cast_set);
Expand Down
4 changes: 3 additions & 1 deletion Desktop/ViewModels/ApplicantForRole.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

namespace Carmen.Desktop.ViewModels
{
public class ApplicantForRole : INotifyPropertyChanged
public class ApplicantForRole : ISelectableApplicant, INotifyPropertyChanged
{
public Applicant Applicant { get; init; }
public Criteria[] PrimaryCriterias { get; init; }
Expand Down Expand Up @@ -97,6 +97,8 @@ public IEnumerable<string> UnavailabilityReasons

public string CommaSeparatedIneligibilityReason => string.Join(", ", IneligibilityReasons);

public string? SelectionText => $"Allocate {RoleName} to {FirstName}";

public ApplicantForRole(IAllocationEngine engine, Applicant applicant, Role role, Criteria[] primary_criterias)
{
this.Applicant = applicant;
Expand Down
49 changes: 49 additions & 0 deletions Desktop/ViewModels/ApplicantForSelection.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using Carmen.CastingEngine.Allocation;
using Carmen.ShowModel.Applicants;
using Carmen.ShowModel.Criterias;
using Carmen.ShowModel.Structure;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;

namespace Carmen.Desktop.ViewModels
{
public class ApplicantForSelection : ISelectableApplicant, INotifyPropertyChanged
{
public Applicant Applicant { get; init; }
public Criteria[] PrimaryCriterias { get; init; }

public event PropertyChangedEventHandler? PropertyChanged;

public bool IsSelected
{
get => true; // so that colour images are shown
set => throw new InvalidOperationException();
}

public string FirstName => Applicant.FirstName;
public string LastName => Applicant.LastName;

public IEnumerable<string> ExistingRoles => Enumerable.Empty<string>();
public IEnumerable<string> UnavailabilityReasons => Enumerable.Empty<string>();
public IEnumerable<string> IneligibilityReasons => Enumerable.Empty<string>();

public string? SelectionText => null; // this hides the checkbox completely

public ApplicantForSelection(Applicant applicant, Criteria[] criterias)
{
Applicant = applicant;
PrimaryCriterias = criterias; // all criterias are useful at the selection phase, not just primary
}

protected void OnPropertyChanged([CallerMemberName] string? name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
}
23 changes: 23 additions & 0 deletions Desktop/ViewModels/ISelectableApplicant.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using Carmen.ShowModel.Applicants;
using Carmen.ShowModel.Criterias;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Carmen.Desktop.ViewModels
{
public interface ISelectableApplicant
{
Applicant Applicant { get; }
Criteria[] PrimaryCriterias { get; }
bool IsSelected { get; set; }
string FirstName { get; }
string LastName { get; }
string? SelectionText { get; }
IEnumerable<string> ExistingRoles { get; }
IEnumerable<string> UnavailabilityReasons { get; }
IEnumerable<string> IneligibilityReasons { get; }
}
}
16 changes: 10 additions & 6 deletions Desktop/Windows/ApplicantDetailsWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@
</cv:MultiConverter>
<cv:HideIfEmpty x:Key="hideIfEmpty"/>
<cv:AbilityMarkFormatter x:Key="abilityMarkFormatter"/>
<cv:MultiConverter x:Key="showIfFalse">
<cv:InvertBoolean/>
<BooleanToVisibilityConverter/>
</cv:MultiConverter>
<BooleanToVisibilityConverter x:Key="showIfTrue"/>
<cv:CollapsedIfNull x:Key="collapsedIfNull"/>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
Expand Down Expand Up @@ -152,15 +158,13 @@
</StackPanel>
</ScrollViewer>
<Grid Grid.Row="2" Margin="5">
<CheckBox IsChecked="{Binding IsSelected}">
<CheckBox IsChecked="{Binding IsSelected}" Visibility="{Binding SelectionText, Converter={StaticResource collapsedIfNull}}">
<TextBlock>
<Run Text="Allocate"/>
<Run Text="{Binding RoleName, Mode=OneWay}"/>
<Run Text="to"/>
<Run Text="{Binding FirstName, Mode=OneWay}"/>
<Run Text="{Binding SelectionText, Mode=OneWay}"/>
</TextBlock>
</CheckBox>
</Grid>
<Image Grid.Column="1" Grid.RowSpan="3" x:Name="ImageControl"/>
<Image Grid.Column="1" Grid.RowSpan="3" x:Name="ImageControl" MouseLeftButtonDown="ImageControl_MouseLeftButtonDown" Visibility="{Binding IsSelected, Converter={StaticResource showIfTrue}}"/>
<Image Grid.Column="1" Grid.RowSpan="3" x:Name="ImageControlGrey" MouseLeftButtonDown="ImageControl_MouseLeftButtonDown" Visibility="{Binding IsSelected, Converter={StaticResource showIfFalse}}"/>
</Grid>
</Window>
Loading