Merge branch 'feature/api_enhancements' into develop

This commit is contained in:
Daniel Schick 2024-03-27 10:08:48 +01:00
commit 862ef9fe88
47 changed files with 5603 additions and 3824 deletions

6
.vscode/launch.json vendored
View File

@ -15,9 +15,9 @@
"SECRET_KEY" : "zdiTz8P3jXOc7jztIQAoelK4zztyuCpJ" // https://randomkeygen.com/ "SECRET_KEY" : "zdiTz8P3jXOc7jztIQAoelK4zztyuCpJ" // https://randomkeygen.com/
}, },
"args": [ "args": [
"run" //, "run",
// "--no-debugger", // "--no-debugger",
//"--no-reload" "--no-reload"
], ],
"jinja": true, "jinja": true,
"justMyCode": true "justMyCode": true

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,87 @@
-- add notification handling columns to shipcall
-- evaluation_time: Time when the "traffic light" was last changed
-- evaluation_notifications_sent: Flag to indicate if notifications were sent for the current evaluation
ALTER TABLE `bremen_calling_devel`.`shipcall`
ADD COLUMN `evaluation_time` DATETIME NULL DEFAULT NULL AFTER `evaluation_message`,
ADD COLUMN `evaluation_notifications_sent` BIT NULL AFTER `evaluation_time`;
-- prepare notification table for historic notification data
-- removed reference to participant and times and dropped unnecessary columns
-- added reference to shipcall
ALTER TABLE `bremen_calling_devel`.`notification`
DROP FOREIGN KEY `FK_NOT_TIMES`,
DROP FOREIGN KEY `FK_NOT_PART`;
ALTER TABLE `bremen_calling_devel`.`notification`
DROP COLUMN `deleted`,
DROP COLUMN `acknowledged`,
DROP COLUMN `participant_id`,
DROP COLUMN `times_id`,
ADD COLUMN `shipcall_id` INT UNSIGNED NULL AFTER `id`,
ADD INDEX `FK_NOTIFICATION_SHIPCALL_idx` (`shipcall_id` ASC) VISIBLE,
DROP INDEX `FK_NOT_PART` ,
DROP INDEX `FK_NOT_TIMES` ;
;
ALTER TABLE `bremen_calling_devel`.`notification`
ADD CONSTRAINT `FK_NOTIFICATION_SHIPCALL`
FOREIGN KEY (`shipcall_id`)
REFERENCES `bremen_calling_devel`.`shipcall` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION;
-- added notification flags
-- participant reference is now mandatory
ALTER TABLE `bremen_calling_devel`.`user`
DROP FOREIGN KEY `FK_USER_PART`;
ALTER TABLE `bremen_calling_devel`.`user`
ADD COLUMN `notify_email` BIT NULL DEFAULT NULL AFTER `api_key`,
ADD COLUMN `notify_whatsapp` BIT NULL DEFAULT NULL AFTER `notify_email`,
ADD COLUMN `notify_signal` BIT NULL DEFAULT NULL AFTER `notify_whatsapp`,
ADD COLUMN `notify_popup` BIT NULL DEFAULT NULL AFTER `notify_signal`,
CHANGE COLUMN `participant_id` `participant_id` INT UNSIGNED NOT NULL ;
ALTER TABLE `bremen_calling_devel`.`user`
ADD CONSTRAINT `FK_USER_PART`
FOREIGN KEY (`participant_id`)
REFERENCES `bremen_calling_devel`.`participant` (`id`);
-- History table for change tracking
CREATE TABLE `bremen_calling_devel`.`history` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`participant_id` INT UNSIGNED NOT NULL,
`user_id` INT UNSIGNED NOT NULL,
`shipcall_id` INT UNSIGNED NOT NULL,
`timestamp` DATETIME NOT NULL COMMENT 'Time of saving',
`eta` DATETIME NULL COMMENT 'Current ETA / ETD value (depends if shipcall or times were saved)',
`type` INT NOT NULL COMMENT 'shipcall or times',
`operation` INT NOT NULL COMMENT 'insert, update or delete',
PRIMARY KEY (`id`))
COMMENT = 'This table stores a history of changes made to shipcalls so that everyone can see who changed what and when';
-- and foreign keys
ALTER TABLE `bremen_calling_devel`.`history`
ADD INDEX `FK_HISTORY_PARTICIPANT_idx` (`participant_id` ASC) VISIBLE,
ADD INDEX `FK_HISTORY_SHIPCALL_idx` (`shipcall_id` ASC) VISIBLE;
;
ALTER TABLE `bremen_calling_devel`.`history`
ADD CONSTRAINT `FK_HISTORY_PARTICIPANT`
FOREIGN KEY (`participant_id`)
REFERENCES `bremen_calling_devel`.`participant` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
ADD CONSTRAINT `FK_HISTORY_SHIPCALL`
FOREIGN KEY (`shipcall_id`)
REFERENCES `bremen_calling_devel`.`shipcall` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
ADD CONSTRAINT `FK_HISTORY_USER`
FOREIGN KEY (`user_id`)
REFERENCES `bremen_calling_devel`.`user` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION;

View File

@ -17,6 +17,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<None Remove="Resources\add.png" />
<None Remove="Resources\arrow_down_green.png" /> <None Remove="Resources\arrow_down_green.png" />
<None Remove="Resources\arrow_down_red.png" /> <None Remove="Resources\arrow_down_red.png" />
<None Remove="Resources\arrow_right_blue.png" /> <None Remove="Resources\arrow_right_blue.png" />
@ -31,6 +32,7 @@
<None Remove="Resources\containership.png" /> <None Remove="Resources\containership.png" />
<None Remove="Resources\delete.png" /> <None Remove="Resources\delete.png" />
<None Remove="Resources\delete2.png" /> <None Remove="Resources\delete2.png" />
<None Remove="Resources\edit.png" />
<None Remove="Resources\emergency_stop_button.png" /> <None Remove="Resources\emergency_stop_button.png" />
<None Remove="Resources\logo_bremen_calling.png" /> <None Remove="Resources\logo_bremen_calling.png" />
<None Remove="Resources\ship2.png" /> <None Remove="Resources\ship2.png" />
@ -68,6 +70,7 @@
<Generator>OpenApiCodeGenerator</Generator> <Generator>OpenApiCodeGenerator</Generator>
<LastGenOutput>BreCalApi.cs</LastGenOutput> <LastGenOutput>BreCalApi.cs</LastGenOutput>
</None> </None>
<Resource Include="Resources\add.png" />
<Resource Include="Resources\arrow_down_green.png" /> <Resource Include="Resources\arrow_down_green.png" />
<Resource Include="Resources\arrow_down_red.png" /> <Resource Include="Resources\arrow_down_red.png" />
<Resource Include="Resources\arrow_right_blue.png" /> <Resource Include="Resources\arrow_right_blue.png" />
@ -82,6 +85,7 @@
<Resource Include="Resources\containership.png" /> <Resource Include="Resources\containership.png" />
<Resource Include="Resources\delete.png" /> <Resource Include="Resources\delete.png" />
<Resource Include="Resources\delete2.png" /> <Resource Include="Resources\delete2.png" />
<Resource Include="Resources\edit.png" />
<Resource Include="Resources\emergency_stop_button.png" /> <Resource Include="Resources\emergency_stop_button.png" />
<Resource Include="Resources\logo_bremen_calling.png" /> <Resource Include="Resources\logo_bremen_calling.png" />
<Resource Include="Resources\ship2.png" /> <Resource Include="Resources\ship2.png" />
@ -106,7 +110,7 @@
<PackageReference Include="JsonSubTypes" Version="2.0.1" /> <PackageReference Include="JsonSubTypes" Version="2.0.1" />
<PackageReference Include="log4net" Version="2.0.15" /> <PackageReference Include="log4net" Version="2.0.15" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Polly" Version="7.2.4" /> <PackageReference Include="Polly" Version="8.2.0" />
<PackageReference Include="RestSharp" Version="110.2.0" /> <PackageReference Include="RestSharp" Version="110.2.0" />
</ItemGroup> </ItemGroup>

View File

@ -0,0 +1,258 @@
// Copyright (c) 2017 schick Informatik
// Description: DataGrid mit etwas "verbesserten" Funktionen
//
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Controls.Primitives;
using System.Collections.Generic;
using System.Globalization;
using System.Threading;
namespace BreCalClient
{
/// <summary>
/// Follow steps 1a or 1b and then 2 to use this custom control in a XAML file.
///
/// Step 1a) Using this custom control in a XAML file that exists in the current project.
/// Add this XmlNamespace attribute to the root element of the markup file where it is
/// to be used:
///
/// xmlns:enictrl="clr-namespace:ENI2.Controls"
///
///
/// Step 1b) Using this custom control in a XAML file that exists in a different project.
/// Add this XmlNamespace attribute to the root element of the markup file where it is
/// to be used:
///
/// xmlns:enictrl="clr-namespace:ENI2.Controls;assembly=ENI2.Controls"
///
/// You will also need to add a project reference from the project where the XAML file lives
/// to this project and Rebuild to avoid compilation errors:
///
/// Right click on the target project in the Solution Explorer and
/// "Add Reference"->"Projects"->[Browse to and select this project]
///
///
/// Step 2)
/// Go ahead and use your control in the XAML file.
///
/// <MyNamespace:ENIDataGrid/>
///
/// </summary>
public class ENIDataGrid : DataGrid
{
public event Action<object>? EditRequested;
public event Action<object>? DeleteRequested;
public event Action? CreateRequested;
public event Action? RefreshGrid;
public event Action<object>? PrintRequested;
public event Action<object>? ExportRequested;
public event Action<object>? ShowTextRequested;
public void Initialize()
{
this.MouseDoubleClick += dataGrid_MouseDoubleClick;
this.PreviewKeyDown += ENIDataGrid_PreviewKeyDown;
this.ContextMenu = new ContextMenu();
this.CanUserAddRows = false;
this.IsReadOnly = false;
MenuItem addItem = new MenuItem();
addItem.Header = BreCalClient.Resources.Resources.textAdd;
addItem.Icon = new Image { Source = Util.LoadImage(BreCalClient.Resources.Resources.add) };
addItem.Click += new RoutedEventHandler(this.addItem);
this.ContextMenu.Items.Add(addItem);
MenuItem deleteItem = new MenuItem();
deleteItem.Header = BreCalClient.Resources.Resources.textDelete;
deleteItem.Icon = new Image { Source = Util.LoadImage(BreCalClient.Resources.Resources.delete) };
deleteItem.Click += this.deleteItem;
this.ContextMenu.Items.Add(deleteItem);
MenuItem editItem = new MenuItem();
editItem.Header = BreCalClient.Resources.Resources.textEdit;
editItem.Icon = new Image { Source = Util.LoadImage(BreCalClient.Resources.Resources.edit) };
editItem.Click += this.editItem;
this.ContextMenu.Items.Add(editItem);
}
private void ENIDataGrid_PreviewKeyDown(object sender, KeyEventArgs e)
{
if(sender is ENIDataGrid)
{
var grid = sender as ENIDataGrid;
if (Key.Delete == e.Key)
this.deleteItem(null, null);
}
}
#region public
public DataGridRow GetRow(int index)
{
DataGridRow row = (DataGridRow)this.ItemContainerGenerator.ContainerFromIndex(index);
if(row == null)
{
this.UpdateLayout();
this.ScrollIntoView(this.Items[index]);
row = (DataGridRow)this.ItemContainerGenerator.ContainerFromIndex(index);
}
return row;
}
public DataGridCell? GetCell(DataGridRow row, int column)
{
if (row != null)
{
DataGridCellsPresenter? presenter = GetVisualChild<DataGridCellsPresenter>(row);
if (presenter == null)
{
this.ScrollIntoView(row, this.Columns[column]);
presenter = GetVisualChild<DataGridCellsPresenter>(row);
}
DataGridCell? cell = (DataGridCell?)presenter?.ItemContainerGenerator.ContainerFromIndex(column);
return cell;
}
return null;
}
public DataGridCell? GetCell(int rowIndex, int columnIndex)
{
DataGridRow row = this.GetRow(rowIndex);
return this.GetCell(row, columnIndex);
}
#endregion
#region protected
protected void addItem(object sender, RoutedEventArgs e)
{
Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-us");
if (!this.IsReadOnly)
{
this.CreateRequested?.Invoke();
}
e.Handled = true;
}
protected void deleteItem(object? sender, RoutedEventArgs? e)
{
if((this.SelectedItems != null) && (this.SelectedItems.Count > 0) && !this.IsReadOnly)
{
MessageBoxResult result = MessageBox.Show("Are your sure?", "Please confirm", MessageBoxButton.YesNo, MessageBoxImage.Question);
if (result == MessageBoxResult.Yes)
{
List<object> deleteList = new List<object>();
foreach (object deleteItem in this.SelectedItems)
deleteList.Add(deleteItem);
foreach (object deleteItem in deleteList)
{
if (deleteItem != null)
{
this.DeleteRequested?.Invoke(deleteItem);
}
}
this.RefreshGrid?.Invoke();
}
}
}
protected void editItem(object sender, RoutedEventArgs e)
{
if((this.SelectedItems != null) && (this.SelectedItems.Count == 1) && !this.IsReadOnly)
{
if (this.SelectedItems[0] is object selectedEntity)
this.EditRequested?.Invoke(selectedEntity);
}
}
protected void printItem(object sender, RoutedEventArgs e)
{
if ((this.SelectedItems != null) && (this.SelectedItems.Count == 1) )
{
if (this.SelectedItems[0] is object selectedEntity)
this.PrintRequested?.Invoke(selectedEntity);
}
}
protected void exportItem(object sender, RoutedEventArgs e)
{
if ((this.SelectedItems != null) && (this.SelectedItems.Count == 1))
{
if (this.SelectedItems[0] is object selectedEntity)
this.ExportRequested?.Invoke(selectedEntity);
}
}
protected void showTextItem(object sender, RoutedEventArgs e)
{
if ((this.SelectedItems != null) && (this.SelectedItems.Count == 1))
{
if (this.SelectedItems[0] is object selectedEntity)
this.ShowTextRequested?.Invoke(selectedEntity);
}
}
#endregion
#region private
private void dataGrid_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
if (sender != null)
{
if ((sender is DataGrid grid) && (grid.SelectedItems != null) && (grid.SelectedItems.Count == 1) && !this.IsReadOnly)
{
DataGridRow? dgr = grid.ItemContainerGenerator.ContainerFromItem(grid.SelectedItem) as DataGridRow;
if (grid.SelectedItem is object selectedEntity)
this.EditRequested?.Invoke(selectedEntity);
}
}
}
#endregion
#region private static
private static T? GetVisualChild<T>(Visual parent) where T : Visual
{
T? child = default;
int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < numVisuals; i++)
{
Visual? v = (Visual)VisualTreeHelper.GetChild(parent, i);
child = v as T;
if (child == null)
{
child = GetVisualChild<T>(v);
}
if (child != null)
{
break;
}
}
return child;
}
#endregion
}
}

View File

@ -0,0 +1,54 @@
<Window x:Class="BreCalClient.EditShipDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:BreCalClient"
xmlns:p = "clr-namespace:BreCalClient.Resources"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
mc:Ignorable="d"
Left="{local:SettingBinding W1Left}" Top="{local:SettingBinding W1Top}"
Title="{x:Static p:Resources.textEditShip}" Height="250" Width="500" Loaded="Window_Loaded" ResizeMode="NoResize">
<Grid x:Name="shipGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".3*" />
<ColumnDefinition Width=".6*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="28" />
<RowDefinition Height="28" />
<RowDefinition Height="28" />
<RowDefinition Height="28" />
<RowDefinition Height="28" />
<RowDefinition Height="28" />
<RowDefinition Height="*" />
<RowDefinition Height="28" />
</Grid.RowDefinitions>
<Label Content="Name" HorizontalAlignment="Right" />
<TextBox x:Name="textBoxName" Grid.Column="1" Margin="2" VerticalContentAlignment="Center" Text="{Binding Name, Mode=OneWay}" />
<Label Content="{x:Static p:Resources.textTugCompany}" HorizontalAlignment="Right" Grid.Row="1" />
<Grid Grid.Row="1" Grid.Column="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="28" />
</Grid.ColumnDefinitions>
<ComboBox x:Name="comboBoxParticipants" Margin="2" DisplayMemberPath="Name" />
<Button x:Name="buttonResetParticipant" Grid.Column="1" Margin="2" Click="buttonResetParticipant_Click">
<Image Source="./Resources/delete2.png"/>
</Button>
</Grid>
<Label Content="IMO" HorizontalAlignment="Right" Grid.Row="2" />
<xctk:IntegerUpDown Name="integerUpDownIMO" Grid.Column="1" Grid.Row="2" Value="{Binding Imo, Mode=OneWay}" Margin="2" Minimum="1000000" Maximum="9999999" ShowButtonSpinner="False"/>
<Label Content="{x:Static p:Resources.textCallsign}" HorizontalAlignment="Right" Grid.Row="3" />
<TextBox x:Name="textBoxCallsign" Grid.Column="1" Grid.Row="3" Margin="2" VerticalContentAlignment="Center" Text="{Binding Callsign, Mode=OneWay}" />
<Label Content="{x:Static p:Resources.textLength}" HorizontalAlignment="Right" Grid.Row="4" />
<xctk:DoubleUpDown Name="doubleUpDownLength" Grid.Row="4" Grid.Column="1" Value="{Binding Length, Mode=OneWay}" Margin="2" Minimum="0" />
<Label Content="{x:Static p:Resources.textWidth}" HorizontalAlignment="Right" Grid.Row="5" />
<xctk:DoubleUpDown Name="doubleUpDownWidth" Grid.Row="5" Grid.Column="1" Value="{Binding Width, Mode=OneWay}" Margin="2" Minimum="0"/>
<StackPanel Grid.Column="1" Grid.Row="7" Orientation="Horizontal" FlowDirection="RightToLeft">
<Button x:Name="buttonCancel" Width="80" Content="{x:Static p:Resources.textCancel}" Margin="2" Click="buttonCancel_Click" />
<Button x:Name="buttonOK" Width="80" Content="OK" Margin="2" Click="buttonOK_Click"/>
</StackPanel>
</Grid>
</Window>

View File

@ -0,0 +1,71 @@
using BreCalClient.misc.Model;
using System.Collections.Generic;
using System.Windows;
namespace BreCalClient
{
/// <summary>
/// Interaction logic for EditShipDialog.xaml
/// </summary>
public partial class EditShipDialog : Window
{
public EditShipDialog()
{
InitializeComponent();
}
public Ship Ship { get; set; } = new();
public List<Participant> Participants { get; } = new List<Participant>();
private void buttonCancel_Click(object sender, RoutedEventArgs e)
{
this.DialogResult = false;
this.Close();
}
private void buttonOK_Click(object sender, RoutedEventArgs e)
{
this.Ship.Name = this.textBoxName.Text.Trim();
if (this.comboBoxParticipants.SelectedItem != null)
{
this.Ship.ParticipantId = ((Participant)this.comboBoxParticipants.SelectedItem).Id;
this.Ship.IsTug = true;
}
else
{
this.Ship.IsTug = false;
}
this.Ship.Imo = this.integerUpDownIMO.Value;
this.Ship.Callsign = this.textBoxCallsign.Text.Trim();
this.Ship.Length = (float?) this.doubleUpDownLength.Value;
this.Ship.Width = (float?) this.doubleUpDownWidth.Value;
this.DialogResult = true;
this.Close();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
this.DataContext = this.Ship;
this.comboBoxParticipants.ItemsSource = this.Participants;
if(this.Ship.ParticipantId != null)
{
foreach(Participant p in this.Participants)
{
if(this.Ship.ParticipantId == p.Id)
this.comboBoxParticipants.SelectedItem = p;
}
}
}
private void buttonResetParticipant_Click(object sender, RoutedEventArgs e)
{
this.comboBoxParticipants.SelectedItem = null;
}
}
}

View File

@ -32,15 +32,16 @@
<Label Content="{x:Static p:Resources.textShip}" Grid.Column="0" Grid.Row="0" HorizontalContentAlignment="Right"/> <Label Content="{x:Static p:Resources.textShip}" Grid.Column="0" Grid.Row="0" HorizontalContentAlignment="Right"/>
<ComboBox x:Name="comboBoxShip" Margin="2" Grid.Column="1" Grid.Row="0" SelectionChanged="comboBoxShip_SelectionChanged" SelectedValuePath="Ship.Id" IsEditable="True" IsTextSearchEnabled="True" IsTextSearchCaseSensitive="False" /> <ComboBox x:Name="comboBoxShip" Margin="2" Grid.Column="1" Grid.Row="0" SelectionChanged="comboBoxShip_SelectionChanged" SelectedValuePath="Ship.Id" IsEditable="True" IsTextSearchEnabled="True" IsTextSearchCaseSensitive="False" />
<Label Content="IMO" Grid.Column="0" Grid.Row="1" HorizontalContentAlignment="Right"/> <Label Content="IMO" Grid.Column="0" Grid.Row="1" HorizontalContentAlignment="Right"/>
<xctk:IntegerUpDown x:Name="integerUpDownIMO" IsReadOnly="True" Margin="2" Grid.Column="1" Grid.Row="1" ShowButtonSpinner="False"/> <xctk:IntegerUpDown x:Name="integerUpDownIMO" IsReadOnly="True" Margin="2" Grid.Column="1" Grid.Row="1" ShowButtonSpinner="False" IsEnabled="False"/>
<Label Content="{x:Static p:Resources.textCallsign}" Grid.Column="0" Grid.Row="2" HorizontalContentAlignment="Right"/> <Label Content="{x:Static p:Resources.textCallsign}" Grid.Column="0" Grid.Row="2" HorizontalContentAlignment="Right"/>
<TextBox x:Name="textBoxCallsign" IsReadOnly="True" Grid.Column="1" Grid.Row="2" Margin="2" /> <TextBox x:Name="textBoxCallsign" IsReadOnly="True" Grid.Column="1" Grid.Row="2" Margin="2" IsEnabled="False"/>
<Label Content="{x:Static p:Resources.textLength}" Grid.Column="0" Grid.Row="3" HorizontalContentAlignment="Right"/> <Label Content="{x:Static p:Resources.textLength}" Grid.Column="0" Grid.Row="3" HorizontalContentAlignment="Right"/>
<xctk:DoubleUpDown x:Name="doubleUpDownLength" Margin="2" Grid.Column="1" Grid.Row="3" FormatString="N2" IsReadOnly="True" /> <xctk:DoubleUpDown x:Name="doubleUpDownLength" Margin="2" Grid.Column="1" Grid.Row="3" FormatString="N2" IsReadOnly="True" IsEnabled="False" ShowButtonSpinner="False"/>
<Label Content="{x:Static p:Resources.textWidth}" Grid.Column="0" Grid.Row="4" HorizontalContentAlignment="Right"/> <Label Content="{x:Static p:Resources.textWidth}" Grid.Column="0" Grid.Row="4" HorizontalContentAlignment="Right"/>
<xctk:DoubleUpDown x:Name="doubleUpDownWidth" Margin="2" Grid.Column="1" Grid.Row="4" FormatString="N2" IsReadOnly="True" /> <xctk:DoubleUpDown x:Name="doubleUpDownWidth" Margin="2" Grid.Column="1" Grid.Row="4" FormatString="N2" IsReadOnly="True" IsEnabled="False" ShowButtonSpinner="False"/>
<Label Content="{x:Static p:Resources.textCancelled}" Grid.Column="0" Grid.Row="5" HorizontalContentAlignment="Right" /> <Label Content="{x:Static p:Resources.textCancelled}" Grid.Column="0" Grid.Row="5" HorizontalContentAlignment="Right" />
<CheckBox x:Name="checkBoxCancelled" Grid.Column="1" Grid.Row="5" Margin="2" VerticalContentAlignment="Center" /> <CheckBox x:Name="checkBoxCancelled" Grid.Column="1" Grid.Row="5" Margin="2" VerticalContentAlignment="Center" />
<Button x:Name="buttonEditShips" Grid.Column="1" Grid.Row="6" Margin="2" Content="{x:Static p:Resources.textEditShips}" Click="buttonEditShips_Click" Visibility="Hidden" />
<Label Content="{x:Static p:Resources.textType}" Grid.Column="2" Grid.Row="0" HorizontalContentAlignment="Right" /> <Label Content="{x:Static p:Resources.textType}" Grid.Column="2" Grid.Row="0" HorizontalContentAlignment="Right" />
<Label Content="ETA" Grid.Column="2" Grid.Row="2" HorizontalContentAlignment="Right"/> <Label Content="ETA" Grid.Column="2" Grid.Row="2" HorizontalContentAlignment="Right"/>
@ -73,7 +74,7 @@
</ComboBox.ContextMenu> </ComboBox.ContextMenu>
</ComboBox> </ComboBox>
<Label x:Name="labelBSMDGranted" Grid.Row="6" Grid.Column="1" Grid.ColumnSpan="2" Content="{x:Static p:Resources.textBSMDGranted}" Visibility="Hidden" FontWeight="DemiBold" /> <Label x:Name="labelBSMDGranted" Grid.Row="5" Grid.Column="3" Grid.ColumnSpan="1" Content="{x:Static p:Resources.textBSMDGranted}" Visibility="Hidden" FontWeight="DemiBold" />
<StackPanel Grid.Row="6" Grid.Column="3" Orientation="Horizontal" HorizontalAlignment="Right"> <StackPanel Grid.Row="6" Grid.Column="3" Orientation="Horizontal" HorizontalAlignment="Right">
<Button Width= "80" Margin="2" Content="{x:Static p:Resources.textOK}" x:Name="buttonOK" Click="buttonOK_Click" IsEnabled="False" /> <Button Width= "80" Margin="2" Content="{x:Static p:Resources.textOK}" x:Name="buttonOK" Click="buttonOK_Click" IsEnabled="False" />
<Button Width="80" Margin="2" Content="{x:Static p:Resources.textCancel}" x:Name="buttonCancel" Click="buttonCancel_Click"/> <Button Width="80" Margin="2" Content="{x:Static p:Resources.textCancel}" x:Name="buttonCancel" Click="buttonCancel_Click"/>

View File

@ -2,6 +2,7 @@
// Description: Windows dialog to create / edit ship calls // Description: Windows dialog to create / edit ship calls
// //
using BreCalClient.misc.Api;
using BreCalClient.misc.Model; using BreCalClient.misc.Model;
using System; using System;
using System.Windows; using System.Windows;
@ -31,6 +32,14 @@ namespace BreCalClient
} }
} }
public bool ShipEditingEnabled
{
get { return this.buttonEditShips.Visibility == Visibility.Visible; }
set { this.buttonEditShips.Visibility = value ? Visibility.Visible : Visibility.Hidden; }
}
public ShipApi ShipApi { get; set; }
#endregion #endregion
#region Event handler #region Event handler
@ -39,8 +48,8 @@ namespace BreCalClient
{ {
this.comboBoxAgency.ItemsSource = BreCalLists.Participants_Agent; this.comboBoxAgency.ItemsSource = BreCalLists.Participants_Agent;
this.comboBoxShip.ItemsSource = BreCalLists.Ships; this.comboBoxShip.ItemsSource = BreCalLists.AllShips;
this.comboBoxCategories.ItemsSource = Enum.GetValues(typeof(TypeEnum)); this.comboBoxCategories.ItemsSource = Enum.GetValues(typeof(ShipcallType));
this.comboBoxArrivalBerth.ItemsSource = BreCalLists.Berths; this.comboBoxArrivalBerth.ItemsSource = BreCalLists.Berths;
this.comboBoxDepartureBerth.ItemsSource = BreCalLists.Berths; this.comboBoxDepartureBerth.ItemsSource = BreCalLists.Berths;
@ -91,12 +100,12 @@ namespace BreCalClient
private void comboBoxCategories_SelectionChanged(object? sender, SelectionChangedEventArgs? e) private void comboBoxCategories_SelectionChanged(object? sender, SelectionChangedEventArgs? e)
{ {
TypeEnum? type = this.comboBoxCategories.SelectedItem as TypeEnum?; ShipcallType? type = this.comboBoxCategories.SelectedItem as ShipcallType?;
if (type != null) if (type != null)
{ {
switch (type) switch (type)
{ {
case TypeEnum.Incoming: case ShipcallType.Arrival:
this.datePickerETA.IsEnabled = true; this.datePickerETA.IsEnabled = true;
this.datePickerETD.IsEnabled = false; this.datePickerETD.IsEnabled = false;
this.datePickerETD.Value = null; this.datePickerETD.Value = null;
@ -104,7 +113,7 @@ namespace BreCalClient
this.comboBoxDepartureBerth.IsEnabled = false; this.comboBoxDepartureBerth.IsEnabled = false;
this.comboBoxArrivalBerth.IsEnabled = true; this.comboBoxArrivalBerth.IsEnabled = true;
break; break;
case TypeEnum.Outgoing: case ShipcallType.Departure:
this.datePickerETA.IsEnabled = false; this.datePickerETA.IsEnabled = false;
this.datePickerETD.IsEnabled = true; this.datePickerETD.IsEnabled = true;
this.datePickerETA.Value = null; this.datePickerETA.Value = null;
@ -112,7 +121,7 @@ namespace BreCalClient
this.comboBoxArrivalBerth.IsEnabled = false; this.comboBoxArrivalBerth.IsEnabled = false;
this.comboBoxDepartureBerth.IsEnabled = true; this.comboBoxDepartureBerth.IsEnabled = true;
break; break;
case TypeEnum.Shifting: case ShipcallType.Shifting:
this.datePickerETA.IsEnabled = true; this.datePickerETA.IsEnabled = true;
this.datePickerETD.IsEnabled = true; this.datePickerETD.IsEnabled = true;
this.comboBoxArrivalBerth.IsEnabled = true; this.comboBoxArrivalBerth.IsEnabled = true;
@ -150,18 +159,18 @@ namespace BreCalClient
} }
else else
{ {
TypeEnum callType = (TypeEnum)comboBoxCategories.SelectedItem; ShipcallType callType = (ShipcallType)comboBoxCategories.SelectedItem;
switch (callType) switch (callType)
{ {
case TypeEnum.Outgoing: case ShipcallType.Departure:
isEnabled &= this.comboBoxDepartureBerth.SelectedItem != null; isEnabled &= this.comboBoxDepartureBerth.SelectedItem != null;
isEnabled &= this.datePickerETD.Value.HasValue; isEnabled &= this.datePickerETD.Value.HasValue;
break; break;
case TypeEnum.Incoming: case ShipcallType.Arrival:
isEnabled &= this.comboBoxArrivalBerth.SelectedItem != null; isEnabled &= this.comboBoxArrivalBerth.SelectedItem != null;
isEnabled &= this.datePickerETA.Value.HasValue; isEnabled &= this.datePickerETA.Value.HasValue;
break; break;
case TypeEnum.Shifting: case ShipcallType.Shifting:
isEnabled &= ((this.comboBoxDepartureBerth.SelectedItem != null) && (this.comboBoxArrivalBerth.SelectedItem != null)); isEnabled &= ((this.comboBoxDepartureBerth.SelectedItem != null) && (this.comboBoxArrivalBerth.SelectedItem != null));
isEnabled &= this.datePickerETD.Value.HasValue; isEnabled &= this.datePickerETD.Value.HasValue;
isEnabled &= this.datePickerETA.Value.HasValue; isEnabled &= this.datePickerETA.Value.HasValue;
@ -178,7 +187,7 @@ namespace BreCalClient
{ {
if (this.ShipcallModel.Shipcall != null) if (this.ShipcallModel.Shipcall != null)
{ {
this.ShipcallModel.Shipcall.Type = (int)this.comboBoxCategories.SelectedItem; this.ShipcallModel.Shipcall.Type = (ShipcallType) this.comboBoxCategories.SelectedItem;
this.ShipcallModel.Shipcall.Eta = this.datePickerETA.Value; this.ShipcallModel.Shipcall.Eta = this.datePickerETA.Value;
this.ShipcallModel.Shipcall.Etd = this.datePickerETD.Value; this.ShipcallModel.Shipcall.Etd = this.datePickerETD.Value;
@ -186,7 +195,7 @@ namespace BreCalClient
this.ShipcallModel.Ship = ((ShipModel)this.comboBoxShip.SelectedItem).Ship; this.ShipcallModel.Ship = ((ShipModel)this.comboBoxShip.SelectedItem).Ship;
this.ShipcallModel.Shipcall.Canceled = this.checkBoxCancelled.IsChecked; this.ShipcallModel.Shipcall.Canceled = this.checkBoxCancelled.IsChecked;
if (this.ShipcallModel.Shipcall.Type != 3) // incoming, outgoing if (this.ShipcallModel.Shipcall.Type != ShipcallType.Shifting) // incoming, outgoing
{ {
this.ShipcallModel.Shipcall.ArrivalBerthId = (this.comboBoxArrivalBerth.SelectedItem != null) ? ((Berth)this.comboBoxArrivalBerth.SelectedItem).Id : null; this.ShipcallModel.Shipcall.ArrivalBerthId = (this.comboBoxArrivalBerth.SelectedItem != null) ? ((Berth)this.comboBoxArrivalBerth.SelectedItem).Id : null;
this.ShipcallModel.Shipcall.DepartureBerthId = (this.comboBoxDepartureBerth.SelectedItem != null) ? ((Berth)this.comboBoxDepartureBerth.SelectedItem).Id : null; this.ShipcallModel.Shipcall.DepartureBerthId = (this.comboBoxDepartureBerth.SelectedItem != null) ? ((Berth)this.comboBoxDepartureBerth.SelectedItem).Id : null;
@ -204,6 +213,7 @@ namespace BreCalClient
ParticipantAssignment pa = new() ParticipantAssignment pa = new()
{ {
ParticipantId = participant.Id, ParticipantId = participant.Id,
Type = (int)Extensions.ParticipantType.AGENCY Type = (int)Extensions.ParticipantType.AGENCY
}; };
this.ShipcallModel.AssignedParticipants[Extensions.ParticipantType.AGENCY] = pa; this.ShipcallModel.AssignedParticipants[Extensions.ParticipantType.AGENCY] = pa;
@ -253,7 +263,7 @@ namespace BreCalClient
if (this.ShipcallModel == null) return; if (this.ShipcallModel == null) return;
if (this.ShipcallModel.Shipcall != null) if (this.ShipcallModel.Shipcall != null)
{ {
this.comboBoxCategories.SelectedItem = (TypeEnum)this.ShipcallModel.Shipcall.Type; this.comboBoxCategories.SelectedItem = this.ShipcallModel.Shipcall.Type;
if (this.ShipcallModel.Shipcall.Eta != DateTime.MinValue) if (this.ShipcallModel.Shipcall.Eta != DateTime.MinValue)
this.datePickerETA.Value = this.ShipcallModel.Shipcall.Eta; this.datePickerETA.Value = this.ShipcallModel.Shipcall.Eta;
// this.textBoxVoyage.Text = this.ShipcallModel.Shipcall.Voyage; // this.textBoxVoyage.Text = this.ShipcallModel.Shipcall.Voyage;
@ -261,7 +271,7 @@ namespace BreCalClient
this.comboBoxShip.SelectedValue = this.ShipcallModel.Shipcall.ShipId; this.comboBoxShip.SelectedValue = this.ShipcallModel.Shipcall.ShipId;
this.checkBoxCancelled.IsChecked = this.ShipcallModel.Shipcall.Canceled ?? false; this.checkBoxCancelled.IsChecked = this.ShipcallModel.Shipcall.Canceled ?? false;
if (this.ShipcallModel.Shipcall.Type != 3) // incoming, outgoing if (this.ShipcallModel.Shipcall.Type != ShipcallType.Shifting) // incoming, outgoing
{ {
this.comboBoxArrivalBerth.SelectedValue = this.ShipcallModel.Shipcall.ArrivalBerthId; this.comboBoxArrivalBerth.SelectedValue = this.ShipcallModel.Shipcall.ArrivalBerthId;
this.comboBoxDepartureBerth.SelectedValue = this.ShipcallModel.Shipcall.DepartureBerthId; this.comboBoxDepartureBerth.SelectedValue = this.ShipcallModel.Shipcall.DepartureBerthId;
@ -340,6 +350,20 @@ namespace BreCalClient
private void comboBoxDepartureBerth_SelectionChanged(object sender, SelectionChangedEventArgs e) private void comboBoxDepartureBerth_SelectionChanged(object sender, SelectionChangedEventArgs e)
{ {
this.CheckForCompletion(); this.CheckForCompletion();
}
private void buttonEditShips_Click(object sender, RoutedEventArgs e)
{
ShipListDialog shipListDialog = new()
{
ShipApi = this.ShipApi
};
shipListDialog.ShowDialog();
// reload combobox
this.comboBoxShip.ItemsSource = null;
this.comboBoxShip.ItemsSource = BreCalLists.AllShips;
} }
#endregion #endregion

View File

@ -31,7 +31,7 @@ namespace BreCalClient
public Times Times { get; set; } = new(); public Times Times { get; set; } = new();
public Extensions.TypeEnum CallType { get; set; } public ShipcallType CallType { get; set; }
#endregion #endregion

View File

@ -30,7 +30,7 @@ namespace BreCalClient
public Times Times { get; set; } = new(); public Times Times { get; set; } = new();
public Extensions.TypeEnum CallType { get; set; } public ShipcallType CallType { get; set; }
#endregion #endregion

View File

@ -30,7 +30,7 @@ namespace BreCalClient
public Times Times { get; set; } = new(); public Times Times { get; set; } = new();
public Extensions.TypeEnum CallType { get; set; } public ShipcallType CallType { get; set; }
#endregion #endregion

View File

@ -27,7 +27,7 @@ namespace BreCalClient
public Times Times { get; set; } = new(); public Times Times { get; set; } = new();
public Extensions.TypeEnum CallType { get; set; } public ShipcallType CallType { get; set; }
#endregion #endregion
@ -75,12 +75,12 @@ namespace BreCalClient
switch (CallType) switch (CallType)
{ {
case Extensions.TypeEnum.Incoming: case ShipcallType.Arrival:
this.labelETA.FontWeight = FontWeights.Bold; this.labelETA.FontWeight = FontWeights.Bold;
this.datePickerETABerth.ContextMenu.IsEnabled = false; this.datePickerETABerth.ContextMenu.IsEnabled = false;
break; break;
case Extensions.TypeEnum.Outgoing: case ShipcallType.Departure:
case Extensions.TypeEnum.Shifting: case ShipcallType.Shifting:
this.labelETD.FontWeight = FontWeights.Bold; this.labelETD.FontWeight = FontWeights.Bold;
this.datePickerETDBerth.ContextMenu.IsEnabled = false; this.datePickerETDBerth.ContextMenu.IsEnabled = false;
break; break;
@ -89,7 +89,7 @@ namespace BreCalClient
private void EnableControls() private void EnableControls()
{ {
Extensions.ParticipantType pType = (Extensions.ParticipantType) (this.Times.ParticipantType ?? 0); Extensions.ParticipantType pType = (Extensions.ParticipantType) this.Times.ParticipantType;
if (this.Times.ParticipantId != App.Participant.Id) return; // if this is not "my" entry, there is no editing! if (this.Times.ParticipantId != App.Participant.Id) return; // if this is not "my" entry, there is no editing!
switch (pType) switch (pType)
@ -97,24 +97,24 @@ namespace BreCalClient
case Extensions.ParticipantType.MOORING: case Extensions.ParticipantType.MOORING:
case Extensions.ParticipantType.PORT_ADMINISTRATION: case Extensions.ParticipantType.PORT_ADMINISTRATION:
case Extensions.ParticipantType.TUG: case Extensions.ParticipantType.TUG:
this.datePickerETABerth.IsEnabled = (CallType == Extensions.TypeEnum.Incoming); this.datePickerETABerth.IsEnabled = (CallType == ShipcallType.Arrival);
//this.checkBoxEtaBerthFixed.IsEnabled = (CallType == Extensions.TypeEnum.Incoming || CallType == Extensions.TypeEnum.Shifting); //this.checkBoxEtaBerthFixed.IsEnabled = (CallType == Extensions.TypeEnum.Incoming || CallType == Extensions.TypeEnum.Shifting);
this.datePickerETDBerth.IsEnabled = (CallType == Extensions.TypeEnum.Outgoing || CallType == Extensions.TypeEnum.Shifting); this.datePickerETDBerth.IsEnabled = (CallType == ShipcallType.Departure || CallType == ShipcallType.Shifting);
//this.checkBoxEtDBerthFixed.IsEnabled = (CallType == Extensions.TypeEnum.Outgoing || CallType == Extensions.TypeEnum.Shifting); //this.checkBoxEtDBerthFixed.IsEnabled = (CallType == Extensions.TypeEnum.Outgoing || CallType == Extensions.TypeEnum.Shifting);
this.datePickerLockTime.IsEnabled = (CallType == Extensions.TypeEnum.Incoming || CallType == Extensions.TypeEnum.Shifting); this.datePickerLockTime.IsEnabled = (CallType == ShipcallType.Arrival || CallType == ShipcallType.Shifting);
//this.checkBoxLockTimeFixed.IsEnabled = (CallType == Extensions.TypeEnum.Incoming || CallType == Extensions.TypeEnum.Shifting); //this.checkBoxLockTimeFixed.IsEnabled = (CallType == Extensions.TypeEnum.Incoming || CallType == Extensions.TypeEnum.Shifting);
this.datePickerZoneEntry.IsEnabled = false; this.datePickerZoneEntry.IsEnabled = false;
//this.checkBoxZoneEntryFixed.IsEnabled = false; //this.checkBoxZoneEntryFixed.IsEnabled = false;
this.textBoxRemarks.IsEnabled = true; this.textBoxRemarks.IsEnabled = true;
break; break;
case Extensions.ParticipantType.PILOT: case Extensions.ParticipantType.PILOT:
this.datePickerETABerth.IsEnabled = (CallType == Extensions.TypeEnum.Incoming); this.datePickerETABerth.IsEnabled = (CallType == ShipcallType.Arrival);
//this.checkBoxEtaBerthFixed.IsEnabled = (CallType == Extensions.TypeEnum.Incoming || CallType == Extensions.TypeEnum.Shifting); //this.checkBoxEtaBerthFixed.IsEnabled = (CallType == Extensions.TypeEnum.Incoming || CallType == Extensions.TypeEnum.Shifting);
this.datePickerETDBerth.IsEnabled = (CallType == Extensions.TypeEnum.Outgoing || CallType == Extensions.TypeEnum.Shifting); this.datePickerETDBerth.IsEnabled = (CallType == ShipcallType.Departure || CallType == ShipcallType.Shifting);
//this.checkBoxEtDBerthFixed.IsEnabled = (CallType == Extensions.TypeEnum.Outgoing || CallType == Extensions.TypeEnum.Shifting); //this.checkBoxEtDBerthFixed.IsEnabled = (CallType == Extensions.TypeEnum.Outgoing || CallType == Extensions.TypeEnum.Shifting);
this.datePickerLockTime.IsEnabled = (CallType == Extensions.TypeEnum.Incoming || CallType == Extensions.TypeEnum.Shifting); this.datePickerLockTime.IsEnabled = (CallType == ShipcallType.Arrival || CallType == ShipcallType.Shifting);
//this.checkBoxLockTimeFixed.IsEnabled = (CallType == Extensions.TypeEnum.Incoming || CallType == Extensions.TypeEnum.Shifting); //this.checkBoxLockTimeFixed.IsEnabled = (CallType == Extensions.TypeEnum.Incoming || CallType == Extensions.TypeEnum.Shifting);
this.datePickerZoneEntry.IsEnabled = (CallType == Extensions.TypeEnum.Incoming); this.datePickerZoneEntry.IsEnabled = (CallType == ShipcallType.Arrival);
//this.checkBoxZoneEntryFixed.IsEnabled = (CallType == Extensions.TypeEnum.Incoming); //this.checkBoxZoneEntryFixed.IsEnabled = (CallType == Extensions.TypeEnum.Incoming);
this.textBoxRemarks.IsEnabled = true; this.textBoxRemarks.IsEnabled = true;
break; break;

View File

@ -21,7 +21,7 @@ namespace BreCalClient
public Times Times { get; set; } = new(); public Times Times { get; set; } = new();
public Extensions.TypeEnum CallType { get; set; } public ShipcallType CallType { get; set; }
#endregion #endregion
@ -72,12 +72,12 @@ namespace BreCalClient
private void CopyToModel() private void CopyToModel()
{ {
switch(this.comboBoxPierside.SelectedIndex) this.Times.PierSide = this.comboBoxPierside.SelectedIndex switch
{ {
case 0: this.Times.PierSide = true; break; 0 => true,
case 1: this.Times.PierSide= false; break; 1 => false,
default: this.Times.PierSide = null; break; _ => null,
} };
this.Times.OperationsStart = this.datePickerOperationStart.Value; this.Times.OperationsStart = this.datePickerOperationStart.Value;
this.Times.OperationsEnd = this.datePickerOperationEnd.Value; this.Times.OperationsEnd = this.datePickerOperationEnd.Value;
this.Times.BerthId = (this.comboBoxBerth.SelectedItem != null) ? ((Berth)this.comboBoxBerth.SelectedItem).Id : null; this.Times.BerthId = (this.comboBoxBerth.SelectedItem != null) ? ((Berth)this.comboBoxBerth.SelectedItem).Id : null;
@ -97,12 +97,12 @@ namespace BreCalClient
switch (CallType) switch (CallType)
{ {
case Extensions.TypeEnum.Incoming: case ShipcallType.Arrival:
this.labelStart.FontWeight = FontWeights.Bold; this.labelStart.FontWeight = FontWeights.Bold;
this.datePickerOperationStart.ContextMenu.IsEnabled = false; this.datePickerOperationStart.ContextMenu.IsEnabled = false;
break; break;
case Extensions.TypeEnum.Outgoing: case ShipcallType.Departure:
case Extensions.TypeEnum.Shifting: case ShipcallType.Shifting:
this.labelEnd.FontWeight = FontWeights.Bold; this.labelEnd.FontWeight = FontWeights.Bold;
this.datePickerOperationEnd.ContextMenu.IsEnabled = false; this.datePickerOperationEnd.ContextMenu.IsEnabled = false;
break; break;
@ -114,11 +114,11 @@ namespace BreCalClient
{ {
if (this.Times.ParticipantId != App.Participant.Id) return; if (this.Times.ParticipantId != App.Participant.Id) return;
this.datePickerOperationStart.IsEnabled = (CallType == Extensions.TypeEnum.Incoming); this.datePickerOperationStart.IsEnabled = CallType == ShipcallType.Arrival;
this.datePickerOperationEnd.IsEnabled = (CallType == Extensions.TypeEnum.Outgoing) || (CallType == Extensions.TypeEnum.Shifting); this.datePickerOperationEnd.IsEnabled = (CallType == ShipcallType.Departure) || (CallType == ShipcallType.Shifting);
this.comboBoxBerth.IsEnabled = (CallType == Extensions.TypeEnum.Incoming); this.comboBoxBerth.IsEnabled = CallType == ShipcallType.Arrival;
this.comboBoxPierside.IsEnabled = (CallType == Extensions.TypeEnum.Incoming); this.comboBoxPierside.IsEnabled = CallType == ShipcallType.Arrival;
this.textBoxBerthRemarks.IsEnabled = (CallType == Extensions.TypeEnum.Incoming); this.textBoxBerthRemarks.IsEnabled = CallType == ShipcallType.Arrival;
this.textBoxRemarks.IsEnabled = true; this.textBoxRemarks.IsEnabled = true;
this.buttonOK.IsEnabled = true; this.buttonOK.IsEnabled = true;
} }

View File

@ -1,6 +1,6 @@
// Copyright (c) 2023 schick Informatik // Copyright (c) 2023 schick Informatik
// Description: some helpers // Description: some helpers
// //
using BreCalClient.misc.Model; using BreCalClient.misc.Model;
using System; using System;
@ -11,7 +11,7 @@ namespace BreCalClient
public static class Extensions public static class Extensions
{ {
#region Enum #region Enum
/// <summary> /// <summary>
/// Copied from models clunky I know /// Copied from models clunky I know
@ -47,16 +47,6 @@ namespace BreCalClient
ALLOW_BSMD = 1, ALLOW_BSMD = 1,
} }
/// <summary>
/// Should actually be defined in yaml
/// </summary>
public enum TypeEnum
{
Incoming = 1,
Outgoing = 2,
Shifting = 3
}
public enum SortOrder public enum SortOrder
{ {
SHIP_NAME, SHIP_NAME,
@ -76,20 +66,20 @@ namespace BreCalClient
public static bool IsFlagSet(this Participant participant, ParticipantFlag flag) public static bool IsFlagSet(this Participant participant, ParticipantFlag flag)
{ {
return (participant.Flags & (uint)flag) != 0; return (participant.Flags & (uint)flag) != 0;
} }
public static string Truncate(this string value, int maxLength) public static string Truncate(this string value, int maxLength)
{ {
if (string.IsNullOrEmpty(value)) return value; if (string.IsNullOrEmpty(value)) return value;
return value.Length <= maxLength ? value : value.Substring(0, maxLength); return value.Length <= maxLength ? value : value[..maxLength];
} }
public static string TruncateDots(this string value, int maxLength) public static string TruncateDots(this string value, int maxLength)
{ {
if(string.IsNullOrEmpty(value)) return value; if(string.IsNullOrEmpty(value)) return value;
if (value.Length <= maxLength) return value; if (value.Length <= maxLength) return value;
if (value.Length > (maxLength + 1)) return $"{value.Substring(0, maxLength)}.."; if (value.Length > (maxLength + 1)) return $"{value[..maxLength]}..";
return value.Substring(0, maxLength); return value[..maxLength];
} }
#endregion #endregion

View File

@ -13,7 +13,7 @@ namespace BreCalClient
string Title { get; set; } string Title { get; set; }
Extensions.TypeEnum CallType { get; set; } ShipcallType CallType { get; set; }
bool? ShowDialog(); bool? ShowDialog();

View File

@ -1,6 +1,6 @@
// Copyright (c) 2023 schick Informatik // Copyright (c) 2023 schick Informatik
// Description: Bremen calling main window // Description: Bremen calling main window
// //
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -18,11 +18,11 @@ using BreCalClient.misc.Model;
using static BreCalClient.Extensions; using static BreCalClient.Extensions;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using Newtonsoft.Json; using Newtonsoft.Json;
using System.Security.Principal;
using Polly; using Polly;
using System.Net.Http; using System.Net.Http;
using System.Net; using System.Net;
using Polly.Retry;
namespace BreCalClient namespace BreCalClient
{ {
@ -43,9 +43,14 @@ namespace BreCalClient
private readonly ConcurrentDictionary<int, ShipcallControlModel> _allShipcallsDict = new(); private readonly ConcurrentDictionary<int, ShipcallControlModel> _allShipcallsDict = new();
private readonly ConcurrentDictionary<int, ShipcallControl> _allShipCallsControlDict = new(); private readonly ConcurrentDictionary<int, ShipcallControl> _allShipCallsControlDict = new();
private readonly List<ShipcallControlModel> _visibleControlModels = new(); private readonly List<ShipcallControlModel> _visibleControlModels = new();
private readonly ShipcallApi _shipcallApi;
private readonly UserApi _userApi;
private readonly TimesApi _timesApi;
private readonly StaticApi _staticApi;
private readonly ShipApi _shipApi;
private readonly DefaultApi _api;
private CancellationTokenSource _tokenSource = new(); private CancellationTokenSource _tokenSource = new();
private LoginResult? _loginResult; private LoginResult? _loginResult;
private bool _refreshImmediately = false; private bool _refreshImmediately = false;
@ -75,8 +80,16 @@ namespace BreCalClient
public MainWindow() public MainWindow()
{ {
InitializeComponent(); InitializeComponent();
_api = new DefaultApi(Properties.Settings.Default.API_URL); _userApi = new UserApi(Properties.Settings.Default.API_URL);
_api.Configuration.ApiKeyPrefix["Authorization"] = "Bearer"; _userApi.Configuration.ApiKeyPrefix["Authorization"] = "Bearer";
_shipcallApi = new ShipcallApi(Properties.Settings.Default.API_URL);
_shipcallApi.Configuration.ApiKeyPrefix["Authorization"] = "Bearer";
_timesApi = new TimesApi(Properties.Settings.Default.API_URL);
_timesApi.Configuration.ApiKeyPrefix["Authorization"] = "Bearer";
_staticApi = new StaticApi(Properties.Settings.Default.API_URL);
_staticApi.Configuration.ApiKeyPrefix["Authorization"] = "Bearer";
_shipApi = new ShipApi(Properties.Settings.Default.API_URL);
_shipApi.Configuration.ApiKeyPrefix["Authorization"] = "Bearer";
const int maxDelayInMilliseconds = 32 * 1000; const int maxDelayInMilliseconds = 32 * 1000;
var jitterer = new Random(); var jitterer = new Random();
@ -111,14 +124,14 @@ namespace BreCalClient
labelVersion.Text = "V. " + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version; labelVersion.Text = "V. " + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
if (!string.IsNullOrEmpty(Properties.Settings.Default.APP_TITLE)) if (!string.IsNullOrEmpty(Properties.Settings.Default.APP_TITLE))
this.Title = Properties.Settings.Default.APP_TITLE; this.Title = Properties.Settings.Default.APP_TITLE;
searchFilterControl.SearchFilterChanged += SearchFilterControl_SearchFilterChanged; searchFilterControl.SearchFilterChanged += SearchFilterControl_SearchFilterChanged;
searchFilterControl.LogoImageClicked += () => searchFilterControl.LogoImageClicked += () =>
{ {
Process.Start("explorer", Properties.Settings.Default.LOGO_IMAGE_URL); Process.Start("explorer", Properties.Settings.Default.LOGO_IMAGE_URL);
}; };
this.comboBoxSortOrder.ItemsSource = Enum.GetValues(typeof(Extensions.SortOrder)); this.comboBoxSortOrder.ItemsSource = Enum.GetValues(typeof(Extensions.SortOrder));
this.comboBoxSortOrder.SelectedIndex = (int)_sortOrder; this.comboBoxSortOrder.SelectedIndex = (int)_sortOrder;
} }
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{ {
@ -140,13 +153,17 @@ namespace BreCalClient
try try
{ {
_loginResult = await _api.LoginPostAsync(_credentials); _loginResult = await _userApi.LoginAsync(_credentials);
if (_loginResult != null) if (_loginResult != null)
{ {
if (_loginResult.Id > 0) if (_loginResult.Id > 0)
{ {
this.busyIndicator.IsBusy = false; this.busyIndicator.IsBusy = false;
this._api.Configuration.ApiKey["Authorization"] = _loginResult.Token; this._userApi.Configuration.ApiKey["Authorization"] = _loginResult.Token;
this._shipcallApi.Configuration.ApiKey["Authorization"] = _loginResult.Token;
this._timesApi.Configuration.ApiKey["Authorization"] = _loginResult.Token;
this._staticApi.Configuration.ApiKey["Authorization"] = _loginResult.Token;
this._shipApi.Configuration.ApiKey["Authorization"] = _loginResult.Token;
this.LoadStaticLists(); this.LoadStaticLists();
this.labelUsername.Text = $"{_loginResult.FirstName} {_loginResult.LastName}"; this.labelUsername.Text = $"{_loginResult.FirstName} {_loginResult.LastName}";
} }
@ -179,12 +196,16 @@ namespace BreCalClient
bool result = false; bool result = false;
try try
{ {
_loginResult = _api.LoginPost(_credentials); _loginResult = _userApi.Login(_credentials);
if (_loginResult != null) if (_loginResult != null)
{ {
if (_loginResult.Id > 0) if (_loginResult.Id > 0)
{ {
this._api.Configuration.ApiKey["Authorization"] = _loginResult.Token; this._userApi.Configuration.ApiKey["Authorization"] = _loginResult.Token;
this._timesApi.Configuration.ApiKey["Authorization"] = _loginResult.Token;
this._shipcallApi.Configuration.ApiKey["Authorization"] = _loginResult.Token;
this._staticApi.Configuration.ApiKey["Authorization"] = _loginResult.Token;
this._shipApi.Configuration.ApiKey["Authorization"] = _loginResult.Token;
result = true; result = true;
} }
} }
@ -207,12 +228,16 @@ namespace BreCalClient
private void buttonNew_Click(object sender, RoutedEventArgs e) private void buttonNew_Click(object sender, RoutedEventArgs e)
{ {
NewWithModel(null); NewWithModel(null);
} }
private void NewWithModel(ShipcallControlModel? model) private void NewWithModel(ShipcallControlModel? model)
{ {
EditShipcallControl esc = new(); EditShipcallControl esc = new()
{
ShipEditingEnabled = App.Participant.IsTypeFlagSet(Extensions.ParticipantType.BSMD),
ShipApi = _shipApi
};
if (model != null) if (model != null)
esc.ShipcallModel = model; esc.ShipcallModel = model;
@ -228,7 +253,7 @@ namespace BreCalClient
esc.ShipcallModel.Shipcall?.Participants.Add(pa); esc.ShipcallModel.Shipcall?.Participants.Add(pa);
try try
{ {
this._api.ShipcallsPost(esc.ShipcallModel.Shipcall); // save new ship call this._shipcallApi.ShipcallCreate(esc.ShipcallModel.Shipcall); // save new ship call
this.AddShipcall(esc.ShipcallModel); this.AddShipcall(esc.ShipcallModel);
} }
catch (Exception ex) catch (Exception ex)
@ -240,11 +265,11 @@ namespace BreCalClient
_tokenSource.Cancel(); // force timer loop end _tokenSource.Cancel(); // force timer loop end
// if this was an arrival, create the matching departure call and open it // if this was an arrival, create the matching departure call and open it
if (esc.ShipcallModel.Shipcall?.Type == (int)Extensions.TypeEnum.Incoming) if (esc.ShipcallModel.Shipcall?.Type == ShipcallType.Arrival)
{ {
ShipcallControlModel scmOut = new(); ShipcallControlModel scmOut = new();
scmOut.Shipcall = new(); scmOut.Shipcall = new();
scmOut.Shipcall.Type = (int)Extensions.TypeEnum.Outgoing; scmOut.Shipcall.Type = ShipcallType.Departure;
scmOut.Shipcall.ShipId = esc.ShipcallModel.Shipcall.ShipId; scmOut.Shipcall.ShipId = esc.ShipcallModel.Shipcall.ShipId;
scmOut.Ship = esc.ShipcallModel.Ship; scmOut.Ship = esc.ShipcallModel.Ship;
DateTime eta = esc.ShipcallModel.Shipcall?.Eta ?? DateTime.Now; DateTime eta = esc.ShipcallModel.Shipcall?.Eta ?? DateTime.Now;
@ -283,7 +308,7 @@ namespace BreCalClient
}; };
try try
{ {
await _api.UserPutAsync(ud); await _userApi.UserUpdateAsync(ud);
MessageBox.Show(BreCalClient.Resources.Resources.textPasswordChanged, BreCalClient.Resources.Resources.textConfirmation, MessageBoxButton.OK, MessageBoxImage.Information); MessageBox.Show(BreCalClient.Resources.Resources.textPasswordChanged, BreCalClient.Resources.Resources.textConfirmation, MessageBoxButton.OK, MessageBoxImage.Information);
} }
catch (Exception ex) catch (Exception ex)
@ -324,25 +349,25 @@ namespace BreCalClient
this.UpdateUI(); this.UpdateUI();
} }
#endregion #endregion
#region network operations #region network operations
private async void LoadStaticLists() private async void LoadStaticLists()
{ {
BreCalLists.InitializeBerths(await _api.BerthsGetAsync()); BreCalLists.InitializeBerths(await _staticApi.BerthsGetAsync());
BreCalLists.InitializeShips(await _api.ShipsGetAsync()); BreCalLists.InitializeShips(await _shipApi.ShipsGetAsync());
BreCalLists.InitializeParticipants(await _api.ParticipantsGetAsync()); BreCalLists.InitializeParticipants(await _staticApi.ParticipantsGetAsync());
this.searchFilterControl.SetBerths(BreCalLists.Berths); this.searchFilterControl.SetBerths(BreCalLists.Berths);
foreach (Participant participant in BreCalLists.Participants) foreach (Participant participant in BreCalLists.Participants)
{ {
if (_loginResult?.ParticipantId == participant.Id) if (_loginResult?.ParticipantId == participant.Id)
{ {
App.Participant = participant; App.Participant = participant;
EnableControlsForParticipant(); EnableControlsForParticipant();
} }
} }
this.searchFilterControl.SetAgencies(BreCalLists.Participants_Agent); this.searchFilterControl.SetAgencies(BreCalLists.Participants_Agent);
@ -355,7 +380,7 @@ namespace BreCalClient
} }
_ = Task.Run(() => RefreshShipcalls()); _ = Task.Run(() => RefreshShipcalls());
} }
public async Task RefreshShipcalls() public async Task RefreshShipcalls()
{ {
@ -372,9 +397,9 @@ namespace BreCalClient
try try
{ {
if(this.searchPastDays != 0) if(this.searchPastDays != 0)
shipcalls = await _api.ShipcallsGetAsync(this.searchPastDays); shipcalls = await _shipcallApi.ShipcallsGetAsync(this.searchPastDays);
else else
shipcalls = await _api.ShipcallsGetAsync(); shipcalls = await _shipcallApi.ShipcallsGetAsync();
this.Dispatcher.Invoke(new Action(() => this.Dispatcher.Invoke(new Action(() =>
{ {
@ -383,7 +408,7 @@ namespace BreCalClient
})); }));
} }
catch (Exception ex) catch (Exception ex)
{ {
this.Dispatcher.Invoke(new Action(() => this.Dispatcher.Invoke(new Action(() =>
{ {
labelGeneralStatus.Text = $"Connection {ConnectionStatus.FAILED}"; labelGeneralStatus.Text = $"Connection {ConnectionStatus.FAILED}";
@ -401,7 +426,7 @@ namespace BreCalClient
foreach (Shipcall shipcall in shipcalls) foreach (Shipcall shipcall in shipcalls)
{ {
// load times for each shipcall // load times for each shipcall
List<Times> currentTimes = await _api.TimesGetAsync(shipcall.Id); List<Times> currentTimes = await _timesApi.TimesGetAsync(shipcall.Id);
if(!_allShipcallsDict.ContainsKey(shipcall.Id)) if(!_allShipcallsDict.ContainsKey(shipcall.Id))
{ {
@ -410,7 +435,7 @@ namespace BreCalClient
{ {
Shipcall = shipcall, Shipcall = shipcall,
Times = currentTimes Times = currentTimes
}; };
this.AddShipcall(scm); this.AddShipcall(scm);
} }
else else
@ -419,8 +444,8 @@ namespace BreCalClient
_allShipcallsDict[shipcall.Id].Shipcall = shipcall; _allShipcallsDict[shipcall.Id].Shipcall = shipcall;
_allShipcallsDict[shipcall.Id].Times = currentTimes; _allShipcallsDict[shipcall.Id].Times = currentTimes;
UpdateShipcall(_allShipcallsDict[shipcall.Id]); UpdateShipcall(_allShipcallsDict[shipcall.Id]);
} }
} }
List<int> existingIds = new(this._allShipcallsDict.Keys); List<int> existingIds = new(this._allShipcallsDict.Keys);
@ -456,7 +481,8 @@ namespace BreCalClient
Shipcall shipcall = scm.Shipcall; Shipcall shipcall = scm.Shipcall;
if (BreCalLists.ShipLookupDict.ContainsKey(shipcall.ShipId)) if (BreCalLists.ShipLookupDict.ContainsKey(shipcall.ShipId))
scm.Ship = BreCalLists.ShipLookupDict[shipcall.ShipId].Ship; scm.Ship = BreCalLists.ShipLookupDict[shipcall.ShipId].Ship;
if (shipcall.Type == 1)
if (shipcall.Type == ShipcallType.Arrival)
{ {
if (BreCalLists.BerthLookupDict.ContainsKey(shipcall.ArrivalBerthId ?? 0)) if (BreCalLists.BerthLookupDict.ContainsKey(shipcall.ArrivalBerthId ?? 0))
scm.Berth = BreCalLists.BerthLookupDict[shipcall.ArrivalBerthId ?? 0].Name; scm.Berth = BreCalLists.BerthLookupDict[shipcall.ArrivalBerthId ?? 0].Name;
@ -473,7 +499,7 @@ namespace BreCalClient
ShipcallControl sc = new() ShipcallControl sc = new()
{ {
Height = 120, Height = 120,
ShipcallControlModel = scm ShipcallControlModel = scm
}; };
sc.EditTimesRequested += Sc_EditTimesRequested; sc.EditTimesRequested += Sc_EditTimesRequested;
sc.EditRequested += Sc_EditRequested; sc.EditRequested += Sc_EditRequested;
@ -481,7 +507,7 @@ namespace BreCalClient
sc.RefreshData(); sc.RefreshData();
this._allShipCallsControlDict[scm.Shipcall.Id] = sc; this._allShipCallsControlDict[scm.Shipcall.Id] = sc;
}); });
} }
private static void UpdateShipcall(ShipcallControlModel scm) private static void UpdateShipcall(ShipcallControlModel scm)
{ {
@ -489,7 +515,8 @@ namespace BreCalClient
Shipcall shipcall = scm.Shipcall; Shipcall shipcall = scm.Shipcall;
if (BreCalLists.ShipLookupDict.ContainsKey(shipcall.ShipId)) if (BreCalLists.ShipLookupDict.ContainsKey(shipcall.ShipId))
scm.Ship = BreCalLists.ShipLookupDict[shipcall.ShipId].Ship; scm.Ship = BreCalLists.ShipLookupDict[shipcall.ShipId].Ship;
if (shipcall.Type == 1)
if (shipcall.Type == ShipcallType.Arrival)
{ {
if (BreCalLists.BerthLookupDict.ContainsKey(shipcall.ArrivalBerthId ?? 0)) if (BreCalLists.BerthLookupDict.ContainsKey(shipcall.ArrivalBerthId ?? 0))
scm.Berth = BreCalLists.BerthLookupDict[shipcall.ArrivalBerthId ?? 0].Name; scm.Berth = BreCalLists.BerthLookupDict[shipcall.ArrivalBerthId ?? 0].Name;
@ -513,7 +540,7 @@ namespace BreCalClient
_visibleControlModels.Remove(removeModel); _visibleControlModels.Remove(removeModel);
this._allShipCallsControlDict.Remove(shipcallId, out _); this._allShipCallsControlDict.Remove(shipcallId, out _);
this._allShipcallsDict.Remove(shipcallId, out _); this._allShipcallsDict.Remove(shipcallId, out _);
} }
private void FilterShipcalls() private void FilterShipcalls()
@ -543,8 +570,8 @@ namespace BreCalClient
if(sfm.Berths.Count > 0 ) if(sfm.Berths.Count > 0 )
{ {
this._visibleControlModels.RemoveAll(x => (!sfm.Berths.Contains((x.Shipcall?.ArrivalBerthId) ?? -1) && (x.Shipcall?.Type == (int) Extensions.TypeEnum.Incoming)) || this._visibleControlModels.RemoveAll(x => (!sfm.Berths.Contains((x.Shipcall?.ArrivalBerthId) ?? -1) && (x.Shipcall?.Type == ShipcallType.Arrival)) ||
(!sfm.Berths.Contains((x.Shipcall?.DepartureBerthId) ?? -1) && (x.Shipcall?.Type != (int) Extensions.TypeEnum.Incoming))); (!sfm.Berths.Contains((x.Shipcall?.DepartureBerthId) ?? -1) && (x.Shipcall?.Type != ShipcallType.Arrival)));
} }
if(sfm.Agencies.Count > 0 ) if(sfm.Agencies.Count > 0 )
@ -557,12 +584,12 @@ namespace BreCalClient
return !sfm.Agencies.Contains(agency.Id); return !sfm.Agencies.Contains(agency.Id);
} }
return true; return true;
}); });
} }
if(sfm.Categories.Count > 0 ) if(sfm.Categories.Count > 0 )
{ {
_ = this._visibleControlModels.RemoveAll(x => !sfm.Categories.Contains((x.Shipcall?.Type) ?? -1)); _ = this._visibleControlModels.RemoveAll(x => { if (x.Shipcall == null) return false; else return !sfm.Categories.Contains(x.Shipcall.Type); });
} }
if(!string.IsNullOrEmpty(sfm.SearchString)) if(!string.IsNullOrEmpty(sfm.SearchString))
@ -594,28 +621,28 @@ namespace BreCalClient
{ {
_ = this._visibleControlModels.RemoveAll(x => x.Shipcall?.Canceled ?? false); _ = this._visibleControlModels.RemoveAll(x => x.Shipcall?.Canceled ?? false);
} }
switch(this._sortOrder) switch(this._sortOrder)
{ {
case Extensions.SortOrder.SHIP_NAME: case Extensions.SortOrder.SHIP_NAME:
this._visibleControlModels.Sort((x, y) => { if (x.Ship == null) return 0; if (y.Ship == null) return 0; return x.Ship.Name.CompareTo(y.Ship.Name); }); this._visibleControlModels.Sort((x, y) => { if (x.Ship == null) return 0; if (y.Ship == null) return 0; return x.Ship.Name.CompareTo(y.Ship.Name); });
break; break;
case Extensions.SortOrder.MODIFIED: case Extensions.SortOrder.MODIFIED:
this._visibleControlModels.Sort((x, y) => { if (x.Shipcall == null) return 0; if (y.Shipcall == null) return 0; return DateTime.Compare(x.Shipcall.Modified ?? x.Shipcall.Created, y.Shipcall.Modified ?? x.Shipcall.Created); }); this._visibleControlModels.Sort((x, y) => { if (x.Shipcall == null) return 0; if (y.Shipcall == null) return 0; return DateTime.Compare(x.Shipcall.Modified ?? x.Shipcall.Created, y.Shipcall.Modified ?? x.Shipcall.Created); });
break; break;
case Extensions.SortOrder.ETA_ETD: case Extensions.SortOrder.ETA_ETD:
this._visibleControlModels.Sort((x, y) => this._visibleControlModels.Sort((x, y) =>
{ {
if (x.Shipcall == null) return 0; if (x.Shipcall == null) return 0;
if (y.Shipcall == null) return 0; if (y.Shipcall == null) return 0;
DateTime xDate = (x.Shipcall.Type == (int) Extensions.TypeEnum.Incoming) ? x.Eta ?? DateTime.Now : x.Etd ?? DateTime.Now; DateTime xDate = (x.Shipcall.Type == ShipcallType.Arrival) ? x.Eta ?? DateTime.Now : x.Etd ?? DateTime.Now;
DateTime yDate = (y.Shipcall.Type == (int) Extensions.TypeEnum.Incoming) ? y.Eta ?? DateTime.Now : y.Etd ?? DateTime.Now; DateTime yDate = (y.Shipcall.Type == ShipcallType.Arrival) ? y.Eta ?? DateTime.Now : y.Etd ?? DateTime.Now;
return DateTime.Compare(xDate, yDate); return DateTime.Compare(xDate, yDate);
}); });
break; break;
default: default:
break; break;
} }
} }
@ -660,12 +687,14 @@ namespace BreCalClient
#region control event handler #region control event handler
private async void Sc_EditRequested(ShipcallControl obj) private async void Sc_EditRequested(ShipcallControl obj)
{ {
if (obj.ShipcallControlModel != null) if (obj.ShipcallControlModel != null)
{ {
EditShipcallControl esc = new() EditShipcallControl esc = new()
{ {
ShipcallModel = obj.ShipcallControlModel ShipcallModel = obj.ShipcallControlModel,
ShipApi = _shipApi,
ShipEditingEnabled = App.Participant.IsTypeFlagSet(Extensions.ParticipantType.BSMD)
}; };
if(esc.ShowDialog() ?? false) if(esc.ShowDialog() ?? false)
@ -673,10 +702,10 @@ namespace BreCalClient
try try
{ {
obj.ShipcallControlModel.Shipcall?.Participants.Clear(); obj.ShipcallControlModel.Shipcall?.Participants.Clear();
obj.ShipcallControlModel.UpdateTimesAssignments(this._api); obj.ShipcallControlModel.UpdateTimesAssignments(this._timesApi);
foreach(ParticipantAssignment pa in obj.ShipcallControlModel.AssignedParticipants.Values) foreach(ParticipantAssignment pa in obj.ShipcallControlModel.AssignedParticipants.Values)
obj.ShipcallControlModel.Shipcall?.Participants.Add(pa); obj.ShipcallControlModel.Shipcall?.Participants.Add(pa);
await _api.ShipcallsPutAsync(obj.ShipcallControlModel.Shipcall); await _shipcallApi.ShipcallUpdateAsync(obj.ShipcallControlModel.Shipcall);
obj.RefreshData(); obj.RefreshData();
_refreshImmediately = true; _refreshImmediately = true;
_tokenSource.Cancel(); _tokenSource.Cancel();
@ -689,7 +718,7 @@ namespace BreCalClient
} }
} }
private async void Sc_EditTimesRequested(ShipcallControl obj, Times? times, Extensions.ParticipantType participantType) private async void Sc_EditTimesRequested(ShipcallControl obj, Times? times, ParticipantType participantType)
{ {
if( obj.ShipcallControlModel == null) { return; } if( obj.ShipcallControlModel == null) { return; }
@ -700,13 +729,13 @@ namespace BreCalClient
etc.Title = obj.ShipcallControlModel.Title; etc.Title = obj.ShipcallControlModel.Title;
if(obj.ShipcallControlModel.Shipcall != null) if(obj.ShipcallControlModel.Shipcall != null)
etc.CallType = (TypeEnum) obj.ShipcallControlModel.Shipcall.Type; etc.CallType = (ShipcallType) obj.ShipcallControlModel.Shipcall.Type;
bool wasEdit = false; bool wasEdit = false;
if (times != null) if (times != null)
{ {
etc.Times = times; etc.Times = times;
wasEdit = true; wasEdit = true;
} }
else else
{ {
@ -725,7 +754,7 @@ namespace BreCalClient
{ {
if (wasEdit) if (wasEdit)
{ {
await _api.TimesPutAsync(etc.Times); await _timesApi.TimesUpdateAsync(etc.Times);
} }
else else
{ {
@ -734,7 +763,7 @@ namespace BreCalClient
{ {
etc.Times.ShipcallId = obj.ShipcallControlModel.Shipcall.Id; etc.Times.ShipcallId = obj.ShipcallControlModel.Shipcall.Id;
} }
Id apiResultId = await _api.TimesPostAsync(etc.Times); Id apiResultId = await _timesApi.TimesCreateAsync(etc.Times);
etc.Times.Id = apiResultId.VarId; etc.Times.Id = apiResultId.VarId;
obj.ShipcallControlModel?.Times.Add(etc.Times); obj.ShipcallControlModel?.Times.Add(etc.Times);
} }
@ -753,13 +782,13 @@ namespace BreCalClient
IEditShipcallTimesControl? editControl = null; IEditShipcallTimesControl? editControl = null;
switch(sc.ShipcallControlModel?.Shipcall?.Type) switch(sc.ShipcallControlModel?.Shipcall?.Type)
{ {
case (int)TypeEnum.Incoming: case ShipcallType.Arrival:
editControl = new EditTimesAgencyIncomingControl(); editControl = new EditTimesAgencyIncomingControl();
break; break;
case (int)TypeEnum.Outgoing: case ShipcallType.Departure:
editControl = new EditTimesAgencyOutgoingControl(); editControl = new EditTimesAgencyOutgoingControl();
break; break;
case (int)TypeEnum.Shifting: case ShipcallType.Shifting:
editControl = new EditTimesAgencyShiftingControl(); editControl = new EditTimesAgencyShiftingControl();
break; break;
} }
@ -772,7 +801,7 @@ namespace BreCalClient
{ {
editControl.Times = times; editControl.Times = times;
wasEdit = true; wasEdit = true;
} }
else else
{ {
if(editControl.ShipcallModel.AssignedParticipants.ContainsKey(ParticipantType.AGENCY)) if(editControl.ShipcallModel.AssignedParticipants.ContainsKey(ParticipantType.AGENCY))
@ -783,7 +812,7 @@ namespace BreCalClient
{ {
try try
{ {
sc.ShipcallControlModel?.UpdateTimesAssignments(_api); // if the agent changed the assignment of the participant to another sc.ShipcallControlModel?.UpdateTimesAssignments(_timesApi); // if the agent changed the assignment of the participant to another
// always try to be the agent, even if we are BSMD // always try to be the agent, even if we are BSMD
if (editControl.ShipcallModel.AssignedParticipants.ContainsKey(ParticipantType.AGENCY)) if (editControl.ShipcallModel.AssignedParticipants.ContainsKey(ParticipantType.AGENCY))
@ -797,15 +826,15 @@ namespace BreCalClient
if (wasEdit) if (wasEdit)
{ {
await _api.TimesPutAsync(editControl.Times); await _timesApi.TimesUpdateAsync(editControl.Times);
} }
else else
{ {
if ((sc.ShipcallControlModel != null) && (sc.ShipcallControlModel.Shipcall != null)) if ((sc.ShipcallControlModel != null) && (sc.ShipcallControlModel.Shipcall != null))
{ {
editControl.Times.ShipcallId = sc.ShipcallControlModel.Shipcall.Id; editControl.Times.ShipcallId = sc.ShipcallControlModel.Shipcall.Id;
} }
Id resultAPI_Id = await _api.TimesPostAsync(editControl.Times); Id resultAPI_Id = await _timesApi.TimesCreateAsync(editControl.Times);
editControl.Times.Id = resultAPI_Id.VarId; editControl.Times.Id = resultAPI_Id.VarId;
sc.ShipcallControlModel?.Times.Add(editControl.Times); sc.ShipcallControlModel?.Times.Add(editControl.Times);
@ -813,7 +842,7 @@ namespace BreCalClient
editControl.ShipcallModel.Shipcall?.Participants.Clear(); editControl.ShipcallModel.Shipcall?.Participants.Clear();
foreach (ParticipantAssignment pa in editControl.ShipcallModel.AssignedParticipants.Values) foreach (ParticipantAssignment pa in editControl.ShipcallModel.AssignedParticipants.Values)
editControl.ShipcallModel.Shipcall?.Participants.Add(pa); editControl.ShipcallModel.Shipcall?.Participants.Add(pa);
await _api.ShipcallsPutAsync(editControl.ShipcallModel.Shipcall); await _shipcallApi.ShipcallUpdateAsync(editControl.ShipcallModel.Shipcall);
_refreshImmediately = true; _refreshImmediately = true;
_tokenSource.Cancel(); _tokenSource.Cancel();
} }
@ -846,7 +875,7 @@ namespace BreCalClient
if (App.Participant.IsTypeFlagSet(Extensions.ParticipantType.BSMD)) if (App.Participant.IsTypeFlagSet(Extensions.ParticipantType.BSMD))
this.buttonNew.Visibility = Visibility.Visible; this.buttonNew.Visibility = Visibility.Visible;
} }
private void Hyperlink_RequestNavigate(object sender, System.Windows.Navigation.RequestNavigateEventArgs e) private void Hyperlink_RequestNavigate(object sender, System.Windows.Navigation.RequestNavigateEventArgs e)
{ {
Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri) { UseShellExecute = true }); Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri) { UseShellExecute = true });

View File

@ -60,6 +60,16 @@ namespace BreCalClient.Resources {
} }
} }
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
public static byte[] add {
get {
object obj = ResourceManager.GetObject("add", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary> /// <summary>
/// Looks up a localized resource of type System.Byte[]. /// Looks up a localized resource of type System.Byte[].
/// </summary> /// </summary>
@ -180,6 +190,16 @@ namespace BreCalClient.Resources {
} }
} }
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
public static byte[] edit {
get {
object obj = ResourceManager.GetObject("edit", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary> /// <summary>
/// Looks up a localized resource of type System.Byte[]. /// Looks up a localized resource of type System.Byte[].
/// </summary> /// </summary>
@ -220,6 +240,15 @@ namespace BreCalClient.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Add.
/// </summary>
public static string textAdd {
get {
return ResourceManager.GetString("textAdd", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Agencies. /// Looks up a localized string similar to Agencies.
/// </summary> /// </summary>
@ -409,6 +438,24 @@ namespace BreCalClient.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Delete.
/// </summary>
public static string textDelete {
get {
return ResourceManager.GetString("textDelete", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Deleted.
/// </summary>
public static string textDeleted {
get {
return ResourceManager.GetString("textDeleted", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Departure terminal. /// Looks up a localized string similar to Departure terminal.
/// </summary> /// </summary>
@ -427,6 +474,24 @@ namespace BreCalClient.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Edit.
/// </summary>
public static string textEdit {
get {
return ResourceManager.GetString("textEdit", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Edit ship.
/// </summary>
public static string textEditShip {
get {
return ResourceManager.GetString("textEditShip", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Edit ship call. /// Looks up a localized string similar to Edit ship call.
/// </summary> /// </summary>
@ -436,6 +501,15 @@ namespace BreCalClient.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Edit ships.
/// </summary>
public static string textEditShips {
get {
return ResourceManager.GetString("textEditShips", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Edit times. /// Looks up a localized string similar to Edit times.
/// </summary> /// </summary>
@ -904,6 +978,15 @@ namespace BreCalClient.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Tug company.
/// </summary>
public static string textTugCompany {
get {
return ResourceManager.GetString("textTugCompany", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Tug required. /// Looks up a localized string similar to Tug required.
/// </summary> /// </summary>

View File

@ -427,4 +427,25 @@
<data name="textStarboard" xml:space="preserve"> <data name="textStarboard" xml:space="preserve">
<value>Steuerbord</value> <value>Steuerbord</value>
</data> </data>
<data name="textAdd" xml:space="preserve">
<value>Hinzufügen</value>
</data>
<data name="textDelete" xml:space="preserve">
<value>Löschen</value>
</data>
<data name="textEdit" xml:space="preserve">
<value>Bearbeiten</value>
</data>
<data name="textEditShips" xml:space="preserve">
<value>Schiffe anlegen / bearbeiten</value>
</data>
<data name="textDeleted" xml:space="preserve">
<value>Gelöscht</value>
</data>
<data name="textEditShip" xml:space="preserve">
<value>Schiff bearbeiten</value>
</data>
<data name="textTugCompany" xml:space="preserve">
<value>Schlepper-Reederei</value>
</data>
</root> </root>

View File

@ -118,6 +118,9 @@
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader> </resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> <assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="add" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>add.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="arrow_down_green" type="System.Resources.ResXFileRef, System.Windows.Forms"> <data name="arrow_down_green" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>arrow_down_green.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> <value>arrow_down_green.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data> </data>
@ -154,6 +157,9 @@
<data name="delete2" type="System.Resources.ResXFileRef, System.Windows.Forms"> <data name="delete2" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>delete2.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> <value>delete2.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data> </data>
<data name="edit" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>edit.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="emergency_stop_button" type="System.Resources.ResXFileRef, System.Windows.Forms"> <data name="emergency_stop_button" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>emergency_stop_button.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> <value>emergency_stop_button.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data> </data>
@ -166,6 +172,9 @@
<data name="sign_warning" type="System.Resources.ResXFileRef, System.Windows.Forms"> <data name="sign_warning" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>sign_warning.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> <value>sign_warning.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data> </data>
<data name="textAdd" xml:space="preserve">
<value>Add</value>
</data>
<data name="textAgencies" xml:space="preserve"> <data name="textAgencies" xml:space="preserve">
<value>Agencies</value> <value>Agencies</value>
</data> </data>
@ -229,15 +238,30 @@
<data name="textConfirmation" xml:space="preserve"> <data name="textConfirmation" xml:space="preserve">
<value>Confirmation</value> <value>Confirmation</value>
</data> </data>
<data name="textDelete" xml:space="preserve">
<value>Delete</value>
</data>
<data name="textDeleted" xml:space="preserve">
<value>Deleted</value>
</data>
<data name="textDepartureTerminal" xml:space="preserve"> <data name="textDepartureTerminal" xml:space="preserve">
<value>Departure terminal</value> <value>Departure terminal</value>
</data> </data>
<data name="textDraft" xml:space="preserve"> <data name="textDraft" xml:space="preserve">
<value>Draft (m)</value> <value>Draft (m)</value>
</data> </data>
<data name="textEdit" xml:space="preserve">
<value>Edit</value>
</data>
<data name="textEditShip" xml:space="preserve">
<value>Edit ship</value>
</data>
<data name="textEditShipcall" xml:space="preserve"> <data name="textEditShipcall" xml:space="preserve">
<value>Edit ship call</value> <value>Edit ship call</value>
</data> </data>
<data name="textEditShips" xml:space="preserve">
<value>Edit ships</value>
</data>
<data name="textEditTimes" xml:space="preserve"> <data name="textEditTimes" xml:space="preserve">
<value>Edit times</value> <value>Edit times</value>
</data> </data>
@ -394,6 +418,9 @@
<data name="textTug" xml:space="preserve"> <data name="textTug" xml:space="preserve">
<value>Tug</value> <value>Tug</value>
</data> </data>
<data name="textTugCompany" xml:space="preserve">
<value>Tug company</value>
</data>
<data name="textTugRequired" xml:space="preserve"> <data name="textTugRequired" xml:space="preserve">
<value>Tug required</value> <value>Tug required</value>
</data> </data>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -100,8 +100,8 @@ namespace BreCalClient
} }
if(sfm.Categories != null) if(sfm.Categories != null)
{ {
foreach(int category in sfm.Categories) foreach(ShipcallType category in sfm.Categories)
this.comboBoxCategories.SelectedItems.Add((Extensions.TypeEnum)category); this.comboBoxCategories.SelectedItems.Add(category);
} }
if (sfm.SearchString != null) this.textBoxSearch.Text = sfm.SearchString; if (sfm.SearchString != null) this.textBoxSearch.Text = sfm.SearchString;
this.upDownShiplengthFrom.Value = sfm.ShipLengthFrom; this.upDownShiplengthFrom.Value = sfm.ShipLengthFrom;
@ -124,7 +124,7 @@ namespace BreCalClient
private void UserControl_Loaded(object sender, System.Windows.RoutedEventArgs e) private void UserControl_Loaded(object sender, System.Windows.RoutedEventArgs e)
{ {
this.comboBoxCategories.ItemsSource = Enum.GetValues(typeof(Extensions.TypeEnum)); this.comboBoxCategories.ItemsSource = Enum.GetValues(typeof(ShipcallType));
} }
private void datePickerETAFrom_SelectedDateChanged(object sender, SelectionChangedEventArgs e) private void datePickerETAFrom_SelectedDateChanged(object sender, SelectionChangedEventArgs e)
@ -142,7 +142,7 @@ namespace BreCalClient
private void comboBoxCategories_ItemSelectionChanged(object sender, Xceed.Wpf.Toolkit.Primitives.ItemSelectionChangedEventArgs e) private void comboBoxCategories_ItemSelectionChanged(object sender, Xceed.Wpf.Toolkit.Primitives.ItemSelectionChangedEventArgs e)
{ {
_model.Categories.Clear(); _model.Categories.Clear();
foreach(int category in comboBoxCategories.SelectedItems) foreach(ShipcallType category in comboBoxCategories.SelectedItems)
_model.Categories.Add(category); _model.Categories.Add(category);
SearchFilterChanged?.Invoke(); SearchFilterChanged?.Invoke();

View File

@ -19,7 +19,7 @@ namespace BreCalClient
public DateTime? EtaTo { get; set; } public DateTime? EtaTo { get; set; }
public List<int> Categories { get; set; } = new(); public List<ShipcallType> Categories { get; set; } = new();
public List<int> Agencies { get; set; } = new(); public List<int> Agencies { get; set; } = new();

View File

@ -0,0 +1,41 @@
<Window x:Class="BreCalClient.ShipListDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:p = "clr-namespace:BreCalClient.Resources"
xmlns:local="clr-namespace:BreCalClient"
mc:Ignorable="d" Left="{local:SettingBinding W1Left}" Top="{local:SettingBinding W1Top}"
Height="490" Width="800" ResizeMode="CanResize" Icon="Resources/containership.ico" Loaded="Window_Loaded">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="28" />
</Grid.RowDefinitions>
<local:ENIDataGrid x:Name="dataGridShips" Grid.Row="0" SelectionMode="Single" IsReadOnly="True" AlternatingRowBackground="LightBlue" AutoGenerateColumns="False"
CanUserAddRows="False" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch">
<local:ENIDataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Style.Triggers>
<DataTrigger Binding="{Binding Ship.Deleted}" Value="True">
<Setter Property="Foreground" Value="DarkGray"/>
</DataTrigger>
</Style.Triggers>
</Style>
</local:ENIDataGrid.RowStyle>
<DataGrid.Columns>
<DataGridTextColumn Header="Id" Binding="{Binding Path=Ship.Id}" IsReadOnly="True"/>
<DataGridTextColumn Header="Name" Binding="{Binding Path=Ship.Name}" IsReadOnly="True"/>
<DataGridCheckBoxColumn Header="{x:Static p:Resources.textTug}" Binding="{Binding Path=Ship.IsTug, Mode=OneWay}" IsReadOnly="True"/>
<DataGridTextColumn Header="{x:Static p:Resources.textTugCompany}" Binding="{Binding Path=TugCompany, Mode=OneWay}" IsReadOnly="True"/>
<DataGridTextColumn Header="IMO" Binding="{Binding Path=Ship.Imo}" IsReadOnly="True"/>
<DataGridTextColumn Header="{x:Static p:Resources.textCallsign}" Binding="{Binding Path=Ship.Callsign}" IsReadOnly="True"/>
<DataGridTextColumn Header="{x:Static p:Resources.textLength}" Binding="{Binding Path=Ship.Length}" IsReadOnly="True"/>
<DataGridTextColumn Header="{x:Static p:Resources.textWidth}" Binding="{Binding Path=Ship.Width}" IsReadOnly="True"/>
<DataGridCheckBoxColumn Header="{x:Static p:Resources.textDeleted}" Binding="{Binding Path=Ship.Deleted, Mode=OneWay}" IsReadOnly="True" />
</DataGrid.Columns>
</local:ENIDataGrid>
<Button Grid.Row="1" x:Name="buttonClose" Content="{x:Static p:Resources.textClose}" HorizontalAlignment="Right" Margin="2" Click="buttonClose_Click"/>
</Grid>
</Window>

View File

@ -0,0 +1,116 @@
// Copyright (c) 2023 schick Informatik
// Description: Administration screen for ships
//
using BreCalClient.misc.Api;
using BreCalClient.misc.Model;
using System;
using System.Windows;
namespace BreCalClient
{
/// <summary>
/// Interaction logic for ShipListDialog.xaml
/// </summary>
public partial class ShipListDialog : Window
{
public ShipListDialog()
{
InitializeComponent();
}
#region Properties
public ShipApi? ShipApi { get; set; }
#endregion
#region Event handler
private void buttonClose_Click(object sender, RoutedEventArgs e)
{
this.Close();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
this.dataGridShips.Initialize();
this.dataGridShips.ItemsSource = BreCalLists.AllShips;
this.dataGridShips.CreateRequested += DataGridShips_CreateRequested; ;
this.dataGridShips.EditRequested += DataGridShips_EditRequested;
this.dataGridShips.DeleteRequested += DataGridShips_DeleteRequested;
}
private async void DataGridShips_DeleteRequested(object obj)
{
if (obj is ShipModel shipmodel)
{
if(this.ShipApi != null)
await this.ShipApi.ShipDeleteAsync(shipmodel.Ship.Id);
BreCalLists.Ships.Remove(shipmodel); // remove from "selectable" ships
shipmodel.Ship.Deleted = true; // set deleted marker on working instance
}
}
private async void DataGridShips_EditRequested(object obj)
{
if (obj is ShipModel shipmodel)
{
EditShipDialog esd = new()
{
Ship = shipmodel.Ship
};
esd.Participants.AddRange(BreCalLists.Participants_Tug);
if (esd.ShowDialog() ?? false)
{
try
{
if (this.ShipApi != null)
{
Id tmpId = await this.ShipApi.ShipUpdateAsync(shipmodel.Ship);
}
this.dataGridShips.ItemsSource = null;
this.dataGridShips.ItemsSource = BreCalLists.AllShips;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
}
private void DataGridShips_CreateRequested()
{
ShipModel shipModel = new ShipModel(new Ship());
EditShipDialog esd = new()
{
Ship = shipModel.Ship
};
esd.Participants.AddRange(BreCalLists.Participants_Tug);
if(esd.ShowDialog() ?? false)
{
try
{
this.ShipApi?.ShipsCreateAsync(shipModel.Ship);
this.dataGridShips.ItemsSource = null;
BreCalLists.AllShips.Add(shipModel);
BreCalLists.Ships.Add(shipModel);
this.dataGridShips.ItemsSource = BreCalLists.AllShips;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
#endregion
}
}

View File

@ -16,6 +16,16 @@ namespace BreCalClient
public Ship Ship { get; private set; } public Ship Ship { get; private set; }
public string TugCompany
{
get { if(this.Ship.ParticipantId.HasValue)
{
return BreCalLists.ParticipantLookupDict[this.Ship.ParticipantId.Value].Name;
}
return "";
}
}
public override string ToString() public override string ToString()
{ {
return String.Format("{0} ({1})", this.Ship.Name, this.Ship.Imo); return String.Format("{0} ({1})", this.Ship.Name, this.Ship.Imo);

View File

@ -1,6 +1,6 @@
// Copyright (c) 2023 schick Informatik // Copyright (c) 2023 schick Informatik
// Description: Show general shipcall info // Description: Show general shipcall info
// //
using BreCalClient.misc.Model; using BreCalClient.misc.Model;
using log4net; using log4net;
@ -53,7 +53,7 @@ namespace BreCalClient
/// <summary> /// <summary>
/// this is our datasource /// this is our datasource
/// </summary> /// </summary>
public ShipcallControlModel? ShipcallControlModel { get; set; } public ShipcallControlModel? ShipcallControlModel { get; set; }
#endregion #endregion
@ -131,7 +131,7 @@ namespace BreCalClient
{ {
this.labelTerminal.FontWeight = FontWeights.Bold; this.labelTerminal.FontWeight = FontWeights.Bold;
this.labelTerminal.Foreground = Brushes.LightYellow; this.labelTerminal.Foreground = Brushes.LightYellow;
} }
else else
{ {
this.labelTerminal.FontWeight = FontWeights.Normal; this.labelTerminal.FontWeight = FontWeights.Normal;
@ -209,13 +209,13 @@ namespace BreCalClient
// this.labelShipName.Content = this.ShipcallControlModel?.Ship?.Name; // this.labelShipName.Content = this.ShipcallControlModel?.Ship?.Name;
switch (this.ShipcallControlModel?.Shipcall?.Type) switch (this.ShipcallControlModel?.Shipcall?.Type)
{ {
case 1: // incoming case ShipcallType.Arrival: // incoming
this.imageShipcallType.Source = new BitmapImage(new Uri("pack://application:,,,/BreCalDevelClient;component/Resources/arrow_down_red.png")); this.imageShipcallType.Source = new BitmapImage(new Uri("pack://application:,,,/BreCalDevelClient;component/Resources/arrow_down_red.png"));
break; break;
case 2: // outgoing case ShipcallType.Departure: // outgoing
this.imageShipcallType.Source = new BitmapImage(new Uri("pack://application:,,,/BreCalDevelClient;component/Resources/arrow_up_blue.png")); this.imageShipcallType.Source = new BitmapImage(new Uri("pack://application:,,,/BreCalDevelClient;component/Resources/arrow_up_blue.png"));
break; break;
case 3: // shifting case ShipcallType.Shifting: // shifting
this.imageShipcallType.Source = new BitmapImage(new Uri("pack://application:,,,/BreCalDevelClient;component/Resources/arrow_right_green.png")); this.imageShipcallType.Source = new BitmapImage(new Uri("pack://application:,,,/BreCalDevelClient;component/Resources/arrow_right_green.png"));
break; break;
default: default:
@ -274,11 +274,11 @@ namespace BreCalClient
this.textBlockBerth.Text = this.ShipcallControlModel?.Berth; this.textBlockBerth.Text = this.ShipcallControlModel?.Berth;
this.textBlockCallsign.Text = this.ShipcallControlModel?.Ship?.Callsign; this.textBlockCallsign.Text = this.ShipcallControlModel?.Ship?.Callsign;
if (this.ShipcallControlModel?.Shipcall?.Type == 1) if (this.ShipcallControlModel?.Shipcall?.Type == ShipcallType.Arrival)
{ {
this.textBlockETA.Text = this.ShipcallControlModel?.Shipcall?.Eta?.ToString("dd.MM. HH:mm"); this.textBlockETA.Text = this.ShipcallControlModel?.Shipcall?.Eta?.ToString("dd.MM. HH:mm");
} }
if ((this.ShipcallControlModel?.Shipcall?.Type == 2) || (this.ShipcallControlModel?.Shipcall?.Type == 3)) if ((this.ShipcallControlModel?.Shipcall?.Type == ShipcallType.Departure) || (this.ShipcallControlModel?.Shipcall?.Type == ShipcallType.Shifting))
{ {
this.labelETA.Text = "ETD"; this.labelETA.Text = "ETD";
this.textBlockETA.Text = this.ShipcallControlModel?.Shipcall?.Etd?.ToString("dd.MM. HH:mm"); this.textBlockETA.Text = this.ShipcallControlModel?.Shipcall?.Etd?.ToString("dd.MM. HH:mm");
@ -287,10 +287,10 @@ namespace BreCalClient
this.textBlockIMO.Text = this.ShipcallControlModel?.Ship?.Imo.ToString(); this.textBlockIMO.Text = this.ShipcallControlModel?.Ship?.Imo.ToString();
this.textBlockLengthWidth.Text = $"{this.ShipcallControlModel?.Ship?.Length} / {this.ShipcallControlModel?.Ship?.Width}"; this.textBlockLengthWidth.Text = $"{this.ShipcallControlModel?.Ship?.Length} / {this.ShipcallControlModel?.Ship?.Width}";
// rename labels if this is not an incoming // rename labels if this is not an incoming
// must be here because there may not be a times record for each participant (yet) // must be here because there may not be a times record for each participant (yet)
if (this.ShipcallControlModel?.Shipcall?.Type != 1) if (this.ShipcallControlModel?.Shipcall?.Type != ShipcallType.Arrival)
{ {
this.labelETAETDAgent.Content = "ETD"; this.labelETAETDAgent.Content = "ETD";
this.labelETAETDMooring.Content = "ETD"; this.labelETAETDMooring.Content = "ETD";
@ -301,7 +301,7 @@ namespace BreCalClient
} }
if (this.ShipcallControlModel != null) if (this.ShipcallControlModel != null)
{ {
Times? agencyTimes = this.ShipcallControlModel?.GetTimesForParticipantType(Extensions.ParticipantType.AGENCY); Times? agencyTimes = this.ShipcallControlModel?.GetTimesForParticipantType(Extensions.ParticipantType.AGENCY);
if (agencyTimes != null) if (agencyTimes != null)
@ -310,11 +310,11 @@ namespace BreCalClient
this.labelAgencyETAETDValue.Content = agencyTimes.EtaBerth.HasValue ? agencyTimes.EtaBerth.Value.ToString("dd.MM.yyyy HH:mm") : "- / -"; this.labelAgencyETAETDValue.Content = agencyTimes.EtaBerth.HasValue ? agencyTimes.EtaBerth.Value.ToString("dd.MM.yyyy HH:mm") : "- / -";
this.textBlockAgencyRemarks.Text = agencyTimes.Remarks; this.textBlockAgencyRemarks.Text = agencyTimes.Remarks;
this.textBlockAgencyBerthRemarks.Text = agencyTimes.BerthInfo; this.textBlockAgencyBerthRemarks.Text = agencyTimes.BerthInfo;
if (this.ShipcallControlModel?.Shipcall?.Type != 1) if (this.ShipcallControlModel?.Shipcall?.Type != ShipcallType.Arrival)
{ {
this.labelAgencyETAETDValue.Content = agencyTimes.EtdBerth.HasValue ? agencyTimes.EtdBerth.Value.ToString("dd.MM.yyyy HH:mm") : "- / -"; this.labelAgencyETAETDValue.Content = agencyTimes.EtdBerth.HasValue ? agencyTimes.EtdBerth.Value.ToString("dd.MM.yyyy HH:mm") : "- / -";
} }
} }
else else
{ {
// no agency times set (yet) // no agency times set (yet)
@ -330,7 +330,7 @@ namespace BreCalClient
this.labelMooringETAETDValue.Content = mooringTimes.EtaBerth.HasValue ? mooringTimes.EtaBerth.Value.ToString("dd.MM.yyyy HH:mm") : "- / -"; this.labelMooringETAETDValue.Content = mooringTimes.EtaBerth.HasValue ? mooringTimes.EtaBerth.Value.ToString("dd.MM.yyyy HH:mm") : "- / -";
this.textBlockMooringRemarks.Text = mooringTimes.Remarks; this.textBlockMooringRemarks.Text = mooringTimes.Remarks;
if (this.ShipcallControlModel?.Shipcall?.Type != 1) if (this.ShipcallControlModel?.Shipcall?.Type != ShipcallType.Arrival)
{ {
this.labelMooringETAETDValue.Content = mooringTimes.EtdBerth.HasValue ? mooringTimes.EtdBerth.Value.ToString("dd.MM.yyyy HH:mm") : "- / -"; this.labelMooringETAETDValue.Content = mooringTimes.EtdBerth.HasValue ? mooringTimes.EtdBerth.Value.ToString("dd.MM.yyyy HH:mm") : "- / -";
} }
@ -346,7 +346,7 @@ namespace BreCalClient
{ {
this.labelPortAuthorityETAETDValue.Content = portAuthorityTimes.EtaBerth.HasValue ? portAuthorityTimes.EtaBerth.Value.ToString("dd.MM.yyyy HH:mm") : "- / -"; this.labelPortAuthorityETAETDValue.Content = portAuthorityTimes.EtaBerth.HasValue ? portAuthorityTimes.EtaBerth.Value.ToString("dd.MM.yyyy HH:mm") : "- / -";
this.textBlockPortAuthorityRemarks.Text = portAuthorityTimes.Remarks; this.textBlockPortAuthorityRemarks.Text = portAuthorityTimes.Remarks;
if (this.ShipcallControlModel?.Shipcall?.Type != 1) if (this.ShipcallControlModel?.Shipcall?.Type != ShipcallType.Arrival)
{ {
this.labelPortAuthorityETAETDValue.Content = portAuthorityTimes.EtdBerth.HasValue ? portAuthorityTimes.EtdBerth.Value.ToString("dd.MM.yyyy HH:mm") : "- / -"; this.labelPortAuthorityETAETDValue.Content = portAuthorityTimes.EtdBerth.HasValue ? portAuthorityTimes.EtdBerth.Value.ToString("dd.MM.yyyy HH:mm") : "- / -";
} }
@ -362,7 +362,7 @@ namespace BreCalClient
{ {
this.labelPilotETAETDValue.Content = pilotTimes.EtaBerth.HasValue ? pilotTimes.EtaBerth.Value.ToString("dd.MM.yyyy HH:mm") : "- / -"; this.labelPilotETAETDValue.Content = pilotTimes.EtaBerth.HasValue ? pilotTimes.EtaBerth.Value.ToString("dd.MM.yyyy HH:mm") : "- / -";
this.textBlockPilotRemarks.Text = pilotTimes.Remarks; this.textBlockPilotRemarks.Text = pilotTimes.Remarks;
if (this.ShipcallControlModel?.Shipcall?.Type != 1) if (this.ShipcallControlModel?.Shipcall?.Type != ShipcallType.Arrival)
{ {
this.labelPilotETAETDValue.Content = pilotTimes.EtdBerth.HasValue ? pilotTimes.EtdBerth.Value.ToString("dd.MM.yyyy HH:mm") : "- / -"; this.labelPilotETAETDValue.Content = pilotTimes.EtdBerth.HasValue ? pilotTimes.EtdBerth.Value.ToString("dd.MM.yyyy HH:mm") : "- / -";
} }
@ -378,7 +378,7 @@ namespace BreCalClient
{ {
this.labelTugETAETDValue.Content = tugTimes.EtaBerth.HasValue ? tugTimes.EtaBerth.Value.ToString("dd.MM.yyyy HH:mm") : "- / -"; this.labelTugETAETDValue.Content = tugTimes.EtaBerth.HasValue ? tugTimes.EtaBerth.Value.ToString("dd.MM.yyyy HH:mm") : "- / -";
this.textBlockTugRemarks.Text = tugTimes.Remarks; this.textBlockTugRemarks.Text = tugTimes.Remarks;
if (this.ShipcallControlModel?.Shipcall?.Type != 1) if (this.ShipcallControlModel?.Shipcall?.Type != ShipcallType.Arrival)
{ {
this.labelTugETAETDValue.Content = tugTimes.EtdBerth.HasValue ? tugTimes.EtdBerth.Value.ToString("dd.MM.yyyy HH:mm") : "- / -"; this.labelTugETAETDValue.Content = tugTimes.EtdBerth.HasValue ? tugTimes.EtdBerth.Value.ToString("dd.MM.yyyy HH:mm") : "- / -";
} }
@ -395,7 +395,7 @@ namespace BreCalClient
this.labelTerminalBerth.Content = this.ShipcallControlModel?.GetBerthText(terminalTimes); this.labelTerminalBerth.Content = this.ShipcallControlModel?.GetBerthText(terminalTimes);
this.labelOperationsStart.Content = terminalTimes.OperationsStart.HasValue ? terminalTimes.OperationsStart.Value.ToString("dd.MM.yyyy HH:mm") : "- / -"; this.labelOperationsStart.Content = terminalTimes.OperationsStart.HasValue ? terminalTimes.OperationsStart.Value.ToString("dd.MM.yyyy HH:mm") : "- / -";
this.textBlockTerminalRemarks.Text = terminalTimes.Remarks; this.textBlockTerminalRemarks.Text = terminalTimes.Remarks;
if (this.ShipcallControlModel?.Shipcall?.Type != 1) if (this.ShipcallControlModel?.Shipcall?.Type != ShipcallType.Arrival)
{ {
this.labelOperationsStart.Content = terminalTimes.OperationsEnd.HasValue ? terminalTimes.OperationsEnd.Value.ToString("dd.MM.yyyy HH:mm") : "- / -"; this.labelOperationsStart.Content = terminalTimes.OperationsEnd.HasValue ? terminalTimes.OperationsEnd.Value.ToString("dd.MM.yyyy HH:mm") : "- / -";
} }
@ -408,7 +408,7 @@ namespace BreCalClient
this.textBlockTerminalRemarks.Text = ""; this.textBlockTerminalRemarks.Text = "";
this.textBlockTerminalBerthRemarks.Text = ""; this.textBlockTerminalBerthRemarks.Text = "";
} }
} }
this.DataContext = this.ShipcallControlModel; this.DataContext = this.ShipcallControlModel;
} }
@ -432,7 +432,7 @@ namespace BreCalClient
{ {
if(App.Participant.IsTypeFlagSet(Extensions.ParticipantType.BSMD)) if(App.Participant.IsTypeFlagSet(Extensions.ParticipantType.BSMD))
this.EditRequested?.Invoke(this); this.EditRequested?.Invoke(this);
} }
private void Image_PreviewMouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e) private void Image_PreviewMouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{ {
@ -440,10 +440,10 @@ namespace BreCalClient
} }
private void labelAgent_PreviewMouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e) private void labelAgent_PreviewMouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{ {
Times? times = this.ShipcallControlModel?.GetTimesForParticipantType(Extensions.ParticipantType.AGENCY); Times? times = this.ShipcallControlModel?.GetTimesForParticipantType(Extensions.ParticipantType.AGENCY);
this.EditAgencyRequested?.Invoke(this, times); this.EditAgencyRequested?.Invoke(this, times);
} }
private void labelMooring_PreviewMouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e) private void labelMooring_PreviewMouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{ {
@ -453,7 +453,7 @@ namespace BreCalClient
private void labelPortAuthority_PreviewMouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e) private void labelPortAuthority_PreviewMouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{ {
Times? times = this.ShipcallControlModel?.GetTimesForParticipantType(Extensions.ParticipantType.PORT_ADMINISTRATION); Times? times = this.ShipcallControlModel?.GetTimesForParticipantType(Extensions.ParticipantType.PORT_ADMINISTRATION);
this.EditTimesRequested?.Invoke(this, times, Extensions.ParticipantType.PORT_ADMINISTRATION); this.EditTimesRequested?.Invoke(this, times, Extensions.ParticipantType.PORT_ADMINISTRATION);
} }

View File

@ -1,6 +1,6 @@
// Copyright (c) 2023 schick Informatik // Copyright (c) 2023 schick Informatik
// Description: Container model for shipcall related info // Description: Container model for shipcall related info
// //
using BreCalClient.misc.Api; using BreCalClient.misc.Api;
using BreCalClient.misc.Model; using BreCalClient.misc.Model;
@ -44,7 +44,7 @@ namespace BreCalClient
public Shipcall? Shipcall { get; set; } public Shipcall? Shipcall { get; set; }
public Ship? Ship { get; set; } public Ship? Ship { get; set; }
public string? Berth { get; set; } public string? Berth { get; set; }
@ -88,7 +88,7 @@ namespace BreCalClient
} }
} }
return tlm; return tlm;
} }
} }
public string Title public string Title
@ -96,8 +96,7 @@ namespace BreCalClient
get get
{ {
if (this.Shipcall == null) return ""; if (this.Shipcall == null) return "";
Extensions.TypeEnum callType = (Extensions.TypeEnum) this.Shipcall.Type; return string.Format("{0} {1}", this.Shipcall.Type, this.Ship?.Name);
return string.Format("{0} {1}", callType, this.Ship?.Name);
} }
} }
@ -112,7 +111,7 @@ namespace BreCalClient
{ {
foreach (ParticipantAssignment participantAssignment in Shipcall.Participants) foreach (ParticipantAssignment participantAssignment in Shipcall.Participants)
{ {
AssignedParticipants[(Extensions.ParticipantType)participantAssignment.Type] = participantAssignment; AssignedParticipants[(Extensions.ParticipantType)participantAssignment.Type] = participantAssignment;
} }
} }
} }
@ -160,15 +159,15 @@ namespace BreCalClient
public string? GetBerthText(Times times) public string? GetBerthText(Times times)
{ {
string? berthText = null; string? berthText = null;
if ((BreCalLists.AllBerths != null) && times.BerthId.HasValue && (this.Shipcall?.Type != (int)Extensions.TypeEnum.Shifting)) if ((BreCalLists.Berths != null) && times.BerthId.HasValue && (this.Shipcall?.Type != ShipcallType.Shifting))
{ {
Berth? berth = BreCalLists.AllBerths.Find((x) => x.Id == times.BerthId); Berth? berth = BreCalLists.AllBerths.Find((x) => x.Id == times.BerthId);
berthText = berth?.Name; berthText = berth?.Name;
} }
if ((berthText == null) && (times.ParticipantType != (int)Extensions.ParticipantType.TERMINAL)) if ((berthText == null) && (times.ParticipantType != (int) Extensions.ParticipantType.TERMINAL))
{ {
if (this.Shipcall?.Type == (int)Extensions.TypeEnum.Incoming) if (this.Shipcall?.Type == ShipcallType.Arrival)
{ {
Berth? berth = BreCalLists.AllBerths?.Find((x) => x.Id == this.Shipcall?.ArrivalBerthId); Berth? berth = BreCalLists.AllBerths?.Find((x) => x.Id == this.Shipcall?.ArrivalBerthId);
berthText = berth?.Name; berthText = berth?.Name;
@ -189,7 +188,7 @@ namespace BreCalClient
/// This function updates the assignments for existing times records accordingly and saves them. /// This function updates the assignments for existing times records accordingly and saves them.
/// </summary> /// </summary>
/// <param name="_api">API reference to PUT eidted times</param> /// <param name="_api">API reference to PUT eidted times</param>
internal async void UpdateTimesAssignments(DefaultApi _api) internal async void UpdateTimesAssignments(TimesApi api)
{ {
foreach (Extensions.ParticipantType participantType in this.AssignedParticipants.Keys) foreach (Extensions.ParticipantType participantType in this.AssignedParticipants.Keys)
{ {
@ -198,7 +197,7 @@ namespace BreCalClient
if(times.ParticipantId != this.AssignedParticipants[participantType].ParticipantId) if(times.ParticipantId != this.AssignedParticipants[participantType].ParticipantId)
{ {
times.ParticipantId = this.AssignedParticipants[participantType].ParticipantId; times.ParticipantId = this.AssignedParticipants[participantType].ParticipantId;
await _api.TimesPutAsync(times); await api.TimesUpdateAsync(times);
} }
} }
@ -225,7 +224,7 @@ namespace BreCalClient
foreach(Times times in deleteTimes) foreach(Times times in deleteTimes)
{ {
_api.TimesDelete(times.Id); api.TimesDelete(times.Id);
this.Times.Remove(times); this.Times.Remove(times);
} }
} }
@ -236,11 +235,11 @@ namespace BreCalClient
internal Participant? GetParticipantForType(Extensions.ParticipantType participantType) internal Participant? GetParticipantForType(Extensions.ParticipantType participantType)
{ {
if(AssignedParticipants.ContainsKey(participantType) && BreCalLists.ParticipantLookupDict.ContainsKey(AssignedParticipants[participantType].ParticipantId)) if(AssignedParticipants.ContainsKey(participantType) && BreCalLists.ParticipantLookupDict.ContainsKey(AssignedParticipants[participantType].ParticipantId))
return BreCalLists.ParticipantLookupDict[AssignedParticipants[participantType].ParticipantId]; return BreCalLists.ParticipantLookupDict[AssignedParticipants[participantType].ParticipantId];
return null; return null;
} }
private bool IsFlagSet(StatusFlags flag) private bool IsFlagSet(StatusFlags flag)
{ {
@ -248,7 +247,7 @@ namespace BreCalClient
return (this.Shipcall.Flags & (int) flag) != 0; return (this.Shipcall.Flags & (int) flag) != 0;
} }
#endregion #endregion
} }
} }

31
src/BreCalClient/Util.cs Normal file
View File

@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media.Imaging;
namespace BreCalClient
{
public static class Util
{
public static BitmapImage? LoadImage (byte[] imageData)
{
if (imageData == null || imageData.Length == 0) return null;
var image = new BitmapImage();
using (var mem = new MemoryStream(imageData))
{
mem.Position = 0;
image.BeginInit();
image.CreateOptions = BitmapCreateOptions.PreservePixelFormat;
image.CacheOption = BitmapCacheOption.OnLoad;
image.UriSource = null;
image.StreamSource = mem;
image.EndInit();
}
image.Freeze();
return image;
}
}
}

View File

@ -12,6 +12,7 @@ from .api import berths
from .api import ships from .api import ships
from .api import login from .api import login
from .api import user from .api import user
from .api import history
from BreCal.brecal_utils.file_handling import get_project_root, ensure_path from BreCal.brecal_utils.file_handling import get_project_root, ensure_path
from BreCal.brecal_utils.test_handling import execute_test_with_pytest, execute_coverage_test from BreCal.brecal_utils.test_handling import execute_test_with_pytest, execute_coverage_test
@ -59,7 +60,7 @@ def create_app(test_config=None):
app.register_blueprint(ships.bp) app.register_blueprint(ships.bp)
app.register_blueprint(login.bp) app.register_blueprint(login.bp)
app.register_blueprint(user.bp) app.register_blueprint(user.bp)
app.register_blueprint(history.bp)
logging.basicConfig(filename='brecaldevel.log', level=logging.DEBUG, format='%(asctime)s | %(name)s | %(levelname)s | %(message)s') logging.basicConfig(filename='brecaldevel.log', level=logging.DEBUG, format='%(asctime)s | %(name)s | %(levelname)s | %(message)s')
local_db.initPool(os.path.dirname(app.instance_path)) local_db.initPool(os.path.dirname(app.instance_path))
@ -73,8 +74,8 @@ def create_app(test_config=None):
return app return app
__all__ = [ __all__ = [
"get_project_root", "get_project_root",
"ensure_path", "ensure_path",
"execute_test_with_pytest", "execute_test_with_pytest",
"execute_coverage_test", "execute_coverage_test",
"difference_to_then", "difference_to_then",

View File

@ -0,0 +1,21 @@
from flask import Blueprint, request
from .. import impl
from ..services.auth_guard import auth_guard
import json
bp = Blueprint('history', __name__)
@bp.route('/history', methods=['get'])
@auth_guard() # no restriction by role
def GetParticipant():
if 'Authorization' in request.headers:
token = request.headers.get('Authorization')
options = {}
if not 'shipcall_id' in request.args:
return json.dumps("missing parameter"), 400
options["shipcall_id"] = request.args.get("shipcall_id")
return impl.history.GetHistory(options)
else:
return json.dumps("not authenticated"), 403

View File

@ -10,12 +10,10 @@ bp = Blueprint('notifications', __name__)
@bp.route('/notifications', methods=['get']) @bp.route('/notifications', methods=['get'])
@auth_guard() # no restriction by role @auth_guard() # no restriction by role
def GetNotifications(): def GetNotifications():
if 'shipcall_id' in request.args:
if 'participant_id' in request.args:
options = {} options = {}
options["participant_id"] = request.args.get("participant_id")
options["shipcall_id"] = request.args.get("shipcall_id") options["shipcall_id"] = request.args.get("shipcall_id")
return impl.notifications.GetNotifications(options) return impl.notifications.GetNotifications(options)
else: else:
logging.warning("attempt to load notifications without participant id") logging.warning("attempt to load notifications without shipcall id")
return json.dumps("missing argument"), 400 return json.dumps("missing argument"), 400

View File

@ -45,6 +45,7 @@ def PutShipcalls():
try: try:
content = request.get_json(force=True) content = request.get_json(force=True)
logging.info(content)
loadedModel = model.ShipcallSchema().load(data=content, many=False, partial=True) loadedModel = model.ShipcallSchema().load(data=content, many=False, partial=True)
except Exception as ex: except Exception as ex:
logging.error(ex) logging.error(ex)

View File

@ -1,7 +1,10 @@
from flask import Blueprint, request from flask import Blueprint, request
from .. import impl from .. import impl
from ..services.auth_guard import auth_guard from ..services.auth_guard import auth_guard
from marshmallow import EXCLUDE
from ..schemas import model
import json import json
import logging
bp = Blueprint('ships', __name__) bp = Blueprint('ships', __name__)
@ -14,3 +17,48 @@ def GetShips():
return impl.ships.GetShips(token) return impl.ships.GetShips(token)
else: else:
return json.dumps("not authenticated"), 403 return json.dumps("not authenticated"), 403
@bp.route('/ships', methods=['post'])
@auth_guard() # no restriction by role
def PostShip():
try:
content = request.get_json(force=True)
loadedModel = model.ShipSchema().load(data=content, many=False, partial=True)
except Exception as ex:
logging.error(ex)
print(ex)
return json.dumps("bad format"), 400
return impl.ships.PostShip(loadedModel)
@bp.route('/ships', methods=['put'])
@auth_guard() # no restriction by role
def PutShip():
try:
content = request.get_json(force=True)
loadedModel = model.ShipSchema().load(data=content, many=False, partial=True, unknown=EXCLUDE)
except Exception as ex:
logging.error(ex)
print(ex)
return json.dumps("bad format"), 400
return impl.ships.PutShip(loadedModel)
@bp.route('/ships', methods=['delete'])
@auth_guard() # no restriction by role
def DeleteShip():
try:
content = request.get_json(force=True)
loadedModel = model.ShipSchema().load(data=content, many=False, partial=True, unknown=EXCLUDE)
except Exception as ex:
logging.error(ex)
print(ex)
return json.dumps("bad format"), 400
return impl.ships.DeleteShip(loadedModel)

View File

@ -5,4 +5,5 @@ from . import shipcalls
from . import times from . import times
from . import ships from . import ships
from . import login from . import login
from . import user from . import user
from . import history

View File

@ -10,8 +10,6 @@ def GetBerths(token):
No parameters, gets all entries No parameters, gets all entries
""" """
# TODO: validate token
try: try:
pooledConnection = local_db.getPoolConnection() pooledConnection = local_db.getPoolConnection()
commands = pydapper.using(pooledConnection) commands = pydapper.using(pooledConnection)

View File

@ -0,0 +1,39 @@
import json
import logging
import pydapper
import pdb
from ..schemas import model
from ..schemas.model import History
from .. import local_db
def GetHistory(options):
"""
:param options: A dictionary containing all the paramters for the Operations
options["shipcall_id"]: **Id of shipcall**.
"""
try:
pooledConnection = local_db.getPoolConnection()
commands = pydapper.using(pooledConnection)
if "shipcall_id" in options and options["shipcall_id"]:
data = commands.query("SELECT id, participant_id, shipcall_id, timestamp, eta, type, operation FROM history WHERE shipcall_id = ?shipcallid?",
model=History.from_query_row,
param={"shipcallid" : options["shipcall_id"]})
pooledConnection.close()
except Exception as ex:
pdb.pm()
logging.error(ex)
print(ex)
result = {}
result["message"] = "call failed"
return json.dumps("call failed"), 500
return json.dumps(data, default=model.obj_dict), 200, {'Content-Type': 'application/json; charset=utf-8'}

View File

@ -14,7 +14,9 @@ def GetUser(options):
hash = bcrypt.hashpw(options["password"].encode('utf-8'), bcrypt.gensalt( 12 )).decode('utf8') hash = bcrypt.hashpw(options["password"].encode('utf-8'), bcrypt.gensalt( 12 )).decode('utf8')
pooledConnection = local_db.getPoolConnection() pooledConnection = local_db.getPoolConnection()
commands = pydapper.using(pooledConnection) commands = pydapper.using(pooledConnection)
data = commands.query("SELECT id, participant_id, first_name, last_name, user_name, user_email, user_phone, password_hash, api_key, created, modified FROM user WHERE user_name = ?username? OR user_email = ?username?", data = commands.query("SELECT id, participant_id, first_name, last_name, user_name, user_email, user_phone, password_hash, " +
"api_key, notify_email, notify_whatsapp, notify_signal, notify_popup, created, modified FROM user " +
"WHERE user_name = ?username? OR user_email = ?username?",
model=model.User, param={"username" : options["username"]}) model=model.User, param={"username" : options["username"]})
# print(data) # print(data)
if len(data) == 1: if len(data) == 1:

View File

@ -8,22 +8,25 @@ from .. import local_db
def GetNotifications(options): def GetNotifications(options):
""" """
:param options: A dictionary containing all the paramters for the Operations :param options: A dictionary containing all the paramters for the Operations
options["participant_id"]: **Id of participant**. *Example: 2*. Id returned through loading of participant options["shipcall_id"]: **Id**. *Example: 42*. Id of referenced ship call.
options["shipcall_id"]: **Id of ship call**. *Example: 52*. Id given in ship call list
""" """
# Implement your business logic here try:
# All the parameters are present in the options argument
return json.dumps({
"acknowledged": "<boolean>",
"id": "<integer>",
"notification_type": "<string>",
"participant_id": "<integer>",
"times_id": "<integer>",
"timestamp": "<date-time>",
}), 200, {'Content-Type': 'application/json; charset=utf-8'}
pooledConnection = local_db.getPoolConnection()
commands = pydapper.using(pooledConnection)
data = commands.query("SELECT id, shipcall_id, level, type, message, created, modified FROM notification " +
"WHERE shipcall_id = ?scid?", model=model.Notification.from_query_row, param={"scid" : options["shipcall_id"]})
pooledConnection.close()
except Exception as ex:
logging.error(ex)
print(ex)
result = {}
result["message"] = "call failed"
return json.dumps(result), 500, {'Content-Type': 'application/json; charset=utf-8'}
return json.dumps(data, default=model.obj_dict), 200, {'Content-Type': 'application/json; charset=utf-8'}

View File

@ -5,6 +5,7 @@ import pydapper
from ..schemas import model from ..schemas import model
from .. import local_db from .. import local_db
from ..services.auth_guard import check_jwt
from BreCal.database.update_database import evaluate_shipcall_state from BreCal.database.update_database import evaluate_shipcall_state
@ -20,7 +21,7 @@ def GetShipcalls(options):
query = ("SELECT s.id as id, ship_id, type, eta, voyage, etd, arrival_berth_id, departure_berth_id, tug_required, pilot_required, " + query = ("SELECT s.id as id, ship_id, type, eta, voyage, etd, arrival_berth_id, departure_berth_id, tug_required, pilot_required, " +
"flags, s.pier_side, bunkering, replenishing_terminal, replenishing_lock, draft, tidal_window_from, " + "flags, s.pier_side, bunkering, replenishing_terminal, replenishing_lock, draft, tidal_window_from, " +
"tidal_window_to, rain_sensitive_cargo, recommended_tugs, anchored, moored_lock, canceled, evaluation, " + "tidal_window_to, rain_sensitive_cargo, recommended_tugs, anchored, moored_lock, canceled, evaluation, " +
"evaluation_message, s.created as created, s.modified as modified " + "evaluation_message, evaluation_time, evaluation_notifications_sent, s.created as created, s.modified as modified " +
"FROM shipcall s " + "FROM shipcall s " +
"LEFT JOIN times t ON t.shipcall_id = s.id AND t.participant_type = 8 " + "LEFT JOIN times t ON t.shipcall_id = s.id AND t.participant_type = 8 " +
"WHERE " + "WHERE " +
@ -32,7 +33,7 @@ def GetShipcalls(options):
"(etd >= DATE(NOW() - INTERVAL %d DAY)))) " + "(etd >= DATE(NOW() - INTERVAL %d DAY)))) " +
"ORDER BY eta") % (options["past_days"], options["past_days"], options["past_days"], options["past_days"]) "ORDER BY eta") % (options["past_days"], options["past_days"], options["past_days"], options["past_days"])
data = commands.query(query, model=model.Shipcall) data = commands.query(query, model=model.Shipcall.from_query_row, buffered=True)
for shipcall in data: for shipcall in data:
participant_query = "SELECT participant_id, type FROM shipcall_participant_map WHERE shipcall_id=?shipcall_id?"; participant_query = "SELECT participant_id, type FROM shipcall_participant_map WHERE shipcall_id=?shipcall_id?";
for record in commands.query(participant_query, model=dict, param={"shipcall_id" : shipcall.id}, buffered=False): for record in commands.query(participant_query, model=dict, param={"shipcall_id" : shipcall.id}, buffered=False):
@ -112,13 +113,20 @@ def PostShipcalls(schemaModel):
commands.execute(query, schemaModel) commands.execute(query, schemaModel)
new_id = commands.execute_scalar("select last_insert_id()") new_id = commands.execute_scalar("select last_insert_id()")
# add participant assignments # add participant assignments if we have a list of participants
pquery = "INSERT INTO shipcall_participant_map (shipcall_id, participant_id, type) VALUES (?shipcall_id?, ?participant_id?, ?type?)" if 'participants' in schemaModel:
for participant_assignment in schemaModel["participants"]: pquery = "INSERT INTO shipcall_participant_map (shipcall_id, participant_id, type) VALUES (?shipcall_id?, ?participant_id?, ?type?)"
commands.execute(pquery, param={"shipcall_id" : new_id, "participant_id" : participant_assignment["participant_id"], "type" : participant_assignment["type"]}) for participant_assignment in schemaModel["participants"]:
commands.execute(pquery, param={"shipcall_id" : new_id, "participant_id" : participant_assignment["participant_id"], "type" : participant_assignment["type"]})
# apply 'Traffic Light' evaluation to obtain 'GREEN', 'YELLOW' or 'RED' evaluation state. The function internally updates the mysql database # apply 'Traffic Light' evaluation to obtain 'GREEN', 'YELLOW' or 'RED' evaluation state. The function internally updates the mysql database
evaluate_shipcall_state(mysql_connector_instance=pooledConnection, shipcall_id=new_id) # new_id (last insert id) refers to the shipcall id # evaluate_shipcall_state(mysql_connector_instance=pooledConnection, shipcall_id=new_id) # new_id (last insert id) refers to the shipcall id
# save history data
# TODO: set ETA properly
user_data = check_jwt()
query = "INSERT INTO history (participant_id, shipcall_id, user_id, timestamp, eta, type, operation) VALUES (?pid?, ?scid?, ?uid?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 1, 1)"
commands.execute(query, {"scid" : new_id, "pid" : user_data["participant_id"], "uid" : user_data["id"]})
return json.dumps({"id" : new_id}), 201, {'Content-Type': 'application/json; charset=utf-8'} return json.dumps({"id" : new_id}), 201, {'Content-Type': 'application/json; charset=utf-8'}
@ -205,6 +213,13 @@ def PutShipcalls(schemaModel):
commands.execute(dquery, param={"existing_id" : elem["id"]}) commands.execute(dquery, param={"existing_id" : elem["id"]})
# apply 'Traffic Light' evaluation to obtain 'GREEN', 'YELLOW' or 'RED' evaluation state. The function internally updates the mysql database # apply 'Traffic Light' evaluation to obtain 'GREEN', 'YELLOW' or 'RED' evaluation state. The function internally updates the mysql database
# evaluate_shipcall_state(mysql_connector_instance=pooledConnection, shipcall_id=schemaModel["id"]) # schemaModel["id"] refers to the shipcall id
# save history data
# TODO: set ETA properly
user_data = check_jwt()
query = "INSERT INTO history (participant_id, shipcall_id, user_id, timestamp, eta, type, operation) VALUES (?pid?, ?scid?, ?uid?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 1, 2)"
commands.execute(query, {"scid" : schemaModel["id"], "pid" : user_data["participant_id"], "uid" : user_data["id"]})
return json.dumps({"id" : schemaModel["id"]}), 200 return json.dumps({"id" : schemaModel["id"]}), 200

View File

@ -10,8 +10,6 @@ def GetShips(token):
No parameters, gets all entries No parameters, gets all entries
""" """
# TODO: validate token
try: try:
pooledConnection = local_db.getPoolConnection() pooledConnection = local_db.getPoolConnection()
@ -35,3 +33,127 @@ def GetShips(token):
def PostShip(schemaModel):
"""
:param schemaModel: The deserialized model of the record to be inserted
"""
# TODO: Validate the incoming data
# This creates a *new* entry
try:
pooledConnection = local_db.getPoolConnection()
commands = pydapper.using(pooledConnection)
query = "INSERT INTO ship ("
isNotFirst = False
for key in schemaModel.keys():
if key == "id":
continue
if key == "created":
continue
if key == "modified":
continue
if isNotFirst:
query += ","
isNotFirst = True
query += key
query += ") VALUES ("
isNotFirst = False
for key in schemaModel.keys():
if key == "id":
continue
if key == "created":
continue
if key == "modified":
continue
if isNotFirst:
query += ","
isNotFirst = True
query += "?" + key + "?"
query += ")"
commands.execute(query, schemaModel)
new_id = commands.execute_scalar("select last_insert_id()")
pooledConnection.close()
return json.dumps({"id" : new_id}), 201, {'Content-Type': 'application/json; charset=utf-8'}
except Exception as ex:
logging.error(ex)
print(ex)
result = {}
result["message"] = "call failed"
return json.dumps(result), 500, {'Content-Type': 'application/json; charset=utf-8'}
def PutShip(schemaModel):
"""
:param schemaModel: The deserialized model of the record to be inserted
"""
# This updates an *existing* entry
try:
pooledConnection = local_db.getPoolConnection()
commands = pydapper.using(pooledConnection)
query = "UPDATE ship SET "
isNotFirst = False
for key in schemaModel.keys():
if key == "id":
continue
if key == "created":
continue
if key == "modified":
continue
if isNotFirst:
query += ", "
isNotFirst = True
query += key + " = ?" + key + "? "
query += "WHERE id = ?id?"
affected_rows = commands.execute(query, param=schemaModel)
pooledConnection.close()
return json.dumps({"id" : schemaModel["id"]}), 200, {'Content-Type': 'application/json; charset=utf-8'}
except Exception as ex:
logging.error(ex)
print(ex)
result = {}
result["message"] = "call failed"
return json.dumps(result), 500, {'Content-Type': 'application/json; charset=utf-8'}
def DeleteShip(options):
"""
:param options: A dictionary containing all the paramters for the Operations
options["id"]
"""
try:
pooledConnection = local_db.getPoolConnection()
commands = pydapper.using(pooledConnection)
affected_rows = commands.execute("UPDATE ship SET deleted = 1 WHERE id = ?id?", param={"id" : options["id"]})
pooledConnection.close()
if affected_rows == 1:
return json.dumps({"id" : options["id"]}), 200, {'Content-Type': 'application/json; charset=utf-8'}
result = {}
result["message"] = "no such record"
return json.dumps(result), 404, {'Content-Type': 'application/json; charset=utf-8'}
except Exception as ex:
logging.error(ex)
print(ex)
result = {}
result["message"] = "call failed"
return json.dumps(result), 500, {'Content-Type': 'application/json; charset=utf-8'}

View File

@ -1,9 +1,11 @@
import json import json
import logging import logging
import traceback
import pydapper import pydapper
from ..schemas import model from ..schemas import model
from .. import local_db from .. import local_db
from ..services.auth_guard import check_jwt
from BreCal.database.update_database import evaluate_shipcall_state from BreCal.database.update_database import evaluate_shipcall_state
@ -14,8 +16,6 @@ def GetTimes(options):
""" """
# TODO: validate token
try: try:
pooledConnection = local_db.getPoolConnection() pooledConnection = local_db.getPoolConnection()
@ -27,6 +27,7 @@ def GetTimes(options):
pooledConnection.close() pooledConnection.close()
except Exception as ex: except Exception as ex:
logging.error(traceback.format_exc())
logging.error(ex) logging.error(ex)
print(ex) print(ex)
result = {} result = {}
@ -85,9 +86,16 @@ def PostTimes(schemaModel):
# apply 'Traffic Light' evaluation to obtain 'GREEN', 'YELLOW' or 'RED' evaluation state. The function internally updates the mysql database 'shipcall' # apply 'Traffic Light' evaluation to obtain 'GREEN', 'YELLOW' or 'RED' evaluation state. The function internally updates the mysql database 'shipcall'
evaluate_shipcall_state(mysql_connector_instance=pooledConnection, shipcall_id=schemaModel["shipcall_id"]) # every times data object refers to the 'shipcall_id' evaluate_shipcall_state(mysql_connector_instance=pooledConnection, shipcall_id=schemaModel["shipcall_id"]) # every times data object refers to the 'shipcall_id'
# save history data
# TODO: set ETA properly
user_data = check_jwt()
query = "INSERT INTO history (participant_id, shipcall_id, user_id, timestamp, eta, type, operation) VALUES (?pid?, ?scid?, ?uid?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 2, 1)"
commands.execute(query, {"scid" : schemaModel["shipcall_id"], "pid" : user_data["participant_id"], "uid" : user_data["id"]})
return json.dumps({"id" : new_id}), 201, {'Content-Type': 'application/json; charset=utf-8'} return json.dumps({"id" : new_id}), 201, {'Content-Type': 'application/json; charset=utf-8'}
except Exception as ex: except Exception as ex:
logging.error(traceback.format_exc())
logging.error(ex) logging.error(ex)
print(ex) print(ex)
result = {} result = {}
@ -132,13 +140,21 @@ def PutTimes(schemaModel):
# apply 'Traffic Light' evaluation to obtain 'GREEN', 'YELLOW' or 'RED' evaluation state. The function internally updates the mysql database 'shipcall' # apply 'Traffic Light' evaluation to obtain 'GREEN', 'YELLOW' or 'RED' evaluation state. The function internally updates the mysql database 'shipcall'
evaluate_shipcall_state(mysql_connector_instance=pooledConnection, shipcall_id=schemaModel["shipcall_id"]) # every times data object refers to the 'shipcall_id' evaluate_shipcall_state(mysql_connector_instance=pooledConnection, shipcall_id=schemaModel["shipcall_id"]) # every times data object refers to the 'shipcall_id'
# save history data
# TODO: set ETA properly
user_data = check_jwt()
if "participant_id" in user_data and "id" in user_data:
query = "INSERT INTO history (participant_id, shipcall_id, user_id, timestamp, eta, type, operation) VALUES (?pid?, ?scid?, ?uid?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 2, 2)"
commands.execute(query, {"pid" : user_data["participant_id"], "scid" : schemaModel["shipcall_id"], "uid" : user_data["id"]})
else:
logging.error("user_data does not contain participant_id or id")
# if affected_rows == 1: # this doesn't work as expected # if affected_rows == 1: # this doesn't work as expected
return json.dumps({"id" : schemaModel["id"]}), 200, {'Content-Type': 'application/json; charset=utf-8'} return json.dumps({"id" : schemaModel["id"]}), 200, {'Content-Type': 'application/json; charset=utf-8'}
# return json.dumps("no such record"), 404, {'Content-Type': 'application/json; charset=utf-8'}
except Exception as ex: except Exception as ex:
logging.error(traceback.format_exc())
logging.error(ex) logging.error(ex)
print(ex) print(ex)
result = {} result = {}
@ -161,6 +177,14 @@ def DeleteTimes(options):
commands = pydapper.using(pooledConnection) commands = pydapper.using(pooledConnection)
affected_rows = commands.execute("DELETE FROM times WHERE id = ?id?", param={"id" : options["id"]}) affected_rows = commands.execute("DELETE FROM times WHERE id = ?id?", param={"id" : options["id"]})
# TODO: howto get the shipcall id here? we will need to load the object first
# TODO: set ETA properly
# save history data
user_data = check_jwt()
query = "INSERT INTO history (participant_id, shipcall_id, user_id, timestamp, eta, type, operation) VALUES (?pid?, 0, ?uid?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 2, 3)"
commands.execute(query, {"pid" : user_data["participant_id"], "uid" : user_data["id"]})
if affected_rows == 1: if affected_rows == 1:
return json.dumps({"id" : options["id"]}), 200, {'Content-Type': 'application/json; charset=utf-8'} return json.dumps({"id" : options["id"]}), 200, {'Content-Type': 'application/json; charset=utf-8'}

View File

@ -1,5 +1,8 @@
from dataclasses import field from dataclasses import field, dataclass
from marshmallow import Schema, fields, INCLUDE, ValidationError from marshmallow import Schema, fields, INCLUDE, ValidationError
from marshmallow.fields import Field
from marshmallow_enum import EnumField
from enum import IntEnum
from marshmallow_dataclass import dataclass from marshmallow_dataclass import dataclass
from typing import List from typing import List
@ -10,6 +13,8 @@ import datetime
def obj_dict(obj): def obj_dict(obj):
if isinstance(obj, datetime.datetime): if isinstance(obj, datetime.datetime):
return obj.isoformat() return obj.isoformat()
if hasattr(obj, 'to_json'):
return obj.to_json()
return obj.__dict__ return obj.__dict__
@dataclass @dataclass
@ -23,6 +28,70 @@ class Berth(Schema):
modified: datetime modified: datetime
deleted: bool deleted: bool
class OperationType(IntEnum):
undefined = 0
insert = 1
update = 2
delete = 3
class ObjectType(IntEnum):
undefined = 0
shipcall = 1
times = 2
class EvaluationType(IntEnum):
undefined = 0
green = 1
yellow = 2
red = 3
class NotificationType(IntEnum):
undefined = 0
email = 1
push = 2
class ShipcallType(IntEnum):
undefined = 0
arrival = 1
departure = 2
shifting = 3
@dataclass
class History:
def __init__(self, id, participant_id, shipcall_id, timestamp, eta, type, operation):
self.id = id
self.participant_id = participant_id
self.shipcall_id = shipcall_id
self.timestamp = timestamp
self.eta = eta
self.type = type
self.operation = operation
pass
id: int
participant_id: int
shipcall_id: int
timestamp: datetime
eta: datetime
type: ObjectType
operation: OperationType
def to_json(self):
return {
"id": self.id,
"participant_id": self.participant_id,
"shipcall_id": self.shipcall_id,
"timestamp": self.timestamp.isoformat() if self.timestamp else "",
"eta": self.eta.isoformat() if self.eta else "",
"type": self.type.name,
"operation": self.operation.name
}
@classmethod
def from_query_row(self, id, participant_id, shipcall_id, timestamp, eta, type, operation):
return self(id, participant_id, shipcall_id, timestamp, eta, ObjectType(type), OperationType(operation))
class Error(Schema): class Error(Schema):
message = fields.String(required=True) message = fields.String(required=True)
@ -31,17 +100,30 @@ class GetVerifyInlineResp(Schema):
pass pass
@dataclass @dataclass
class Notification(Schema): class Notification:
id: int id: int
times_id: int shipcall_id: int
acknowledged: bool
level: int level: int
type: int type: NotificationType
message: str message: str
created: datetime created: datetime
modified: datetime modified: datetime
def to_json(self):
return {
"id": self.id,
"shipcall_id": self.shipcall_id,
"level": self.level,
"type": self.type.name,
"message": self.message,
"created": self.created.isoformat() if self.created else "",
"modified": self.modified.isoformat() if self.modified else ""
}
@classmethod
def from_query_row(self, id, shipcall_id, level, type, message, created, modified):
return self(id, shipcall_id, level, NotificationType(type), message, created, modified)
@dataclass @dataclass
class Participant(Schema): class Participant(Schema):
id: int id: int
@ -69,7 +151,7 @@ class ShipcallSchema(Schema):
id = fields.Int() id = fields.Int()
ship_id = fields.Int() ship_id = fields.Int()
type = fields.Int() type = EnumField(ShipcallType, by_value=True, required=True)
eta = fields.DateTime(Required = False, allow_none=True) eta = fields.DateTime(Required = False, allow_none=True)
voyage = fields.Str(allow_none=True, metadata={'Required':False}) # Solving: RemovedInMarshmallow4Warning: Passing field metadata as keyword arguments is deprecated. Use the explicit `metadata=...` argument instead. Additional metadata: {'Required': False} voyage = fields.Str(allow_none=True, metadata={'Required':False}) # Solving: RemovedInMarshmallow4Warning: Passing field metadata as keyword arguments is deprecated. Use the explicit `metadata=...` argument instead. Additional metadata: {'Required': False}
etd = fields.DateTime(Required = False, allow_none=True) etd = fields.DateTime(Required = False, allow_none=True)
@ -90,8 +172,10 @@ class ShipcallSchema(Schema):
anchored = fields.Bool(Required = False, allow_none=True) anchored = fields.Bool(Required = False, allow_none=True)
moored_lock = fields.Bool(Required = False, allow_none=True) moored_lock = fields.Bool(Required = False, allow_none=True)
canceled = fields.Bool(Required = False, allow_none=True) canceled = fields.Bool(Required = False, allow_none=True)
evaluation = fields.Int(Required = False, allow_none=True) evaluation = EnumField(EvaluationType, required=False, allow_none=True, by_value=True)
evaluation_message = fields.Str(allow_none=True, metadata={'Required':False}) # Solving: RemovedInMarshmallow4Warning: Passing field metadata as keyword arguments is deprecated. Use the explicit `metadata=...` argument instead. Additional metadata: {'Required': False} evaluation_message = fields.Str(allow_none=True, metadata={'Required':False}) # Solving: RemovedInMarshmallow4Warning: Passing field metadata as keyword arguments is deprecated. Use the explicit `metadata=...` argument instead. Additional metadata: {'Required': False}
evaluation_time = fields.DateTime(Required = False, allow_none=True)
evaluation_notifications_sent = fields.Bool(Required = False, allow_none=True)
participants = fields.List(fields.Nested(ParticipantAssignmentSchema)) participants = fields.List(fields.Nested(ParticipantAssignmentSchema))
created = fields.DateTime(Required = False, allow_none=True) created = fields.DateTime(Required = False, allow_none=True)
modified = fields.DateTime(Required = False, allow_none=True) modified = fields.DateTime(Required = False, allow_none=True)
@ -104,14 +188,15 @@ class Participant_Assignment:
pass pass
participant_id: int participant_id: int
type: int type: int # a variant would be to use the IntFlag type (with appropriate serialization)
@dataclass @dataclass
class Shipcall: class Shipcall:
id: int id: int
ship_id: int ship_id: int
type: str type: ShipcallType
eta: datetime eta: datetime
voyage: str voyage: str
etd: datetime etd: datetime
@ -132,12 +217,54 @@ class Shipcall:
anchored: bool anchored: bool
moored_lock: bool moored_lock: bool
canceled: bool canceled: bool
evaluation: int evaluation: EvaluationType
evaluation_message: str evaluation_message: str
evaluation_time: datetime
evaluation_notifications_sent: bool
created: datetime created: datetime
modified: datetime modified: datetime
participants: List[Participant_Assignment] = field(default_factory=list) participants: List[Participant_Assignment] = field(default_factory=list)
def to_json(self):
return {
"id": self.id,
"ship_id": self.ship_id,
"type": self.type.name,
"eta": self.eta.isoformat() if self.eta else "",
"voyage": self.voyage,
"etd": self.etd.isoformat() if self.etd else "",
"arrival_berth_id": self.arrival_berth_id,
"departure_berth_id": self.departure_berth_id,
"tug_required": self.tug_required,
"pilot_required": self.pilot_required,
"flags": self.flags,
"pier_side": self.pier_side,
"bunkering": self.bunkering,
"replenishing_terminal": self.replenishing_terminal,
"replenishing_lock": self.replenishing_lock,
"draft": self.draft,
"tidal_window_from": self.tidal_window_from.isoformat() if self.tidal_window_from else "",
"tidal_window_to": self.tidal_window_to.isoformat() if self.tidal_window_to else "",
"rain_sensitive_cargo": self.rain_sensitive_cargo,
"recommended_tugs": self.recommended_tugs,
"anchored": self.anchored,
"moored_lock": self.moored_lock,
"canceled": self.canceled,
"evaluation": self.evaluation.name,
"evaluation_message": self.evaluation_message,
"evaluation_time": self.evaluation_time.isoformat() if self.evaluation_time else "",
"evaluation_notifications_sent": self.evaluation_notifications_sent,
"created": self.created.isoformat() if self.created else "",
"modified": self.modified.isoformat() if self.modified else "",
"participants": [participant.__dict__ for participant in self.participants]
}
@classmethod
def from_query_row(self, id, ship_id, type, eta, voyage, etd, arrival_berth_id, departure_berth_id, tug_required, pilot_required, flags, pier_side, bunkering, replenishing_terminal, replenishing_lock, draft, tidal_window_from, tidal_window_to, rain_sensitive_cargo, recommended_tugs, anchored, moored_lock, canceled, evaluation, evaluation_message, evaluation_time, evaluation_notifications_sent, created, modified):
return self(id, ship_id, ShipcallType(type), eta, voyage, etd, arrival_berth_id, departure_berth_id, tug_required, pilot_required, flags, pier_side, bunkering, replenishing_terminal, replenishing_lock, draft, tidal_window_from, tidal_window_to, rain_sensitive_cargo, recommended_tugs, anchored, moored_lock, canceled, EvaluationType(evaluation), evaluation_message, evaluation_time, evaluation_notifications_sent, created, modified)
class ShipcallId(Schema): class ShipcallId(Schema):
pass pass
@ -185,7 +312,6 @@ class UserSchema(Schema):
@dataclass @dataclass
class Times: class Times:
id: int id: int
eta_berth: datetime eta_berth: datetime
eta_berth_fixed: bool eta_berth_fixed: bool
@ -219,11 +345,15 @@ class User:
user_phone: str user_phone: str
password_hash: str password_hash: str
api_key: str api_key: str
notify_email: bool
notify_whatsapp: bool
notify_signal: bool
notify_popup: bool
created: datetime created: datetime
modified: datetime modified: datetime
@dataclass @dataclass
class Ship(Schema): class Ship:
id: int id: int
name: str name: str
imo: int imo: int
@ -233,11 +363,32 @@ class Ship(Schema):
width: float width: float
is_tug: bool is_tug: bool
bollard_pull: int bollard_pull: int
eni: str eni: int
created: datetime created: datetime
modified: datetime modified: datetime
deleted: bool deleted: bool
class ShipSchema(Schema):
def __init__(self):
super().__init__(unknown=None)
pass
id = fields.Int(Required=False)
name = fields.String(allow_none=False, metadata={'Required':True})
imo = fields.Int(allow_none=False, metadata={'Required':True})
callsign = fields.String(allow_none=True, metadata={'Required':False})
participant_id = fields.Int(allow_none=True, metadata={'Required':False})
length = fields.Float(allow_none=True, metadata={'Required':False})
width = fields.Float(allow_none=True, metadata={'Required':False})
is_tug = fields.Bool(allow_none=True, metadata={'Required':False}, default=False)
bollard_pull = fields.Int(allow_none=True, metadata={'Required':False})
eni = fields.Int(allow_none=True, metadata={'Required':False})
created = fields.DateTime(allow_none=True, metadata={'Required':False})
modified = fields.DateTime(allow_none=True, metadata={'Required':False})
deleted = fields.Bool(allow_none=True, metadata={'Required':False}, default=False)
class TimesId(Schema): class TimesId(Schema):
pass pass

View File

@ -27,7 +27,7 @@ def UpdateShipcalls(options:dict = {'past_days':2}):
commands = pydapper.using(pooledConnection) commands = pydapper.using(pooledConnection)
query = ("SELECT id, ship_id, type, eta, voyage, etd, arrival_berth_id, departure_berth_id, tug_required, pilot_required, " query = ("SELECT id, ship_id, type, eta, voyage, etd, arrival_berth_id, departure_berth_id, tug_required, pilot_required, "
"flags, pier_side, bunkering, replenishing_terminal, replenishing_lock, draft, tidal_window_from, tidal_window_to, rain_sensitive_cargo, recommended_tugs, " "flags, pier_side, bunkering, replenishing_terminal, replenishing_lock, draft, tidal_window_from, tidal_window_to, rain_sensitive_cargo, recommended_tugs, "
"anchored, moored_lock, canceled, evaluation, evaluation_message, created, modified FROM shipcall WHERE ((type = 1 OR type = 3) AND eta >= DATE(NOW() - INTERVAL %d DAY)" "anchored, moored_lock, canceled, evaluation, evaluation_message, evaluation_notifications_sent, evaluation_time, created, modified FROM shipcall WHERE ((type = 1 OR type = 3) AND eta >= DATE(NOW() - INTERVAL %d DAY)"
"OR (type = 2 AND etd >= DATE(NOW() - INTERVAL %d DAY))) " "OR (type = 2 AND etd >= DATE(NOW() - INTERVAL %d DAY))) "
"ORDER BY eta") % (options["past_days"], options["past_days"]) "ORDER BY eta") % (options["past_days"], options["past_days"])

View File

@ -41,40 +41,44 @@ def get_shipcall_simple():
canceled = False canceled = False
evaluation = None evaluation = None
evaluation_message = "" evaluation_message = ""
evaluation_time = None
evaluation_notifications_sent = False
created = datetime.datetime.now() created = datetime.datetime.now()
modified = created+datetime.timedelta(seconds=10) modified = created+datetime.timedelta(seconds=10)
participants = [generate_uuid1_int(), generate_uuid1_int(), generate_uuid1_int(), generate_uuid1_int()] # field(default_factory=[generate_uuid1_int(), generate_uuid1_int(), generate_uuid1_int(), generate_uuid1_int()]) # list participants = [generate_uuid1_int(), generate_uuid1_int(), generate_uuid1_int(), generate_uuid1_int()] # field(default_factory=[generate_uuid1_int(), generate_uuid1_int(), generate_uuid1_int(), generate_uuid1_int()]) # list
shipcall = Shipcall( shipcall = Shipcall(
shipcall_id, shipcall_id,
ship_id, ship_id,
role_type, role_type,
eta, eta,
voyage, voyage,
etd, etd,
arrival_berth_id, arrival_berth_id,
departure_berth_id, departure_berth_id,
tug_required, tug_required,
pilot_required, pilot_required,
flags, flags,
pier_side, pier_side,
bunkering, bunkering,
replenishing_terminal, replenishing_terminal,
replenishing_lock, replenishing_lock,
draft, draft,
tidal_window_from, tidal_window_from,
tidal_window_to, tidal_window_to,
rain_sensitive_cargo, rain_sensitive_cargo,
recommended_tugs, recommended_tugs,
anchored, anchored,
moored_lock, moored_lock,
canceled, canceled,
evaluation, evaluation,
evaluation_message, evaluation_message,
created, evaluation_time,
modified, evaluation_notifications_sent,
participants, created,
modified,
participants,
) )
return shipcall return shipcall