Skip to content

Commit

Permalink
Issue9: Applicant details while selecting (#48)
Browse files Browse the repository at this point in the history
  • Loading branch information
davidlang42 authored Nov 27, 2023
1 parent 58139ac commit 7fc0eb1
Show file tree
Hide file tree
Showing 8 changed files with 206 additions and 46 deletions.
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

0 comments on commit 7fc0eb1

Please sign in to comment.