Merge branch 'release/1.5.0' into develop
This commit is contained in:
commit
79ed86937c
@ -61,11 +61,12 @@ PUT / DELETE calls referencing entities that are not found in the database will
|
||||
|
||||
### Return value
|
||||
|
||||
If a validation rule fails the call should return 400 (Bad request) including an error message in the format already in use:
|
||||
If a validation rule fails the call should return 400 (Bad request) including an error message in the following format:
|
||||
|
||||
```json
|
||||
{
|
||||
"message" : "reason why this call failed"
|
||||
"error_field" : "A reference to the respective field(s) which have caused the error",
|
||||
"error_description" : "Reason why this call failed"
|
||||
}
|
||||
```
|
||||
|
||||
@ -81,6 +82,10 @@ Date and date+time values are specified as text formatted in [RFC 3339](https://
|
||||
|
||||
Usually the "Z" is missing at the end indicating local time.
|
||||
|
||||
Generally, times may not be updated to a value in the past. There are exception (see SHIPCALL PUT below).
|
||||
|
||||
Times should also not be set to a value more than 1 year in the future. The reasoning is to prevent shipcalls to stick to the top of the list by implausible (or for the workings of Bremen calling irrelevant) values too far in the future.
|
||||
|
||||
## /shipcall POST
|
||||
|
||||
1. The call may only be performed by a user belonging to participant group type BSMD.
|
||||
@ -112,7 +117,7 @@ Usually the "Z" is missing at the end indicating local time.
|
||||
|
||||
#### Required fields
|
||||
|
||||
* eta / etd (depending on value of type: 1: eta, 2: etd, 3: both)
|
||||
* eta / etd (depending on value of type: 1: eta, 2: etd, 3: etd)
|
||||
* type
|
||||
* ship_id
|
||||
* arrival_berth_id / departure_berth_id (depending on type, see above)
|
||||
@ -123,7 +128,11 @@ Usually the "Z" is missing at the end indicating local time.
|
||||
1. The call may only be performed by a user belonging to participant group type BSMD.
|
||||
2. If a agency is selected via the shipcall_participant_map entry, users of this agency may also edit the shipcall. Care has to be taken: The agency must have been set _before_ a member of the group may edit the record.
|
||||
In other words: no setting the agency and editing the record by a member of the agency within the same call.
|
||||
3. See value rules in /shipcall POST. Exception: Canceled may be set but only if not already set.
|
||||
3. See value rules in /shipcall POST.
|
||||
Exceptions:
|
||||
a) Canceled may be set but only if not already set.
|
||||
b) ETA/ETD may be in the past. This can happen if an agency has entered an ETA/ETD (times) in the future but
|
||||
wants to edit fields of the shipcall record (e.g. the draft) but the shipcall was originally created with an ETA/ETD in the past
|
||||
4. A cancelled shipcall may not be changed (is logical delete)
|
||||
|
||||
#### Required fields
|
||||
@ -147,6 +156,7 @@ The id field is required, missing fields will not be updated.
|
||||
| Field | Validation |
|
||||
|-------|------------|
|
||||
| eta_berth, etd_berth, lock_time, zone_entry, operations_start, operations_end | if set these values must be in the future|
|
||||
| eta_interval_end, etd_interval_end | if set these values must be in the future. They must be larger than their ETA/ETD counterparts. |
|
||||
| remarks, berth_info | must be <= 512 chars |
|
||||
| participant_type | must not be BSMD |
|
||||
|
||||
@ -188,6 +198,7 @@ shipcall_id, participant_id, participant_type
|
||||
|
||||
1. A dataset may only be changed by a user belonging to the same participant as the times dataset is referring to.
|
||||
2. See reference and value checking as specified in /times POST.
|
||||
3. The shipcall type may not be changed.
|
||||
|
||||
#### Required fields
|
||||
|
||||
|
||||
@ -32,7 +32,7 @@ ___
|
||||
| 0002 | Zeiten für einen Eintrag weichen voneinander ab | Bedingungen:<br/> - Header der Zeile ist zugeordnet (Agentur, Festmacher usw. - außer BSMD-Spalte)<br/> - Zeiten ungleich (leere Einträge nicht berücksichtigen => 0001) | |
|
||||
| 0002 - A | Agentur + Festmacher + Hafenamt + Lotsen + Schlepper / einkommend | Schnittmenge aus:<br/>times_agency:<br/> - ETA Berth <br/>____und____<br/>times_mooring:<br/> - ETA Berth <br/>____und____<br/>times_portauthority:<br/>- ETA Berth <br/>____und____<br/>times_pilot:<br/> - ETA Berth <br/>____und____<br/>times_tug:<br/> - ETA Berth | rot |
|
||||
| 0002 - B | Agentur + Festmacher + Hafenamt + Lotsen + Schlepper / ausgehend | Schnittmenge aus:<br/>times_agency:<br/> - ETD Berth <br/>____und____<br/>times_mooring:<br/> - ETD Berth <br/>____und____<br/>times_portauthority:<br/>- ETD Berth <br/>____und____<br/>times_pilot:<br/> - ETD Berth <br/>____und____<br/>times_tug:<br/> - ETD Berth | rot |
|
||||
| 0002 - C | Agentur + Festmacher + Hafenamt + Lotsen + Schlepper / Verholung | Schnittmenge aus:<br />times_agency:<br/> - ETA Berth <br/> - ETD Berth <br/>____und____<br/>times_mooring:<br/> - ETA Berth <br/> - ETD Berth <br/>____und____<br/>times_portauthority:<br/> - ETA Berth <br/>- ETD Berth <br/>____und____<br/>times_pilot:<br/> - ETA Berth <br/> - ETD Berth <br/>____und____<br/>times_tug:<br/> - ETA Berth <br/> - ETD Berth | rot |
|
||||
| 0002 - C | Agentur + Festmacher + Hafenamt + Lotsen + Schlepper / Verholung | Schnittmenge aus:<br/>times_agency:<br/> - ETD Berth <br/>____und____<br/>times_mooring:<br/> - ETD Berth <br/>____und____<br/>times_portauthority:<br/>- ETD Berth <br/>____und____<br/>times_pilot:<br/> - ETD Berth <br/>____und____<br/>times_tug:<br/> - ETD Berth | rot |
|
||||
| 0003 | Arbeitszeit überschneidet sich mit Fahrtzeit | Bedingungen:<br/> - Header der Zeile ist zugeordnet (Terminal)<br/> - Zeiten passt nicht zu Ankunft / Abfahrt (leere Einträge nicht berücksichtigen => 0001) | |
|
||||
| 0003 - A | Terminal / einkommend | times_terminal:<br/> - Operation Start<br/>___vor (kleiner als)____<br/> times_agency:<br/> - ETA Berth | rot, aktuell __deaktiviert__! |
|
||||
| 0003 - B | Terminal / ausgehend + Verholung | times_terminal:<br/> - Operation Ende<br/>___nach (größer als)____<br/> times_agency:<br/> - ETD Berth | rot, aktuell __deaktiviert__! |
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
|
||||
//----------------------
|
||||
// <auto-generated>
|
||||
// Generated REST API Client Code Generator v1.10.9.0 on 29.08.2024 08:26:31
|
||||
// Generated REST API Client Code Generator v1.11.0.0 on 10.09.2024 11:10:02
|
||||
// Using the tool OpenAPI Generator v7.8.0
|
||||
// </auto-generated>
|
||||
//----------------------
|
||||
@ -46,7 +46,7 @@ using System.Threading.Tasks;
|
||||
*
|
||||
* Administer DEBRE ship calls, times and notifications
|
||||
*
|
||||
* The version of the OpenAPI document: 1.4.0
|
||||
* The version of the OpenAPI document: 1.5.0
|
||||
* Contact: info@textbausteine.net
|
||||
* Generated by: https://github.com/openapitools/openapi-generator.git
|
||||
*/
|
||||
@ -850,7 +850,7 @@ namespace BreCalClient.misc.Api
|
||||
*
|
||||
* Administer DEBRE ship calls, times and notifications
|
||||
*
|
||||
* The version of the OpenAPI document: 1.4.0
|
||||
* The version of the OpenAPI document: 1.5.0
|
||||
* Contact: info@textbausteine.net
|
||||
* Generated by: https://github.com/openapitools/openapi-generator.git
|
||||
*/
|
||||
@ -1505,7 +1505,7 @@ namespace BreCalClient.misc.Api
|
||||
*
|
||||
* Administer DEBRE ship calls, times and notifications
|
||||
*
|
||||
* The version of the OpenAPI document: 1.4.0
|
||||
* The version of the OpenAPI document: 1.5.0
|
||||
* Contact: info@textbausteine.net
|
||||
* Generated by: https://github.com/openapitools/openapi-generator.git
|
||||
*/
|
||||
@ -2290,7 +2290,7 @@ namespace BreCalClient.misc.Api
|
||||
*
|
||||
* Administer DEBRE ship calls, times and notifications
|
||||
*
|
||||
* The version of the OpenAPI document: 1.4.0
|
||||
* The version of the OpenAPI document: 1.5.0
|
||||
* Contact: info@textbausteine.net
|
||||
* Generated by: https://github.com/openapitools/openapi-generator.git
|
||||
*/
|
||||
@ -3109,7 +3109,7 @@ namespace BreCalClient.misc.Api
|
||||
*
|
||||
* Administer DEBRE ship calls, times and notifications
|
||||
*
|
||||
* The version of the OpenAPI document: 1.4.0
|
||||
* The version of the OpenAPI document: 1.5.0
|
||||
* Contact: info@textbausteine.net
|
||||
* Generated by: https://github.com/openapitools/openapi-generator.git
|
||||
*/
|
||||
@ -3594,7 +3594,7 @@ namespace BreCalClient.misc.Api
|
||||
*
|
||||
* Administer DEBRE ship calls, times and notifications
|
||||
*
|
||||
* The version of the OpenAPI document: 1.4.0
|
||||
* The version of the OpenAPI document: 1.5.0
|
||||
* Contact: info@textbausteine.net
|
||||
* Generated by: https://github.com/openapitools/openapi-generator.git
|
||||
*/
|
||||
@ -4314,7 +4314,7 @@ namespace BreCalClient.misc.Client
|
||||
*
|
||||
* Administer DEBRE ship calls, times and notifications
|
||||
*
|
||||
* The version of the OpenAPI document: 1.4.0
|
||||
* The version of the OpenAPI document: 1.5.0
|
||||
* Contact: info@textbausteine.net
|
||||
* Generated by: https://github.com/openapitools/openapi-generator.git
|
||||
*/
|
||||
@ -4374,7 +4374,7 @@ namespace BreCalClient.misc.Client
|
||||
*
|
||||
* Administer DEBRE ship calls, times and notifications
|
||||
*
|
||||
* The version of the OpenAPI document: 1.4.0
|
||||
* The version of the OpenAPI document: 1.5.0
|
||||
* Contact: info@textbausteine.net
|
||||
* Generated by: https://github.com/openapitools/openapi-generator.git
|
||||
*/
|
||||
@ -4514,7 +4514,7 @@ namespace BreCalClient.misc.Client
|
||||
*
|
||||
* Administer DEBRE ship calls, times and notifications
|
||||
*
|
||||
* The version of the OpenAPI document: 1.4.0
|
||||
* The version of the OpenAPI document: 1.5.0
|
||||
* Contact: info@textbausteine.net
|
||||
* Generated by: https://github.com/openapitools/openapi-generator.git
|
||||
*/
|
||||
@ -4732,7 +4732,7 @@ namespace BreCalClient.misc.Client
|
||||
*
|
||||
* Administer DEBRE ship calls, times and notifications
|
||||
*
|
||||
* The version of the OpenAPI document: 1.4.0
|
||||
* The version of the OpenAPI document: 1.5.0
|
||||
* Contact: info@textbausteine.net
|
||||
* Generated by: https://github.com/openapitools/openapi-generator.git
|
||||
*/
|
||||
@ -4817,7 +4817,7 @@ namespace BreCalClient.misc.Client
|
||||
{
|
||||
Proxy = null;
|
||||
UserAgent = WebUtility.UrlEncode("OpenAPI-Generator/1.0.0/csharp");
|
||||
BasePath = "https://brecaldevel.bsmd-emswe.eu";
|
||||
BasePath = "https://brecaltest.bsmd-emswe.eu";
|
||||
DefaultHeaders = new ConcurrentDictionary<string, string>();
|
||||
ApiKey = new ConcurrentDictionary<string, string>();
|
||||
ApiKeyPrefix = new ConcurrentDictionary<string, string>();
|
||||
@ -4825,7 +4825,7 @@ namespace BreCalClient.misc.Client
|
||||
{
|
||||
{
|
||||
new Dictionary<string, object> {
|
||||
{"url", "https://brecaldevel.bsmd-emswe.eu"},
|
||||
{"url", "https://brecaltest.bsmd-emswe.eu"},
|
||||
{"description", "Development server hosted on vcup"},
|
||||
}
|
||||
}
|
||||
@ -4844,7 +4844,7 @@ namespace BreCalClient.misc.Client
|
||||
IDictionary<string, string> defaultHeaders,
|
||||
IDictionary<string, string> apiKey,
|
||||
IDictionary<string, string> apiKeyPrefix,
|
||||
string basePath = "https://brecaldevel.bsmd-emswe.eu") : this()
|
||||
string basePath = "https://brecaltest.bsmd-emswe.eu") : this()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(basePath))
|
||||
throw new ArgumentException("The provided basePath is invalid.", "basePath");
|
||||
@ -5191,7 +5191,7 @@ namespace BreCalClient.misc.Client
|
||||
string report = "C# SDK (BreCalClient.misc) Debug Report:\n";
|
||||
report += " OS: " + System.Environment.OSVersion + "\n";
|
||||
report += " .NET Framework Version: " + System.Environment.Version + "\n";
|
||||
report += " Version of the API: 1.4.0\n";
|
||||
report += " Version of the API: 1.5.0\n";
|
||||
report += " SDK Package Version: 1.0.0\n";
|
||||
return report;
|
||||
}
|
||||
@ -5260,7 +5260,7 @@ namespace BreCalClient.misc.Client
|
||||
*
|
||||
* Administer DEBRE ship calls, times and notifications
|
||||
*
|
||||
* The version of the OpenAPI document: 1.4.0
|
||||
* The version of the OpenAPI document: 1.5.0
|
||||
* Contact: info@textbausteine.net
|
||||
* Generated by: https://github.com/openapitools/openapi-generator.git
|
||||
*/
|
||||
@ -5280,7 +5280,7 @@ namespace BreCalClient.misc.Client
|
||||
*
|
||||
* Administer DEBRE ship calls, times and notifications
|
||||
*
|
||||
* The version of the OpenAPI document: 1.4.0
|
||||
* The version of the OpenAPI document: 1.5.0
|
||||
* Contact: info@textbausteine.net
|
||||
* Generated by: https://github.com/openapitools/openapi-generator.git
|
||||
*/
|
||||
@ -5337,7 +5337,7 @@ namespace BreCalClient.misc.Client
|
||||
*
|
||||
* Administer DEBRE ship calls, times and notifications
|
||||
*
|
||||
* The version of the OpenAPI document: 1.4.0
|
||||
* The version of the OpenAPI document: 1.5.0
|
||||
* Contact: info@textbausteine.net
|
||||
* Generated by: https://github.com/openapitools/openapi-generator.git
|
||||
*/
|
||||
@ -5370,7 +5370,7 @@ namespace BreCalClient.misc.Client
|
||||
*
|
||||
* Administer DEBRE ship calls, times and notifications
|
||||
*
|
||||
* The version of the OpenAPI document: 1.4.0
|
||||
* The version of the OpenAPI document: 1.5.0
|
||||
* Contact: info@textbausteine.net
|
||||
* Generated by: https://github.com/openapitools/openapi-generator.git
|
||||
*/
|
||||
@ -5403,7 +5403,7 @@ namespace BreCalClient.misc.Client
|
||||
*
|
||||
* Administer DEBRE ship calls, times and notifications
|
||||
*
|
||||
* The version of the OpenAPI document: 1.4.0
|
||||
* The version of the OpenAPI document: 1.5.0
|
||||
* Contact: info@textbausteine.net
|
||||
* Generated by: https://github.com/openapitools/openapi-generator.git
|
||||
*/
|
||||
@ -5494,7 +5494,7 @@ namespace BreCalClient.misc.Client
|
||||
*
|
||||
* Administer DEBRE ship calls, times and notifications
|
||||
*
|
||||
* The version of the OpenAPI document: 1.4.0
|
||||
* The version of the OpenAPI document: 1.5.0
|
||||
* Contact: info@textbausteine.net
|
||||
* Generated by: https://github.com/openapitools/openapi-generator.git
|
||||
*/
|
||||
@ -5611,7 +5611,7 @@ namespace BreCalClient.misc.Client
|
||||
*
|
||||
* Administer DEBRE ship calls, times and notifications
|
||||
*
|
||||
* The version of the OpenAPI document: 1.4.0
|
||||
* The version of the OpenAPI document: 1.5.0
|
||||
* Contact: info@textbausteine.net
|
||||
* Generated by: https://github.com/openapitools/openapi-generator.git
|
||||
*/
|
||||
@ -5695,7 +5695,7 @@ namespace BreCalClient.misc.Client
|
||||
*
|
||||
* Administer DEBRE ship calls, times and notifications
|
||||
*
|
||||
* The version of the OpenAPI document: 1.4.0
|
||||
* The version of the OpenAPI document: 1.5.0
|
||||
* Contact: info@textbausteine.net
|
||||
* Generated by: https://github.com/openapitools/openapi-generator.git
|
||||
*/
|
||||
@ -5954,7 +5954,7 @@ namespace BreCalClient.misc.Client
|
||||
*
|
||||
* Administer DEBRE ship calls, times and notifications
|
||||
*
|
||||
* The version of the OpenAPI document: 1.4.0
|
||||
* The version of the OpenAPI document: 1.5.0
|
||||
* Contact: info@textbausteine.net
|
||||
* Generated by: https://github.com/openapitools/openapi-generator.git
|
||||
*/
|
||||
@ -5982,7 +5982,7 @@ namespace BreCalClient.misc.Client
|
||||
*
|
||||
* Administer DEBRE ship calls, times and notifications
|
||||
*
|
||||
* The version of the OpenAPI document: 1.4.0
|
||||
* The version of the OpenAPI document: 1.5.0
|
||||
* Contact: info@textbausteine.net
|
||||
* Generated by: https://github.com/openapitools/openapi-generator.git
|
||||
*/
|
||||
@ -6052,7 +6052,7 @@ namespace BreCalClient.misc.Client
|
||||
*
|
||||
* Administer DEBRE ship calls, times and notifications
|
||||
*
|
||||
* The version of the OpenAPI document: 1.4.0
|
||||
* The version of the OpenAPI document: 1.5.0
|
||||
* Contact: info@textbausteine.net
|
||||
* Generated by: https://github.com/openapitools/openapi-generator.git
|
||||
*/
|
||||
@ -6079,7 +6079,7 @@ namespace BreCalClient.misc.Client
|
||||
*
|
||||
* Administer DEBRE ship calls, times and notifications
|
||||
*
|
||||
* The version of the OpenAPI document: 1.4.0
|
||||
* The version of the OpenAPI document: 1.5.0
|
||||
* Contact: info@textbausteine.net
|
||||
* Generated by: https://github.com/openapitools/openapi-generator.git
|
||||
*/
|
||||
@ -6146,7 +6146,7 @@ namespace BreCalClient.misc.Model
|
||||
*
|
||||
* Administer DEBRE ship calls, times and notifications
|
||||
*
|
||||
* The version of the OpenAPI document: 1.4.0
|
||||
* The version of the OpenAPI document: 1.5.0
|
||||
* Contact: info@textbausteine.net
|
||||
* Generated by: https://github.com/openapitools/openapi-generator.git
|
||||
*/
|
||||
@ -6272,7 +6272,7 @@ namespace BreCalClient.misc.Model
|
||||
*
|
||||
* Administer DEBRE ship calls, times and notifications
|
||||
*
|
||||
* The version of the OpenAPI document: 1.4.0
|
||||
* The version of the OpenAPI document: 1.5.0
|
||||
* Contact: info@textbausteine.net
|
||||
* Generated by: https://github.com/openapitools/openapi-generator.git
|
||||
*/
|
||||
@ -6357,14 +6357,14 @@ namespace BreCalClient.misc.Model
|
||||
*
|
||||
* Administer DEBRE ship calls, times and notifications
|
||||
*
|
||||
* The version of the OpenAPI document: 1.4.0
|
||||
* The version of the OpenAPI document: 1.5.0
|
||||
* Contact: info@textbausteine.net
|
||||
* Generated by: https://github.com/openapitools/openapi-generator.git
|
||||
*/
|
||||
namespace BreCalClient.misc.Model
|
||||
{
|
||||
/// <summary>
|
||||
/// Error
|
||||
/// Structure returned when invalid data (bad request) is created.
|
||||
/// </summary>
|
||||
[DataContract(Name = "Error")]
|
||||
public partial class Error : IValidatableObject
|
||||
@ -6378,7 +6378,9 @@ namespace BreCalClient.misc.Model
|
||||
/// Initializes a new instance of the <see cref="Error" /> class.
|
||||
/// </summary>
|
||||
/// <param name="message">A human readable error message (required).</param>
|
||||
public Error(string message = default(string))
|
||||
/// <param name="errors">A list of errors.</param>
|
||||
/// <param name="validData">A dictionary of valid data.</param>
|
||||
public Error(string message = default(string), List<string> errors = default(List<string>), Object validData = default(Object))
|
||||
{
|
||||
// to ensure "message" is required (not null)
|
||||
if (message == null)
|
||||
@ -6386,6 +6388,8 @@ namespace BreCalClient.misc.Model
|
||||
throw new ArgumentNullException("message is a required property for Error and cannot be null");
|
||||
}
|
||||
this.Message = message;
|
||||
this.Errors = errors;
|
||||
this.ValidData = validData;
|
||||
}
|
||||
/// <summary>
|
||||
/// A human readable error message
|
||||
@ -6394,6 +6398,18 @@ namespace BreCalClient.misc.Model
|
||||
[DataMember(Name = "message", IsRequired = true, EmitDefaultValue = true)]
|
||||
public string Message { get; set; }
|
||||
/// <summary>
|
||||
/// A list of errors
|
||||
/// </summary>
|
||||
/// <value>A list of errors</value>
|
||||
[DataMember(Name = "errors", EmitDefaultValue = true)]
|
||||
public List<string> Errors { get; set; }
|
||||
/// <summary>
|
||||
/// A dictionary of valid data
|
||||
/// </summary>
|
||||
/// <value>A dictionary of valid data</value>
|
||||
[DataMember(Name = "valid_data", EmitDefaultValue = true)]
|
||||
public Object ValidData { get; set; }
|
||||
/// <summary>
|
||||
/// Returns the string presentation of the object
|
||||
/// </summary>
|
||||
/// <returns>String presentation of the object</returns>
|
||||
@ -6402,6 +6418,8 @@ namespace BreCalClient.misc.Model
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.Append("class Error {\n");
|
||||
sb.Append(" Message: ").Append(Message).Append("\n");
|
||||
sb.Append(" Errors: ").Append(Errors).Append("\n");
|
||||
sb.Append(" ValidData: ").Append(ValidData).Append("\n");
|
||||
sb.Append("}\n");
|
||||
return sb.ToString();
|
||||
}
|
||||
@ -6430,7 +6448,7 @@ namespace BreCalClient.misc.Model
|
||||
*
|
||||
* Administer DEBRE ship calls, times and notifications
|
||||
*
|
||||
* The version of the OpenAPI document: 1.4.0
|
||||
* The version of the OpenAPI document: 1.5.0
|
||||
* Contact: info@textbausteine.net
|
||||
* Generated by: https://github.com/openapitools/openapi-generator.git
|
||||
*/
|
||||
@ -6470,7 +6488,7 @@ namespace BreCalClient.misc.Model
|
||||
*
|
||||
* Administer DEBRE ship calls, times and notifications
|
||||
*
|
||||
* The version of the OpenAPI document: 1.4.0
|
||||
* The version of the OpenAPI document: 1.5.0
|
||||
* Contact: info@textbausteine.net
|
||||
* Generated by: https://github.com/openapitools/openapi-generator.git
|
||||
*/
|
||||
@ -6580,7 +6598,7 @@ namespace BreCalClient.misc.Model
|
||||
*
|
||||
* Administer DEBRE ship calls, times and notifications
|
||||
*
|
||||
* The version of the OpenAPI document: 1.4.0
|
||||
* The version of the OpenAPI document: 1.5.0
|
||||
* Contact: info@textbausteine.net
|
||||
* Generated by: https://github.com/openapitools/openapi-generator.git
|
||||
*/
|
||||
@ -6642,7 +6660,7 @@ namespace BreCalClient.misc.Model
|
||||
*
|
||||
* Administer DEBRE ship calls, times and notifications
|
||||
*
|
||||
* The version of the OpenAPI document: 1.4.0
|
||||
* The version of the OpenAPI document: 1.5.0
|
||||
* Contact: info@textbausteine.net
|
||||
* Generated by: https://github.com/openapitools/openapi-generator.git
|
||||
*/
|
||||
@ -6768,7 +6786,7 @@ namespace BreCalClient.misc.Model
|
||||
*
|
||||
* Administer DEBRE ship calls, times and notifications
|
||||
*
|
||||
* The version of the OpenAPI document: 1.4.0
|
||||
* The version of the OpenAPI document: 1.5.0
|
||||
* Contact: info@textbausteine.net
|
||||
* Generated by: https://github.com/openapitools/openapi-generator.git
|
||||
*/
|
||||
@ -6872,7 +6890,7 @@ namespace BreCalClient.misc.Model
|
||||
*
|
||||
* Administer DEBRE ship calls, times and notifications
|
||||
*
|
||||
* The version of the OpenAPI document: 1.4.0
|
||||
* The version of the OpenAPI document: 1.5.0
|
||||
* Contact: info@textbausteine.net
|
||||
* Generated by: https://github.com/openapitools/openapi-generator.git
|
||||
*/
|
||||
@ -6907,7 +6925,7 @@ namespace BreCalClient.misc.Model
|
||||
*
|
||||
* Administer DEBRE ship calls, times and notifications
|
||||
*
|
||||
* The version of the OpenAPI document: 1.4.0
|
||||
* The version of the OpenAPI document: 1.5.0
|
||||
* Contact: info@textbausteine.net
|
||||
* Generated by: https://github.com/openapitools/openapi-generator.git
|
||||
*/
|
||||
@ -6937,7 +6955,7 @@ namespace BreCalClient.misc.Model
|
||||
*
|
||||
* Administer DEBRE ship calls, times and notifications
|
||||
*
|
||||
* The version of the OpenAPI document: 1.4.0
|
||||
* The version of the OpenAPI document: 1.5.0
|
||||
* Contact: info@textbausteine.net
|
||||
* Generated by: https://github.com/openapitools/openapi-generator.git
|
||||
*/
|
||||
@ -6977,7 +6995,7 @@ namespace BreCalClient.misc.Model
|
||||
*
|
||||
* Administer DEBRE ship calls, times and notifications
|
||||
*
|
||||
* The version of the OpenAPI document: 1.4.0
|
||||
* The version of the OpenAPI document: 1.5.0
|
||||
* Contact: info@textbausteine.net
|
||||
* Generated by: https://github.com/openapitools/openapi-generator.git
|
||||
*/
|
||||
@ -7134,7 +7152,7 @@ namespace BreCalClient.misc.Model
|
||||
*
|
||||
* Administer DEBRE ship calls, times and notifications
|
||||
*
|
||||
* The version of the OpenAPI document: 1.4.0
|
||||
* The version of the OpenAPI document: 1.5.0
|
||||
* Contact: info@textbausteine.net
|
||||
* Generated by: https://github.com/openapitools/openapi-generator.git
|
||||
*/
|
||||
@ -7210,7 +7228,7 @@ namespace BreCalClient.misc.Model
|
||||
*
|
||||
* Administer DEBRE ship calls, times and notifications
|
||||
*
|
||||
* The version of the OpenAPI document: 1.4.0
|
||||
* The version of the OpenAPI document: 1.5.0
|
||||
* Contact: info@textbausteine.net
|
||||
* Generated by: https://github.com/openapitools/openapi-generator.git
|
||||
*/
|
||||
@ -7380,7 +7398,7 @@ namespace BreCalClient.misc.Model
|
||||
*
|
||||
* Administer DEBRE ship calls, times and notifications
|
||||
*
|
||||
* The version of the OpenAPI document: 1.4.0
|
||||
* The version of the OpenAPI document: 1.5.0
|
||||
* Contact: info@textbausteine.net
|
||||
* Generated by: https://github.com/openapitools/openapi-generator.git
|
||||
*/
|
||||
@ -7695,7 +7713,7 @@ namespace BreCalClient.misc.Model
|
||||
*
|
||||
* Administer DEBRE ship calls, times and notifications
|
||||
*
|
||||
* The version of the OpenAPI document: 1.4.0
|
||||
* The version of the OpenAPI document: 1.5.0
|
||||
* Contact: info@textbausteine.net
|
||||
* Generated by: https://github.com/openapitools/openapi-generator.git
|
||||
*/
|
||||
@ -7735,7 +7753,7 @@ namespace BreCalClient.misc.Model
|
||||
*
|
||||
* Administer DEBRE ship calls, times and notifications
|
||||
*
|
||||
* The version of the OpenAPI document: 1.4.0
|
||||
* The version of the OpenAPI document: 1.5.0
|
||||
* Contact: info@textbausteine.net
|
||||
* Generated by: https://github.com/openapitools/openapi-generator.git
|
||||
*/
|
||||
@ -8017,7 +8035,7 @@ namespace BreCalClient.misc.Model
|
||||
*
|
||||
* Administer DEBRE ship calls, times and notifications
|
||||
*
|
||||
* The version of the OpenAPI document: 1.4.0
|
||||
* The version of the OpenAPI document: 1.5.0
|
||||
* Contact: info@textbausteine.net
|
||||
* Generated by: https://github.com/openapitools/openapi-generator.git
|
||||
*/
|
||||
|
||||
@ -2,7 +2,7 @@ openapi: 3.0.0
|
||||
x-stoplight:
|
||||
id: mwv4y8vcnopwr
|
||||
info:
|
||||
version: 1.4.1
|
||||
version: 1.5.0
|
||||
title: Bremen calling API
|
||||
description: 'Administer DEBRE ship calls, times and notifications'
|
||||
termsOfService: 'https://www.bsmd.de/'
|
||||
@ -14,7 +14,7 @@ info:
|
||||
name: Use at your own risk
|
||||
url: 'https://www.bsmd.de/license'
|
||||
servers:
|
||||
- url: 'https://brecaldevel.bsmd-emswe.eu'
|
||||
- url: 'https://brecaltest.bsmd-emswe.eu'
|
||||
description: Development server hosted on vcup
|
||||
tags:
|
||||
- name: user
|
||||
@ -943,12 +943,21 @@ components:
|
||||
type: integer
|
||||
Error:
|
||||
type: object
|
||||
required:
|
||||
- message
|
||||
description: Structure returned when invalid data (bad request) is created.
|
||||
properties:
|
||||
message:
|
||||
description: A human readable error message
|
||||
type: string
|
||||
errors:
|
||||
description: A list of errors
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
valid_data:
|
||||
description: A dictionary of valid data
|
||||
type: object
|
||||
required:
|
||||
- message
|
||||
ShipcallType:
|
||||
type: string
|
||||
enum:
|
||||
|
||||
@ -29,7 +29,7 @@
|
||||
<applicationSettings>
|
||||
<BreCalClient.Properties.Settings>
|
||||
<setting name="BG_COLOR" serializeAs="String">
|
||||
<value>#1D751F</value>
|
||||
<value>#751D1F</value>
|
||||
</setting>
|
||||
<setting name="APP_TITLE" serializeAs="String">
|
||||
<value>!!Bremen calling Testversion!!</value>
|
||||
@ -38,7 +38,7 @@
|
||||
<value>https://www.textbausteine.net/</value>
|
||||
</setting>
|
||||
<setting name="API_URL" serializeAs="String">
|
||||
<value>https://brecaldevel.bsmd-emswe.eu</value>
|
||||
<value>https://brecaltest.bsmd-emswe.eu</value>
|
||||
</setting>
|
||||
</BreCalClient.Properties.Settings>
|
||||
</applicationSettings>
|
||||
|
||||
@ -8,12 +8,12 @@
|
||||
<SignAssembly>True</SignAssembly>
|
||||
<StartupObject>BreCalClient.App</StartupObject>
|
||||
<AssemblyOriginatorKeyFile>..\..\misc\brecal.snk</AssemblyOriginatorKeyFile>
|
||||
<AssemblyVersion>1.4.1.0</AssemblyVersion>
|
||||
<FileVersion>1.4.1.0</FileVersion>
|
||||
<AssemblyVersion>1.5.0.4</AssemblyVersion>
|
||||
<FileVersion>1.5.0.4</FileVersion>
|
||||
<Title>Bremen calling client</Title>
|
||||
<Description>A Windows WPF client for the Bremen calling API.</Description>
|
||||
<ApplicationIcon>containership.ico</ApplicationIcon>
|
||||
<AssemblyName>BreCalDevelClient</AssemblyName>
|
||||
<AssemblyName>BreCalTestClient</AssemblyName>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@ -116,12 +116,12 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Extended.Wpf.Toolkit" Version="4.6.0" />
|
||||
<PackageReference Include="Extended.Wpf.Toolkit" Version="4.6.1" />
|
||||
<PackageReference Include="JsonSubTypes" Version="2.0.1" />
|
||||
<PackageReference Include="log4net" Version="2.0.17" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="Polly" Version="8.3.1" />
|
||||
<PackageReference Include="RestSharp" Version="110.2.0" />
|
||||
<PackageReference Include="Polly" Version="8.4.1" />
|
||||
<PackageReference Include="RestSharp" Version="112.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@ -41,8 +41,8 @@ namespace BreCalClient
|
||||
}
|
||||
this.Ship.Imo = this.integerUpDownIMO.Value;
|
||||
this.Ship.Callsign = this.textBoxCallsign.Text.ToUpper().Trim();
|
||||
this.Ship.Length = (float?) this.doubleUpDownLength.Value;
|
||||
this.Ship.Width = (float?) this.doubleUpDownWidth.Value;
|
||||
this.Ship.Length = this.doubleUpDownLength.Value;
|
||||
this.Ship.Width = this.doubleUpDownWidth.Value;
|
||||
this.DialogResult = true;
|
||||
this.Close();
|
||||
|
||||
|
||||
@ -205,15 +205,23 @@ namespace BreCalClient
|
||||
case ShipcallType.Departure:
|
||||
isEnabled &= this.comboBoxDepartureBerth.SelectedItem != null;
|
||||
isEnabled &= this.datePickerETD.Value.HasValue;
|
||||
if(this.datePickerETD.Value.HasValue)
|
||||
isEnabled &= (this.datePickerETD.Value.Value > DateTime.Now);
|
||||
break;
|
||||
case ShipcallType.Arrival:
|
||||
isEnabled &= this.comboBoxArrivalBerth.SelectedItem != null;
|
||||
isEnabled &= this.datePickerETA.Value.HasValue;
|
||||
if(this.datePickerETA.Value.HasValue)
|
||||
isEnabled &= (this.datePickerETA.Value.Value > DateTime.Now);
|
||||
break;
|
||||
case ShipcallType.Shifting:
|
||||
isEnabled &= ((this.comboBoxDepartureBerth.SelectedItem != null) && (this.comboBoxArrivalBerth.SelectedItem != null));
|
||||
isEnabled &= this.datePickerETD.Value.HasValue;
|
||||
isEnabled &= this.datePickerETA.Value.HasValue;
|
||||
if (this.datePickerETD.Value.HasValue)
|
||||
isEnabled &= (this.datePickerETD.Value.Value > DateTime.Now);
|
||||
if (this.datePickerETA.Value.HasValue)
|
||||
isEnabled &= (this.datePickerETA.Value.Value > DateTime.Now);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
|
||||
using BreCalClient.misc.Model;
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Windows;
|
||||
using static BreCalClient.Extensions;
|
||||
|
||||
@ -70,9 +71,16 @@ namespace BreCalClient
|
||||
|
||||
private void buttonOK_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
this.CopyToModel();
|
||||
this.DialogResult = true;
|
||||
this.Close();
|
||||
if (!CheckValues(out string message))
|
||||
{
|
||||
MessageBox.Show(message, "Warning", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.CopyToModel();
|
||||
this.DialogResult = true;
|
||||
this.Close();
|
||||
}
|
||||
}
|
||||
|
||||
private void buttonCancel_Click(object sender, RoutedEventArgs e)
|
||||
@ -85,6 +93,49 @@ namespace BreCalClient
|
||||
|
||||
#region private methods
|
||||
|
||||
private bool CheckValues(out string message)
|
||||
{
|
||||
message = "";
|
||||
|
||||
if (this.datePickerETA.Value.HasValue && (this.datePickerETA.Value.Value < DateTime.Now) && (this.datePickerETA_End.Value == null))
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textETAInThePast;
|
||||
return false;
|
||||
}
|
||||
|
||||
if(this.datePickerETA_End.Value.HasValue && this.datePickerETA_End.Value < DateTime.Now)
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textETAInThePast;
|
||||
return false;
|
||||
}
|
||||
|
||||
if(this.datePickerETA.Value.HasValue && this.datePickerETA_End.Value.HasValue && this.datePickerETA.Value > this.datePickerETA_End.Value)
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textEndValueBeforeStartValue;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.datePickerTidalWindowFrom.Value.HasValue && (this.datePickerTidalWindowFrom.Value.Value < DateTime.Now) && (this.datePickerTidalWindowTo.Value == null))
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textTideTimesInThePast;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.datePickerTidalWindowTo.Value.HasValue && this.datePickerTidalWindowTo.Value < DateTime.Now)
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textTideTimesInThePast;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.datePickerTidalWindowFrom.Value.HasValue && this.datePickerTidalWindowTo.Value.HasValue && this.datePickerTidalWindowFrom.Value > this.datePickerTidalWindowTo.Value)
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textEndValueBeforeStartValue;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void CopyToModel()
|
||||
{
|
||||
if (this.ShipcallModel.Shipcall != null)
|
||||
|
||||
@ -80,9 +80,16 @@ namespace BreCalClient
|
||||
|
||||
private void buttonOK_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
this.CopyToModel();
|
||||
this.DialogResult = true;
|
||||
this.Close();
|
||||
if (!CheckValues(out string message))
|
||||
{
|
||||
System.Windows.MessageBox.Show(message, "Warning", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.CopyToModel();
|
||||
this.DialogResult = true;
|
||||
this.Close();
|
||||
}
|
||||
}
|
||||
|
||||
private void buttonCancel_Click(object sender, RoutedEventArgs e)
|
||||
@ -95,6 +102,50 @@ namespace BreCalClient
|
||||
|
||||
#region private methods
|
||||
|
||||
|
||||
private bool CheckValues(out string message)
|
||||
{
|
||||
message = "";
|
||||
|
||||
if (this.datePickerETD.Value.HasValue && (this.datePickerETD.Value.Value < DateTime.Now) && (this.datePickerETD_End.Value == null))
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textETDInThePast;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.datePickerETD_End.Value.HasValue && this.datePickerETD_End.Value < DateTime.Now)
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textETDInThePast;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.datePickerETD.Value.HasValue && this.datePickerETD_End.Value.HasValue && this.datePickerETD.Value > this.datePickerETD_End.Value)
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textEndValueBeforeStartValue;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.datePickerTidalWindowFrom.Value.HasValue && (this.datePickerTidalWindowFrom.Value.Value < DateTime.Now) && (this.datePickerTidalWindowTo.Value == null))
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textTideTimesInThePast;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.datePickerTidalWindowTo.Value.HasValue && this.datePickerTidalWindowTo.Value < DateTime.Now)
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textTideTimesInThePast;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.datePickerTidalWindowFrom.Value.HasValue && this.datePickerTidalWindowTo.Value.HasValue && this.datePickerTidalWindowFrom.Value > this.datePickerTidalWindowTo.Value)
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textEndValueBeforeStartValue;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void CopyToModel()
|
||||
{
|
||||
if (this.ShipcallModel.Shipcall != null)
|
||||
|
||||
@ -71,9 +71,16 @@ namespace BreCalClient
|
||||
|
||||
private void buttonOK_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
this.CopyToModel();
|
||||
this.DialogResult = true;
|
||||
this.Close();
|
||||
if (!CheckValues(out string message))
|
||||
{
|
||||
System.Windows.MessageBox.Show(message, "Warning", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.CopyToModel();
|
||||
this.DialogResult = true;
|
||||
this.Close();
|
||||
}
|
||||
}
|
||||
|
||||
private void buttonCancel_Click(object sender, RoutedEventArgs e)
|
||||
@ -86,6 +93,67 @@ namespace BreCalClient
|
||||
|
||||
#region private methods
|
||||
|
||||
private bool CheckValues(out string message)
|
||||
{
|
||||
message = "";
|
||||
|
||||
if (this.datePickerETA.Value.HasValue && (this.datePickerETA.Value.Value < DateTime.Now) && (this.datePickerETA_End.Value == null))
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textETAInThePast;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.datePickerETA_End.Value.HasValue && this.datePickerETA_End.Value < DateTime.Now)
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textETAInThePast;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.datePickerETA.Value.HasValue && this.datePickerETA_End.Value.HasValue && this.datePickerETA.Value > this.datePickerETA_End.Value)
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textEndValueBeforeStartValue;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.datePickerETD.Value.HasValue && (this.datePickerETD.Value.Value < DateTime.Now) && (this.datePickerETD_End.Value == null))
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textETDInThePast;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.datePickerETD_End.Value.HasValue && this.datePickerETD_End.Value < DateTime.Now)
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textETDInThePast;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.datePickerETD.Value.HasValue && this.datePickerETD_End.Value.HasValue && this.datePickerETD.Value > this.datePickerETD_End.Value)
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textEndValueBeforeStartValue;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.datePickerTidalWindowFrom.Value.HasValue && (this.datePickerTidalWindowFrom.Value.Value < DateTime.Now) && (this.datePickerTidalWindowTo.Value == null))
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textTideTimesInThePast;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.datePickerTidalWindowTo.Value.HasValue && this.datePickerTidalWindowTo.Value < DateTime.Now)
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textTideTimesInThePast;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.datePickerTidalWindowFrom.Value.HasValue && this.datePickerTidalWindowTo.Value.HasValue && this.datePickerTidalWindowFrom.Value > this.datePickerTidalWindowTo.Value)
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textEndValueBeforeStartValue;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void CopyToModel()
|
||||
{
|
||||
if (this.ShipcallModel.Shipcall != null)
|
||||
|
||||
@ -44,7 +44,7 @@
|
||||
<ColumnDefinition Width=".5*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<local:DateTimePickerExt IsEnabled="False" Grid.Row="0" Grid.Column="0" Margin="4,2,0,2" x:Name="datePickerETABerth" Format="Custom" FormatString="dd.MM. yyyy HH:mm">
|
||||
<local:DateTimePickerExt IsEnabled="False" Grid.Row="0" Grid.Column="0" Margin="2" x:Name="datePickerETABerth" Format="Custom" FormatString="dd.MM. yyyy HH:mm">
|
||||
<xctk:DateTimePicker.ContextMenu>
|
||||
<ContextMenu>
|
||||
<MenuItem Header="{x:Static p:Resources.textClearValue}" Name="contextMenuItemClearETA" Click="contextMenuItemClearETA_Click" >
|
||||
|
||||
@ -46,9 +46,16 @@ namespace BreCalClient
|
||||
|
||||
private void buttonOK_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
this.CopyToModel();
|
||||
this.DialogResult = true;
|
||||
this.Close();
|
||||
if (!CheckValues(out string message))
|
||||
{
|
||||
System.Windows.MessageBox.Show(message, "Warning", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.CopyToModel();
|
||||
this.DialogResult = true;
|
||||
this.Close();
|
||||
}
|
||||
}
|
||||
|
||||
private void buttonCancel_Click(object sender, RoutedEventArgs e)
|
||||
@ -85,6 +92,44 @@ namespace BreCalClient
|
||||
|
||||
#region private methods
|
||||
|
||||
private bool CheckValues(out string message)
|
||||
{
|
||||
|
||||
message = "";
|
||||
|
||||
if (this.datePickerETABerth.Value.HasValue && (this.datePickerETABerth.Value.Value < DateTime.Now) && (this.datePickerETABerth_End.Value == null))
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textETAInThePast;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.datePickerETABerth_End.Value.HasValue && this.datePickerETABerth_End.Value < DateTime.Now)
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textETAInThePast;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.datePickerETABerth.Value.HasValue && this.datePickerETABerth_End.Value.HasValue && this.datePickerETABerth.Value > this.datePickerETABerth_End.Value)
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textEndValueBeforeStartValue;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.datePickerLockTime.Value.HasValue && (this.datePickerLockTime.Value.Value < DateTime.Now))
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textLockTimeInThePast;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.datePickerZoneEntry.Value.HasValue && this.datePickerZoneEntry.Value < DateTime.Now)
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textZoneEntryInThePast;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void CopyToModel()
|
||||
{
|
||||
this.Times.Remarks = this.textBoxRemarks.Text.Trim().Truncate(512);
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
//
|
||||
|
||||
using BreCalClient.misc.Model;
|
||||
using System;
|
||||
using System.Windows;
|
||||
|
||||
namespace BreCalClient
|
||||
@ -61,9 +62,16 @@ namespace BreCalClient
|
||||
|
||||
private void buttonOK_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
this.CopyToModel();
|
||||
this.DialogResult = true;
|
||||
this.Close();
|
||||
if (!CheckValues(out string message))
|
||||
{
|
||||
System.Windows.MessageBox.Show(message, "Warning", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.CopyToModel();
|
||||
this.DialogResult = true;
|
||||
this.Close();
|
||||
}
|
||||
}
|
||||
|
||||
private void buttonCancel_Click(object sender, RoutedEventArgs e)
|
||||
@ -95,6 +103,50 @@ namespace BreCalClient
|
||||
|
||||
#region private methods
|
||||
|
||||
private bool CheckValues(out string message)
|
||||
{
|
||||
|
||||
message = "";
|
||||
|
||||
if (this.datePickerOperationStart.Value.HasValue && (this.datePickerOperationStart.Value.Value < DateTime.Now) && (this.datePickerOperationStart_End.Value == null))
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textOperationStartInThePast;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.datePickerOperationStart_End.Value.HasValue && this.datePickerOperationStart_End.Value < DateTime.Now)
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textOperationStartInThePast;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.datePickerOperationStart.Value.HasValue && this.datePickerOperationStart_End.Value.HasValue && this.datePickerOperationStart.Value > this.datePickerOperationStart_End.Value)
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textEndValueBeforeStartValue;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.datePickerOperationEnd.Value.HasValue && (this.datePickerOperationEnd.Value.Value < DateTime.Now) && (this.datePickerOperationEnd_End.Value == null))
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textOperationEndInThePast;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.datePickerOperationEnd_End.Value.HasValue && this.datePickerOperationEnd_End.Value < DateTime.Now)
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textOperationEndInThePast;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.datePickerOperationEnd.Value.HasValue && this.datePickerOperationEnd_End.Value.HasValue && this.datePickerOperationEnd.Value > this.datePickerOperationEnd_End.Value)
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textEndValueBeforeStartValue;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void CopyToModel()
|
||||
{
|
||||
this.Times.PierSide = this.comboBoxPierside.SelectedIndex switch
|
||||
|
||||
@ -337,7 +337,7 @@ namespace BreCalClient
|
||||
{
|
||||
this.Dispatcher.Invoke(new Action(() =>
|
||||
{
|
||||
MessageBox.Show(ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
ShowErrorDialog(ex.Message, "Error saving user information");
|
||||
}));
|
||||
}
|
||||
}
|
||||
@ -899,7 +899,10 @@ namespace BreCalClient
|
||||
try
|
||||
{
|
||||
obj.ShipcallControlModel.Shipcall?.Participants.Clear();
|
||||
obj.ShipcallControlModel.UpdateTimesAssignments(this._timesApi);
|
||||
if(! await obj.ShipcallControlModel.UpdateTimesAssignments(this._timesApi))
|
||||
{
|
||||
ShowErrorDialog(obj.ShipcallControlModel.LastErrorMessage, "Update assignments error");
|
||||
}
|
||||
foreach(ParticipantAssignment pa in obj.ShipcallControlModel.AssignedParticipants.Values)
|
||||
obj.ShipcallControlModel.Shipcall?.Participants.Add(pa);
|
||||
await _shipcallApi.ShipcallUpdateAsync(obj.ShipcallControlModel.Shipcall);
|
||||
@ -1011,7 +1014,13 @@ namespace BreCalClient
|
||||
{
|
||||
try
|
||||
{
|
||||
sc.ShipcallControlModel?.UpdateTimesAssignments(_timesApi); // if the agent changed the assignment of the participant to another
|
||||
if (sc.ShipcallControlModel != null)
|
||||
{
|
||||
if (!await sc.ShipcallControlModel.UpdateTimesAssignments(_timesApi)) // if the agent changed the assignment of the participant to another
|
||||
{
|
||||
ShowErrorDialog(sc.ShipcallControlModel.LastErrorMessage, "Error updating times assignment");
|
||||
}
|
||||
}
|
||||
|
||||
// always try to be the agent, even if we are BSMD
|
||||
if (editControl.ShipcallModel.AssignedParticipants.ContainsKey(ParticipantType.AGENCY))
|
||||
@ -1064,14 +1073,27 @@ namespace BreCalClient
|
||||
// Error calling ShipcallUpdate: {\"message\": \"PUT Requests for shipcalls can only be issued by an assigned AGENCY or BSMD users
|
||||
// (if the special-flag is enabled). Assigned Agency: ShipcallParticipantMap(id=628, shipcall_id=115, participant_id=10,
|
||||
// type=8, created=datetime.datetime(2024, 8, 28, 15, 13, 14), modified=None) with Flags: 42\"}
|
||||
|
||||
Match m = Regex.Match(message, "\\{(.*)\\}");
|
||||
if ((m != null) && m.Success)
|
||||
{
|
||||
dynamic? msg = JsonConvert.DeserializeObject(m.Value);
|
||||
if(msg != null)
|
||||
try
|
||||
{
|
||||
message = msg.message;
|
||||
dynamic? msg = JsonConvert.DeserializeObject(m.Value);
|
||||
if (msg != null)
|
||||
{
|
||||
if (msg.error_field != null)
|
||||
{
|
||||
caption = $"{caption}: {msg.error_field}";
|
||||
}
|
||||
|
||||
if(msg.error_description != null)
|
||||
{
|
||||
message = msg.error_description;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception) { }
|
||||
}
|
||||
|
||||
_log.ErrorFormat("{0} - {1}", caption, message);
|
||||
|
||||
@ -5,7 +5,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<ApplicationRevision>0</ApplicationRevision>
|
||||
<ApplicationVersion>1.4.1.0</ApplicationVersion>
|
||||
<ApplicationVersion>1.5.0.4</ApplicationVersion>
|
||||
<BootstrapperEnabled>True</BootstrapperEnabled>
|
||||
<Configuration>Debug</Configuration>
|
||||
<CreateDesktopShortcut>True</CreateDesktopShortcut>
|
||||
|
||||
4
src/BreCalClient/Properties/Settings.Designer.cs
generated
4
src/BreCalClient/Properties/Settings.Designer.cs
generated
@ -25,7 +25,7 @@ namespace BreCalClient.Properties {
|
||||
|
||||
[global::System.Configuration.ApplicationScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("#1D751F")]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("#751D1F")]
|
||||
public string BG_COLOR {
|
||||
get {
|
||||
return ((string)(this["BG_COLOR"]));
|
||||
@ -64,7 +64,7 @@ namespace BreCalClient.Properties {
|
||||
|
||||
[global::System.Configuration.ApplicationScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("https://brecaldevel.bsmd-emswe.eu")]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("https://brecaltest.bsmd-emswe.eu")]
|
||||
public string API_URL {
|
||||
get {
|
||||
return ((string)(this["API_URL"]));
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
<Profiles />
|
||||
<Settings>
|
||||
<Setting Name="BG_COLOR" Type="System.String" Scope="Application">
|
||||
<Value Profile="(Default)">#1D751F</Value>
|
||||
<Value Profile="(Default)">#751D1F</Value>
|
||||
</Setting>
|
||||
<Setting Name="APP_TITLE" Type="System.String" Scope="Application">
|
||||
<Value Profile="(Default)">!!Bremen calling Testversion!!</Value>
|
||||
@ -15,7 +15,7 @@
|
||||
<Value Profile="(Default)" />
|
||||
</Setting>
|
||||
<Setting Name="API_URL" Type="System.String" Scope="Application">
|
||||
<Value Profile="(Default)">https://brecaldevel.bsmd-emswe.eu</Value>
|
||||
<Value Profile="(Default)">https://brecaltest.bsmd-emswe.eu</Value>
|
||||
</Setting>
|
||||
<Setting Name="Width" Type="System.Double" Scope="User">
|
||||
<Value Profile="(Default)">800</Value>
|
||||
|
||||
99
src/BreCalClient/Resources/Resources.Designer.cs
generated
99
src/BreCalClient/Resources/Resources.Designer.cs
generated
@ -389,6 +389,15 @@ namespace BreCalClient.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Both tide times are required.
|
||||
/// </summary>
|
||||
public static string textBothTideTimesNecessary {
|
||||
get {
|
||||
return ResourceManager.GetString("textBothTideTimesNecessary", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to BSMD right to edit granted.
|
||||
/// </summary>
|
||||
@ -623,6 +632,15 @@ namespace BreCalClient.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Some interval end times are before start times.
|
||||
/// </summary>
|
||||
public static string textEndValueBeforeStartValue {
|
||||
get {
|
||||
return ResourceManager.GetString("textEndValueBeforeStartValue", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Ship / remark.
|
||||
/// </summary>
|
||||
@ -632,6 +650,15 @@ namespace BreCalClient.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Error.
|
||||
/// </summary>
|
||||
public static string textError {
|
||||
get {
|
||||
return ResourceManager.GetString("textError", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to ETA berth.
|
||||
/// </summary>
|
||||
@ -641,6 +668,15 @@ namespace BreCalClient.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to ETA value is in the past.
|
||||
/// </summary>
|
||||
public static string textETAInThePast {
|
||||
get {
|
||||
return ResourceManager.GetString("textETAInThePast", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to ETD berth.
|
||||
/// </summary>
|
||||
@ -650,6 +686,15 @@ namespace BreCalClient.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to ETD value is in the past.
|
||||
/// </summary>
|
||||
public static string textETDInThePast {
|
||||
get {
|
||||
return ResourceManager.GetString("textETDInThePast", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Exit.
|
||||
/// </summary>
|
||||
@ -740,6 +785,15 @@ namespace BreCalClient.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Lock time is in the past.
|
||||
/// </summary>
|
||||
public static string textLockTimeInThePast {
|
||||
get {
|
||||
return ResourceManager.GetString("textLockTimeInThePast", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Login.
|
||||
/// </summary>
|
||||
@ -830,6 +884,15 @@ namespace BreCalClient.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Operation end is in the past.
|
||||
/// </summary>
|
||||
public static string textOperationEndInThePast {
|
||||
get {
|
||||
return ResourceManager.GetString("textOperationEndInThePast", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Operations end.
|
||||
/// </summary>
|
||||
@ -848,6 +911,15 @@ namespace BreCalClient.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Operation start is in the past.
|
||||
/// </summary>
|
||||
public static string textOperationStartInThePast {
|
||||
get {
|
||||
return ResourceManager.GetString("textOperationStartInThePast", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Outgoing.
|
||||
/// </summary>
|
||||
@ -1145,6 +1217,15 @@ namespace BreCalClient.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Times for the tide window are in the past.
|
||||
/// </summary>
|
||||
public static string textTideTimesInThePast {
|
||||
get {
|
||||
return ResourceManager.GetString("textTideTimesInThePast", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Timestamp.
|
||||
/// </summary>
|
||||
@ -1262,6 +1343,15 @@ namespace BreCalClient.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Warning.
|
||||
/// </summary>
|
||||
public static string textWarning {
|
||||
get {
|
||||
return ResourceManager.GetString("textWarning", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Width.
|
||||
/// </summary>
|
||||
@ -1280,6 +1370,15 @@ namespace BreCalClient.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Zone entry time is in the past.
|
||||
/// </summary>
|
||||
public static string textZoneEntryInThePast {
|
||||
get {
|
||||
return ResourceManager.GetString("textZoneEntryInThePast", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Zone entry.
|
||||
/// </summary>
|
||||
|
||||
@ -508,4 +508,37 @@
|
||||
<data name="textClearAll" xml:space="preserve">
|
||||
<value>Alle Eintragungen zurücksetzen?</value>
|
||||
</data>
|
||||
<data name="textBothTideTimesNecessary" xml:space="preserve">
|
||||
<value>Beide Tidenzeiten sollten angegeben werden (von - bis)</value>
|
||||
</data>
|
||||
<data name="textEndValueBeforeStartValue" xml:space="preserve">
|
||||
<value>Endzeit liegt vor Startzeit</value>
|
||||
</data>
|
||||
<data name="textError" xml:space="preserve">
|
||||
<value>Error</value>
|
||||
</data>
|
||||
<data name="textETAInThePast" xml:space="preserve">
|
||||
<value>Zeitpunkt ETA liegt in der Vergangenheit</value>
|
||||
</data>
|
||||
<data name="textETDInThePast" xml:space="preserve">
|
||||
<value>Zeitpunkt ETD liegt in der Vergangenheit</value>
|
||||
</data>
|
||||
<data name="textLockTimeInThePast" xml:space="preserve">
|
||||
<value>Schleusenzeit liegt in der Vergangenheit</value>
|
||||
</data>
|
||||
<data name="textOperationEndInThePast" xml:space="preserve">
|
||||
<value>Operation Endzeit liegt in der Vergangenheit</value>
|
||||
</data>
|
||||
<data name="textOperationStartInThePast" xml:space="preserve">
|
||||
<value>Operation Startzeit liegt in der Vergangenheit</value>
|
||||
</data>
|
||||
<data name="textTideTimesInThePast" xml:space="preserve">
|
||||
<value>Tidenzeit liegt in der Vergangenheit</value>
|
||||
</data>
|
||||
<data name="textWarning" xml:space="preserve">
|
||||
<value>Warnung</value>
|
||||
</data>
|
||||
<data name="textZoneEntryInThePast" xml:space="preserve">
|
||||
<value>Zeit Reviereintritt liegt in der Vergangenheit</value>
|
||||
</data>
|
||||
</root>
|
||||
@ -214,6 +214,9 @@
|
||||
<data name="textBerths" xml:space="preserve">
|
||||
<value>Berths</value>
|
||||
</data>
|
||||
<data name="textBothTideTimesNecessary" xml:space="preserve">
|
||||
<value>Both tide times are required</value>
|
||||
</data>
|
||||
<data name="textBSMDGranted" xml:space="preserve">
|
||||
<value>BSMD right to edit granted</value>
|
||||
</data>
|
||||
@ -292,15 +295,27 @@
|
||||
<data name="textEmail" xml:space="preserve">
|
||||
<value>E-mail</value>
|
||||
</data>
|
||||
<data name="textEndValueBeforeStartValue" xml:space="preserve">
|
||||
<value>Some interval end times are before start times</value>
|
||||
</data>
|
||||
<data name="textEnterKeyword" xml:space="preserve">
|
||||
<value>Ship / remark</value>
|
||||
</data>
|
||||
<data name="textError" xml:space="preserve">
|
||||
<value>Error</value>
|
||||
</data>
|
||||
<data name="textETABerth" xml:space="preserve">
|
||||
<value>ETA berth</value>
|
||||
</data>
|
||||
<data name="textETAInThePast" xml:space="preserve">
|
||||
<value>ETA value is in the past</value>
|
||||
</data>
|
||||
<data name="textETDBerth" xml:space="preserve">
|
||||
<value>ETD berth</value>
|
||||
</data>
|
||||
<data name="textETDInThePast" xml:space="preserve">
|
||||
<value>ETD value is in the past</value>
|
||||
</data>
|
||||
<data name="textExit" xml:space="preserve">
|
||||
<value>Exit</value>
|
||||
</data>
|
||||
@ -331,6 +346,9 @@
|
||||
<data name="textLockTime" xml:space="preserve">
|
||||
<value>Lock time</value>
|
||||
</data>
|
||||
<data name="textLockTimeInThePast" xml:space="preserve">
|
||||
<value>Lock time is in the past</value>
|
||||
</data>
|
||||
<data name="textLogin" xml:space="preserve">
|
||||
<value>Login</value>
|
||||
</data>
|
||||
@ -361,12 +379,18 @@
|
||||
<data name="textOperation" xml:space="preserve">
|
||||
<value>Operation</value>
|
||||
</data>
|
||||
<data name="textOperationEndInThePast" xml:space="preserve">
|
||||
<value>Operation end is in the past</value>
|
||||
</data>
|
||||
<data name="textOperationsEnd" xml:space="preserve">
|
||||
<value>Operations end</value>
|
||||
</data>
|
||||
<data name="textOperationsStart" xml:space="preserve">
|
||||
<value>Operations start</value>
|
||||
</data>
|
||||
<data name="textOperationStartInThePast" xml:space="preserve">
|
||||
<value>Operation start is in the past</value>
|
||||
</data>
|
||||
<data name="textOutgoing" xml:space="preserve">
|
||||
<value>Outgoing</value>
|
||||
</data>
|
||||
@ -466,6 +490,9 @@
|
||||
<data name="textTidalWindow" xml:space="preserve">
|
||||
<value>Tidal window</value>
|
||||
</data>
|
||||
<data name="textTideTimesInThePast" xml:space="preserve">
|
||||
<value>Times for the tide window are in the past</value>
|
||||
</data>
|
||||
<data name="textTimestamp" xml:space="preserve">
|
||||
<value>Timestamp</value>
|
||||
</data>
|
||||
@ -505,12 +532,18 @@
|
||||
<data name="textVoyage" xml:space="preserve">
|
||||
<value>Voyage</value>
|
||||
</data>
|
||||
<data name="textWarning" xml:space="preserve">
|
||||
<value>Warning</value>
|
||||
</data>
|
||||
<data name="textWidth" xml:space="preserve">
|
||||
<value>Width</value>
|
||||
</data>
|
||||
<data name="textWrongCredentials" xml:space="preserve">
|
||||
<value>Wrong username or password</value>
|
||||
</data>
|
||||
<data name="textZoneEntryInThePast" xml:space="preserve">
|
||||
<value>Zone entry time is in the past</value>
|
||||
</data>
|
||||
<data name="textZoneEntryTime" xml:space="preserve">
|
||||
<value>Zone entry</value>
|
||||
</data>
|
||||
|
||||
@ -49,8 +49,15 @@ namespace BreCalClient
|
||||
{
|
||||
if (!shipmodel.Ship.Deleted)
|
||||
{
|
||||
if (this.ShipApi != null)
|
||||
await this.ShipApi.ShipDeleteAsync(shipmodel.Ship.Id);
|
||||
try
|
||||
{
|
||||
if (this.ShipApi != null)
|
||||
await this.ShipApi.ShipDeleteAsync(shipmodel.Ship.Id);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show(ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
BreCalLists.Ships.Remove(shipmodel); // remove from "selectable" ships
|
||||
shipmodel.Ship.Deleted = true; // set deleted marker on working instance
|
||||
this.dataGridShips.ItemsSource = null;
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:p = "clr-namespace:BreCalClient.Resources"
|
||||
xmlns:sets="clr-namespace:BreCalClient.Properties"
|
||||
xmlns:db="clr-namespace:BreCalClient;assembly=BreCalDevelClient"
|
||||
xmlns:db="clr-namespace:BreCalClient;assembly=BreCalTestClient"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="135" d:DesignWidth="800">
|
||||
<Border BorderBrush="LightGray" Margin="1" BorderThickness="1">
|
||||
|
||||
@ -216,13 +216,13 @@ namespace BreCalClient
|
||||
switch (this.ShipcallControlModel?.Shipcall?.Type)
|
||||
{
|
||||
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:,,,/BreCalTestClient;component/Resources/arrow_down_red.png"));
|
||||
break;
|
||||
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:,,,/BreCalTestClient;component/Resources/arrow_up_blue.png"));
|
||||
break;
|
||||
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:,,,/BreCalTestClient;component/Resources/arrow_right_green.png"));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@ -231,13 +231,13 @@ namespace BreCalClient
|
||||
switch(this.ShipcallControlModel?.LightMode)
|
||||
{
|
||||
case EvaluationType.Green:
|
||||
this.imageEvaluation.Source = new BitmapImage(new Uri("pack://application:,,,/BreCalDevelClient;component/Resources/check.png"));
|
||||
this.imageEvaluation.Source = new BitmapImage(new Uri("pack://application:,,,/BreCalTestClient;component/Resources/check.png"));
|
||||
break;
|
||||
case EvaluationType.Yellow:
|
||||
this.imageEvaluation.Source = new BitmapImage(new Uri("pack://application:,,,/BreCalDevelClient;component/Resources/sign_warning.png"));
|
||||
this.imageEvaluation.Source = new BitmapImage(new Uri("pack://application:,,,/BreCalTestClient;component/Resources/sign_warning.png"));
|
||||
break;
|
||||
case EvaluationType.Red:
|
||||
this.imageEvaluation.Source = new BitmapImage(new Uri("pack://application:,,,/BreCalDevelClient;component/Resources/delete2.png"));
|
||||
this.imageEvaluation.Source = new BitmapImage(new Uri("pack://application:,,,/BreCalTestClient;component/Resources/delete2.png"));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
@ -6,6 +6,7 @@ using BreCalClient.misc.Api;
|
||||
using BreCalClient.misc.Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BreCalClient
|
||||
{
|
||||
@ -38,6 +39,8 @@ namespace BreCalClient
|
||||
|
||||
public string? Berth { get; set; }
|
||||
|
||||
public string LastErrorMessage { get; private set; } = "";
|
||||
|
||||
internal Dictionary<Extensions.ParticipantType, ParticipantAssignment> AssignedParticipants { get; } = new();
|
||||
|
||||
public List<Times> Times { get; set; } = new();
|
||||
@ -247,8 +250,9 @@ namespace BreCalClient
|
||||
/// This function updates the assignments for existing times records accordingly and saves them.
|
||||
/// </summary>
|
||||
/// <param name="_api">API reference to PUT eidted times</param>
|
||||
internal async void UpdateTimesAssignments(TimesApi api)
|
||||
internal async Task<bool> UpdateTimesAssignments(TimesApi api)
|
||||
{
|
||||
|
||||
foreach (Extensions.ParticipantType participantType in this.AssignedParticipants.Keys)
|
||||
{
|
||||
Times? times = this.GetTimesForParticipantType(participantType);
|
||||
@ -256,7 +260,15 @@ namespace BreCalClient
|
||||
if(times.ParticipantId != this.AssignedParticipants[participantType].ParticipantId)
|
||||
{
|
||||
times.ParticipantId = this.AssignedParticipants[participantType].ParticipantId;
|
||||
await api.TimesUpdateAsync(times);
|
||||
try
|
||||
{
|
||||
await api.TimesUpdateAsync(times);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LastErrorMessage = ex.Message;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -283,9 +295,18 @@ namespace BreCalClient
|
||||
|
||||
foreach(Times times in deleteTimes)
|
||||
{
|
||||
api.TimesDelete(times.Id);
|
||||
this.Times.Remove(times);
|
||||
try
|
||||
{
|
||||
api.TimesDelete(times.Id);
|
||||
this.Times.Remove(times);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LastErrorMessage = ex.Message;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
<applicationSettings>
|
||||
<RoleEditor.Properties.Settings>
|
||||
<setting name="ConnectionString" serializeAs="String">
|
||||
<value>Server=localhost;User ID=ds;Password=HalloWach_2323XXL!!;Database=bremen_calling_devel;Port=33306</value>
|
||||
<value>Server=localhost;User ID=ds;Password=HalloWach_2323XXL!!;Database=bremen_calling_test;Port=33306</value>
|
||||
</setting>
|
||||
</RoleEditor.Properties.Settings>
|
||||
</applicationSettings>
|
||||
|
||||
@ -66,7 +66,7 @@ def create_app(test_config=None, instance_path=None):
|
||||
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='brecaltest.log', level=logging.DEBUG, format='%(asctime)s | %(name)s | %(levelname)s | %(message)s')
|
||||
local_db.initPool(os.path.dirname(app.instance_path))
|
||||
logging.info('App started')
|
||||
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
import logging
|
||||
from flask import Blueprint, request
|
||||
from webargs.flaskparser import parser
|
||||
from .. import impl
|
||||
from ..services.auth_guard import auth_guard
|
||||
import json
|
||||
from BreCal.validators.validation_error import create_dynamic_exception_response
|
||||
|
||||
bp = Blueprint('berths', __name__)
|
||||
|
||||
@ -11,8 +13,12 @@ bp = Blueprint('berths', __name__)
|
||||
@auth_guard() # no restriction by role
|
||||
def GetBerths():
|
||||
|
||||
if 'Authorization' in request.headers:
|
||||
token = request.headers.get('Authorization')
|
||||
return impl.berths.GetBerths(token)
|
||||
else:
|
||||
return json.dumps("not authenticated"), 403
|
||||
try:
|
||||
if 'Authorization' in request.headers:
|
||||
token = request.headers.get('Authorization')
|
||||
return impl.berths.GetBerths(token)
|
||||
else:
|
||||
return create_dynamic_exception_response(ex=None, status_code=403, message="not authenticated")
|
||||
|
||||
except Exception as ex:
|
||||
return create_dynamic_exception_response(ex=ex, status_code=400)
|
||||
|
||||
@ -1,21 +1,26 @@
|
||||
import logging
|
||||
from flask import Blueprint, request
|
||||
from .. import impl
|
||||
from ..services.auth_guard import auth_guard
|
||||
import json
|
||||
from BreCal.validators.validation_error import create_dynamic_exception_response
|
||||
|
||||
bp = Blueprint('history', __name__)
|
||||
|
||||
@bp.route('/history', methods=['get'])
|
||||
@auth_guard() # no restriction by role
|
||||
def GetParticipant():
|
||||
def GetHistory():
|
||||
|
||||
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
|
||||
try:
|
||||
if 'Authorization' in request.headers:
|
||||
token = request.headers.get('Authorization')
|
||||
options = {}
|
||||
if not 'shipcall_id' in request.args:
|
||||
return create_dynamic_exception_response(ex=None, status_code=400, message="missing parameter: shipcall_id")
|
||||
options["shipcall_id"] = request.args.get("shipcall_id")
|
||||
return impl.history.GetHistory(options)
|
||||
else:
|
||||
return create_dynamic_exception_response(ex=None, status_code=403, message="not authenticated")
|
||||
|
||||
except Exception as ex:
|
||||
return create_dynamic_exception_response(ex=ex, status_code=400)
|
||||
|
||||
@ -3,6 +3,7 @@ from .. import impl
|
||||
from ..services.auth_guard import auth_guard
|
||||
import logging
|
||||
import json
|
||||
from BreCal.validators.validation_error import create_dynamic_exception_response
|
||||
|
||||
bp = Blueprint('notifications', __name__)
|
||||
|
||||
@ -10,10 +11,13 @@ bp = Blueprint('notifications', __name__)
|
||||
@bp.route('/notifications', methods=['get'])
|
||||
@auth_guard() # no restriction by role
|
||||
def GetNotifications():
|
||||
if 'shipcall_id' in request.args:
|
||||
options = {}
|
||||
options["shipcall_id"] = request.args.get("shipcall_id")
|
||||
return impl.notifications.GetNotifications(options)
|
||||
else:
|
||||
logging.warning("attempt to load notifications without shipcall id")
|
||||
return json.dumps("missing argument"), 400
|
||||
try:
|
||||
if 'shipcall_id' in request.args:
|
||||
options = {}
|
||||
options["shipcall_id"] = request.args.get("shipcall_id")
|
||||
return impl.notifications.GetNotifications(options)
|
||||
else:
|
||||
return create_dynamic_exception_response(ex=None, status_code=400, message="missing argument: shipcall_id")
|
||||
|
||||
except Exception as ex:
|
||||
return create_dynamic_exception_response(ex=ex, status_code=400)
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
import logging
|
||||
from flask import Blueprint, request
|
||||
from .. import impl
|
||||
from ..services.auth_guard import auth_guard
|
||||
import json
|
||||
from BreCal.validators.validation_error import create_dynamic_exception_response
|
||||
|
||||
bp = Blueprint('participants', __name__)
|
||||
|
||||
@ -9,11 +11,14 @@ bp = Blueprint('participants', __name__)
|
||||
@auth_guard() # no restriction by role
|
||||
def GetParticipant():
|
||||
|
||||
if 'Authorization' in request.headers:
|
||||
token = request.headers.get('Authorization')
|
||||
options = {}
|
||||
options["user_id"] = request.args.get("user_id")
|
||||
return impl.participant.GetParticipant(options)
|
||||
else:
|
||||
return json.dumps("not authenticated"), 403
|
||||
try:
|
||||
if 'Authorization' in request.headers:
|
||||
token = request.headers.get('Authorization')
|
||||
options = {}
|
||||
options["user_id"] = request.args.get("user_id")
|
||||
return impl.participant.GetParticipant(options)
|
||||
else:
|
||||
return create_dynamic_exception_response(ex=None, status_code=403, message="not authenticated")
|
||||
|
||||
except Exception as ex:
|
||||
return create_dynamic_exception_response(ex=ex, status_code=400)
|
||||
|
||||
@ -7,7 +7,7 @@ from ..services.auth_guard import auth_guard, check_jwt
|
||||
from BreCal.validators.input_validation import validate_posted_shipcall_data, check_if_user_is_bsmd_type
|
||||
from BreCal.validators.input_validation_shipcall import InputValidationShipcall
|
||||
from BreCal.database.sql_handler import execute_sql_query_standalone
|
||||
from BreCal.validators.validation_error import create_validation_error_response, create_werkzeug_error_response
|
||||
from BreCal.validators.validation_error import create_validation_error_response, create_werkzeug_error_response, create_dynamic_exception_response
|
||||
from . import verify_if_request_is_json
|
||||
|
||||
import logging
|
||||
@ -20,24 +20,28 @@ bp = Blueprint('shipcalls', __name__)
|
||||
@bp.route('/shipcalls', methods=['get'])
|
||||
@auth_guard() # no restriction by role
|
||||
def GetShipcalls():
|
||||
if 'Authorization' in request.headers:
|
||||
token = request.headers.get('Authorization') # see impl/login to see the token encoding, which is a JWT token.
|
||||
try:
|
||||
if 'Authorization' in request.headers:
|
||||
token = request.headers.get('Authorization') # see impl/login to see the token encoding, which is a JWT token.
|
||||
|
||||
"""
|
||||
from BreCal.services.jwt_handler import decode_jwt
|
||||
jwt = token.split('Bearer ')[1] # string key
|
||||
payload = decode_jwt(jwt) # dictionary, which includes 'id' (user id) and 'participant_id'
|
||||
"""
|
||||
from BreCal.services.jwt_handler import decode_jwt
|
||||
jwt = token.split('Bearer ')[1] # string key
|
||||
payload = decode_jwt(jwt) # dictionary, which includes 'id' (user id) and 'participant_id'
|
||||
|
||||
# oneline:
|
||||
payload = decode_jwt(request.headers.get("Authorization").split("Bearer ")[-1])
|
||||
"""
|
||||
options = {}
|
||||
options["participant_id"] = request.args.get("participant_id")
|
||||
options["past_days"] = request.args.get("past_days", default=1, type=int)
|
||||
# oneline:
|
||||
payload = decode_jwt(request.headers.get("Authorization").split("Bearer ")[-1])
|
||||
"""
|
||||
options = {}
|
||||
options["participant_id"] = request.args.get("participant_id")
|
||||
options["past_days"] = request.args.get("past_days", default=1, type=int)
|
||||
|
||||
return impl.shipcalls.GetShipcalls(options)
|
||||
else:
|
||||
return json.dumps("not authenticated"), 403
|
||||
return impl.shipcalls.GetShipcalls(options)
|
||||
else:
|
||||
return create_dynamic_exception_response(ex=None, status_code=403, message="not authenticated")
|
||||
|
||||
except Exception as ex:
|
||||
return create_dynamic_exception_response(ex=ex, status_code=400)
|
||||
|
||||
|
||||
@bp.route('/shipcalls', methods=['post'])
|
||||
@ -55,19 +59,14 @@ def PostShipcalls():
|
||||
|
||||
# validate the posted shipcall data & the user's authority
|
||||
InputValidationShipcall.evaluate_post_data(user_data, loadedModel, content)
|
||||
return impl.shipcalls.PostShipcalls(loadedModel)
|
||||
|
||||
except ValidationError as ex:
|
||||
logging.error(ex)
|
||||
print(ex)
|
||||
return create_validation_error_response(ex=ex, status_code=400)
|
||||
|
||||
except Exception as ex:
|
||||
logging.error(ex)
|
||||
logging.error(traceback.format_exc())
|
||||
print(ex)
|
||||
return json.dumps("bad format"), 400
|
||||
|
||||
return impl.shipcalls.PostShipcalls(loadedModel)
|
||||
return create_dynamic_exception_response(ex=ex, status_code=400, message="bad format")
|
||||
|
||||
|
||||
@bp.route('/shipcalls', methods=['put'])
|
||||
@ -85,20 +84,15 @@ def PutShipcalls():
|
||||
|
||||
# validate the PUT shipcall data and the user's authority
|
||||
InputValidationShipcall.evaluate_put_data(user_data, loadedModel, content)
|
||||
return impl.shipcalls.PutShipcalls(loadedModel)
|
||||
|
||||
except ValidationError as ex:
|
||||
logging.error(ex)
|
||||
print(ex)
|
||||
return create_validation_error_response(ex=ex, status_code=400)
|
||||
|
||||
except werkzeug.exceptions.Forbidden as ex:
|
||||
logging.error(ex)
|
||||
print(ex)
|
||||
return create_werkzeug_error_response(ex=ex, status_code=403)
|
||||
|
||||
except Exception as ex:
|
||||
logging.error(ex)
|
||||
print(ex)
|
||||
return json.dumps("bad format"), 400
|
||||
logging.error(traceback.format_exc())
|
||||
return create_dynamic_exception_response(ex=None, status_code=400, message="bad format")
|
||||
|
||||
return impl.shipcalls.PutShipcalls(loadedModel)
|
||||
|
||||
@ -6,7 +6,7 @@ from ..schemas import model
|
||||
import json
|
||||
import logging
|
||||
from . import verify_if_request_is_json
|
||||
from BreCal.validators.validation_error import create_validation_error_response
|
||||
from BreCal.validators.validation_error import create_validation_error_response, create_dynamic_exception_response
|
||||
|
||||
from BreCal.validators.input_validation import check_if_user_is_bsmd_type
|
||||
from BreCal.validators.input_validation_ship import InputValidationShip
|
||||
@ -17,11 +17,15 @@ bp = Blueprint('ships', __name__)
|
||||
@auth_guard() # no restriction by role
|
||||
def GetShips():
|
||||
|
||||
if 'Authorization' in request.headers:
|
||||
token = request.headers.get('Authorization')
|
||||
return impl.ships.GetShips(token)
|
||||
else:
|
||||
return json.dumps("not authenticated"), 403
|
||||
try:
|
||||
if 'Authorization' in request.headers:
|
||||
token = request.headers.get('Authorization')
|
||||
return impl.ships.GetShips(token)
|
||||
else:
|
||||
return create_dynamic_exception_response(ex=None, status_code=403, message="not authenticated")
|
||||
|
||||
except Exception as ex:
|
||||
return create_dynamic_exception_response(ex=ex, status_code=400)
|
||||
|
||||
|
||||
@bp.route('/ships', methods=['post'])
|
||||
@ -38,25 +42,20 @@ def PostShip():
|
||||
# as ParticipantType is an IntFlag, a user belonging to multiple groups is properly evaluated.
|
||||
is_bsmd = check_if_user_is_bsmd_type(user_data)
|
||||
if not is_bsmd:
|
||||
raise ValidationError(f"current user does not belong to BSMD. Cannot post shipcalls. Found user data: {user_data}")
|
||||
raise ValidationError({"participant_type":f"current user does not belong to BSMD. Cannot post shipcalls. Found user data: {user_data}"})
|
||||
|
||||
content = request.get_json(force=True)
|
||||
loadedModel = model.ShipSchema().load(data=content, many=False, partial=True)
|
||||
|
||||
# validate the request data & user permissions
|
||||
InputValidationShip.evaluate_post_data(user_data, loadedModel, content)
|
||||
return impl.ships.PostShip(loadedModel)
|
||||
|
||||
except ValidationError as ex:
|
||||
logging.error(ex)
|
||||
print(ex)
|
||||
return create_validation_error_response(ex=ex, status_code=400)
|
||||
|
||||
except Exception as ex:
|
||||
logging.error(ex)
|
||||
print(ex)
|
||||
return json.dumps(repr(ex)), 400
|
||||
|
||||
return impl.ships.PostShip(loadedModel)
|
||||
return create_dynamic_exception_response(ex=ex, status_code=400, message=None)
|
||||
|
||||
|
||||
@bp.route('/ships', methods=['put'])
|
||||
@ -74,18 +73,13 @@ def PutShip():
|
||||
|
||||
# validate the request data & user permissions
|
||||
InputValidationShip.evaluate_put_data(user_data, loadedModel, content)
|
||||
return impl.ships.PutShip(loadedModel)
|
||||
|
||||
except ValidationError as ex:
|
||||
logging.error(ex)
|
||||
print(ex)
|
||||
return create_validation_error_response(ex=ex, status_code=400)
|
||||
|
||||
except Exception as ex:
|
||||
logging.error(ex)
|
||||
print(ex)
|
||||
return json.dumps(repr(ex)), 400
|
||||
|
||||
return impl.ships.PutShip(loadedModel)
|
||||
return create_dynamic_exception_response(ex=ex, status_code=400)
|
||||
|
||||
|
||||
@bp.route('/ships', methods=['delete'])
|
||||
@ -97,25 +91,21 @@ def DeleteShip():
|
||||
|
||||
# read the user data from the JWT token (set when login is performed)
|
||||
user_data = check_jwt()
|
||||
ship_id = request.args.get("id")
|
||||
|
||||
if 'id' in request.args:
|
||||
options = {}
|
||||
options["id"] = request.args.get("id")
|
||||
else:
|
||||
return json.dumps("no id provided"), 400
|
||||
return create_dynamic_exception_response(ex=None, status_code=400, message="no id provided")
|
||||
|
||||
# validate the request data & user permissions
|
||||
ship_id = request.args.get("id")
|
||||
InputValidationShip.evaluate_delete_data(user_data, ship_id)
|
||||
return impl.ships.DeleteShip(options)
|
||||
|
||||
except ValidationError as ex:
|
||||
logging.error(ex)
|
||||
print(ex)
|
||||
return create_validation_error_response(ex=ex, status_code=400)
|
||||
|
||||
except Exception as ex:
|
||||
logging.error(ex)
|
||||
print(ex)
|
||||
return json.dumps(repr(ex)), 400
|
||||
return create_dynamic_exception_response(ex=ex, status_code=400)
|
||||
|
||||
return impl.ships.DeleteShip(options)
|
||||
|
||||
@ -7,7 +7,7 @@ import logging
|
||||
from marshmallow import ValidationError
|
||||
from BreCal.validators.input_validation_times import InputValidationTimes
|
||||
from . import verify_if_request_is_json
|
||||
from BreCal.validators.validation_error import create_validation_error_response, create_werkzeug_error_response
|
||||
from BreCal.validators.validation_error import create_validation_error_response, create_werkzeug_error_response, create_dynamic_exception_response
|
||||
|
||||
bp = Blueprint('times', __name__)
|
||||
|
||||
@ -16,10 +16,13 @@ bp = Blueprint('times', __name__)
|
||||
@auth_guard() # no restriction by role
|
||||
def GetTimes():
|
||||
|
||||
options = {}
|
||||
options["shipcall_id"] = request.args.get("shipcall_id")
|
||||
try:
|
||||
options = {}
|
||||
options["shipcall_id"] = request.args.get("shipcall_id")
|
||||
return impl.times.GetTimes(options)
|
||||
|
||||
return impl.times.GetTimes(options)
|
||||
except Exception as ex:
|
||||
return create_dynamic_exception_response(ex=ex, status_code=400)
|
||||
|
||||
|
||||
@bp.route('/times', methods=['post'])
|
||||
@ -41,18 +44,14 @@ def PostTimes():
|
||||
|
||||
# validate the request
|
||||
InputValidationTimes.evaluate_post_data(user_data, loadedModel, content)
|
||||
return impl.times.PostTimes(loadedModel)
|
||||
|
||||
except ValidationError as ex:
|
||||
logging.error(ex)
|
||||
print(ex)
|
||||
return create_validation_error_response(ex=ex, status_code=400)
|
||||
|
||||
except Exception as ex:
|
||||
logging.error(ex)
|
||||
print(ex)
|
||||
return json.dumps("bad format"), 400
|
||||
return create_dynamic_exception_response(ex=ex, status_code=400, message="bad format")
|
||||
|
||||
return impl.times.PostTimes(loadedModel)
|
||||
|
||||
|
||||
@bp.route('/times', methods=['put'])
|
||||
@ -70,18 +69,14 @@ def PutTimes():
|
||||
|
||||
# validate the request
|
||||
InputValidationTimes.evaluate_put_data(user_data, loadedModel, content)
|
||||
return impl.times.PutTimes(loadedModel)
|
||||
|
||||
except ValidationError as ex:
|
||||
logging.error(ex)
|
||||
print(ex)
|
||||
return create_validation_error_response(ex=ex, status_code=400)
|
||||
|
||||
except Exception as ex:
|
||||
logging.error(ex)
|
||||
print(ex)
|
||||
return json.dumps("bad format"), 400
|
||||
return create_dynamic_exception_response(ex=ex, status_code=400, message="bad format")
|
||||
|
||||
return impl.times.PutTimes(loadedModel)
|
||||
|
||||
|
||||
@bp.route('/times', methods=['delete'])
|
||||
@ -101,15 +96,10 @@ def DeleteTimes():
|
||||
|
||||
return impl.times.DeleteTimes(options)
|
||||
else:
|
||||
logging.warning("Times delete missing id argument")
|
||||
return json.dumps("missing argument"), 400
|
||||
return create_dynamic_exception_response(ex=None, status_code=400, message="Times delete missing argument: id")
|
||||
|
||||
except ValidationError as ex:
|
||||
logging.error(ex)
|
||||
print(ex)
|
||||
return create_validation_error_response(ex=ex, status_code=400)
|
||||
|
||||
except Exception as ex:
|
||||
logging.error(ex)
|
||||
print(ex)
|
||||
return json.dumps("bad format"), 400
|
||||
return create_dynamic_exception_response(ex=ex, status_code=400, message="bad format")
|
||||
|
||||
@ -6,6 +6,7 @@ import json
|
||||
import logging
|
||||
from marshmallow import ValidationError
|
||||
from . import verify_if_request_is_json
|
||||
from BreCal.validators.validation_error import create_dynamic_exception_response, create_validation_error_response
|
||||
|
||||
bp = Blueprint('user', __name__)
|
||||
|
||||
@ -18,16 +19,12 @@ def PutUser():
|
||||
|
||||
content = request.get_json(force=True)
|
||||
loadedModel = model.UserSchema().load(data=content, many=False, partial=True)
|
||||
return impl.user.PutUser(loadedModel)
|
||||
|
||||
except ValidationError as ex:
|
||||
logging.error(ex)
|
||||
print(ex)
|
||||
return json.dumps(f"bad format. \nError Messages: {ex.messages}. \nValid Data: {ex.valid_data}"), 400
|
||||
return create_validation_error_response(ex=ex, status_code=400)
|
||||
|
||||
except Exception as ex:
|
||||
logging.error(ex)
|
||||
print(ex)
|
||||
return json.dumps("bad format"), 400
|
||||
return create_dynamic_exception_response(ex=None, status_code=400, message="bad format")
|
||||
|
||||
return impl.user.PutUser(loadedModel)
|
||||
|
||||
|
||||
@ -266,6 +266,11 @@ class SQLQuery():
|
||||
query = "SELECT id, name, imo, callsign, participant_id, length, width, is_tug, bollard_pull, eni, created, modified, deleted FROM ship ORDER BY name"
|
||||
return query
|
||||
|
||||
@staticmethod
|
||||
def get_ship_by_id()->str:
|
||||
query = "SELECT * FROM ship where id = ?id?"
|
||||
return query
|
||||
|
||||
@staticmethod
|
||||
def get_times()->str:
|
||||
query = "SELECT id, eta_berth, eta_berth_fixed, etd_berth, etd_berth_fixed, lock_time, lock_time_fixed, " + \
|
||||
|
||||
@ -10,6 +10,7 @@ from ..services.auth_guard import check_jwt
|
||||
from BreCal.database.update_database import evaluate_shipcall_state
|
||||
from BreCal.database.sql_queries import create_sql_query_shipcall_get, create_sql_query_shipcall_post, create_sql_query_shipcall_put, create_sql_query_history_post, create_sql_query_history_put, SQLQuery
|
||||
from marshmallow import Schema, fields, ValidationError
|
||||
from BreCal.validators.validation_error import create_validation_error_response
|
||||
|
||||
def GetShipcalls(options):
|
||||
"""
|
||||
@ -152,9 +153,7 @@ def PostShipcalls(schemaModel):
|
||||
return json.dumps({"id" : new_id}), 201, {'Content-Type': 'application/json; charset=utf-8'}
|
||||
|
||||
except ValidationError as ex:
|
||||
logging.error(ex)
|
||||
print(ex)
|
||||
return json.dumps({"message":f"bad format. \nError Messages: {ex.messages}. \nValid Data: {ex.valid_data}"}), 400
|
||||
return create_validation_error_response(ex, status_code=400, create_log=True)
|
||||
|
||||
except Exception as ex:
|
||||
logging.error(traceback.format_exc())
|
||||
@ -265,9 +264,7 @@ def PutShipcalls(schemaModel):
|
||||
return json.dumps({"id" : schemaModel["id"]}), 200
|
||||
|
||||
except ValidationError as ex:
|
||||
logging.error(ex)
|
||||
print(ex)
|
||||
return json.dumps({"message":f"bad format. \nError Messages: {ex.messages}. \nValid Data: {ex.valid_data}"}), 400
|
||||
return create_validation_error_response(ex, status_code=400, create_log=True)
|
||||
|
||||
except Exception as ex:
|
||||
logging.error(traceback.format_exc())
|
||||
|
||||
@ -7,11 +7,11 @@ import sys
|
||||
|
||||
config_path = None
|
||||
|
||||
def initPool(instancePath, connection_filename="connection_data_devel.json"):
|
||||
def initPool(instancePath, connection_filename="connection_data_test.json"):
|
||||
try:
|
||||
global config_path
|
||||
if(config_path == None):
|
||||
config_path = os.path.join(instancePath,f'../../../secure/{connection_filename}') #connection_data_devel.json');
|
||||
config_path = os.path.join(instancePath,f'../../../secure/{connection_filename}') #connection_data_test.json');
|
||||
|
||||
print (config_path)
|
||||
|
||||
|
||||
@ -177,7 +177,7 @@ class Participant(Schema):
|
||||
|
||||
valid_type = 0 <= value < max_int
|
||||
if not valid_type:
|
||||
raise ValidationError(f"the provided integer is not supported for default behaviour of the ParticipantType IntFlag. Your choice: {value}. Supported values are: 0 <= value {max_int}")
|
||||
raise ValidationError({"type":f"the provided integer is not supported for default behaviour of the ParticipantType IntFlag. Your choice: {value}. Supported values are: 0 <= value {max_int}"})
|
||||
|
||||
|
||||
@validates("flags")
|
||||
@ -188,7 +188,7 @@ class Participant(Schema):
|
||||
|
||||
valid_type = 0 <= value < max_int
|
||||
if not valid_type:
|
||||
raise ValidationError(f"the provided integer is not supported for default behaviour of the ParticipantFlag IntFlag. Your choice: {value}. Supported values are: 0 <= value {max_int}")
|
||||
raise ValidationError({"flags":f"the provided integer is not supported for default behaviour of the ParticipantFlag IntFlag. Your choice: {value}. Supported values are: 0 <= value {max_int}"})
|
||||
|
||||
|
||||
class ParticipantList(Participant):
|
||||
@ -253,7 +253,7 @@ class ShipcallSchema(Schema):
|
||||
valid_shipcall_type = int(value) in [item.value for item in ShipcallType]
|
||||
|
||||
if not valid_shipcall_type:
|
||||
raise ValidationError(f"the provided type is not a valid shipcall type.")
|
||||
raise ValidationError({"type":f"the provided type is not a valid shipcall type."})
|
||||
|
||||
|
||||
@dataclass
|
||||
@ -393,7 +393,7 @@ class TimesSchema(Schema):
|
||||
value = ParticipantType(value)
|
||||
|
||||
if ParticipantType.BSMD in value:
|
||||
raise ValidationError(f"the participant_type must not be .BSMD")
|
||||
raise ValidationError({"participant_type":f"the participant_type must not be .BSMD"})
|
||||
|
||||
@validates("eta_berth")
|
||||
def validate_eta_berth(self, value):
|
||||
@ -437,6 +437,21 @@ class TimesSchema(Schema):
|
||||
valid_time = validate_time_is_in_not_too_distant_future(raise_validation_error=True, value=value, months=12)
|
||||
return
|
||||
|
||||
@validates("eta_interval_end")
|
||||
def validate_eta_interval_end(self, value):
|
||||
# violation when time is not in the future, but also does not exceed a threshold for the 'reasonable' future
|
||||
# when 'value' is 'None', a ValidationError is not issued.
|
||||
valid_time = validate_time_is_in_not_too_distant_future(raise_validation_error=True, value=value, months=12)
|
||||
return
|
||||
|
||||
@validates("etd_interval_end")
|
||||
def validate_etd_interval_end(self, value):
|
||||
# violation when time is not in the future, but also does not exceed a threshold for the 'reasonable' future
|
||||
# when 'value' is 'None', a ValidationError is not issued.
|
||||
valid_time = validate_time_is_in_not_too_distant_future(raise_validation_error=True, value=value, months=12)
|
||||
return
|
||||
|
||||
|
||||
# deserialize PUT object target
|
||||
|
||||
class UserSchema(Schema):
|
||||
@ -456,12 +471,12 @@ class UserSchema(Schema):
|
||||
def validate_user_phone(self, value):
|
||||
valid_characters = list(map(str,range(0,10)))+["+", " "]
|
||||
if not all([v in valid_characters for v in value]):
|
||||
raise ValidationError(f"one of the phone number values is not valid.")
|
||||
raise ValidationError({"user_phone":f"one of the phone number values is not valid."})
|
||||
|
||||
@validates("user_email")
|
||||
def validate_user_email(self, value):
|
||||
if not "@" in value:
|
||||
raise ValidationError(f"invalid email address")
|
||||
raise ValidationError({"user_email":f"invalid email address"})
|
||||
|
||||
|
||||
@dataclass
|
||||
@ -550,12 +565,12 @@ class ShipSchema(Schema):
|
||||
def validate_name(self, value):
|
||||
character_length = len(str(value))
|
||||
if character_length<1:
|
||||
raise ValidationError(f"'name' argument should have at least one character")
|
||||
raise ValidationError({"name":f"'name' argument should have at least one character"})
|
||||
elif character_length>=64:
|
||||
raise ValidationError(f"'name' argument should have at max. 63 characters")
|
||||
raise ValidationError({"name":f"'name' argument should have at max. 63 characters"})
|
||||
|
||||
if check_if_string_has_special_characters(value):
|
||||
raise ValidationError(f"'name' argument should not have special characters.")
|
||||
raise ValidationError({"name":f"'name' argument should not have special characters."})
|
||||
return
|
||||
|
||||
@validates("imo")
|
||||
@ -563,7 +578,7 @@ class ShipSchema(Schema):
|
||||
value = str(value).zfill(7) # 1 becomes '0000001' (7 characters). 12345678 becomes '12345678' (8 characters)
|
||||
imo_length = len(value)
|
||||
if imo_length != 7:
|
||||
raise ValidationError(f"'imo' should be a 7-digit number")
|
||||
raise ValidationError({"imo":f"'imo' should be a 7-digit number"})
|
||||
return
|
||||
|
||||
@validates("callsign")
|
||||
@ -571,10 +586,10 @@ class ShipSchema(Schema):
|
||||
if value is not None:
|
||||
callsign_length = len(str(value))
|
||||
if callsign_length>8:
|
||||
raise ValidationError(f"'callsign' argument should not have more than 8 characters")
|
||||
raise ValidationError({"callsign":f"'callsign' argument should not have more than 8 characters"})
|
||||
|
||||
if check_if_string_has_special_characters(value):
|
||||
raise ValidationError(f"'callsign' argument should not have special characters.")
|
||||
raise ValidationError({"callsign":f"'callsign' argument should not have special characters."})
|
||||
return
|
||||
|
||||
|
||||
|
||||
12
src/server/BreCal/stubs/times.py
Normal file
12
src/server/BreCal/stubs/times.py
Normal file
@ -0,0 +1,12 @@
|
||||
import datetime
|
||||
from BreCal.schemas import model
|
||||
from BreCal.schemas.model import ParticipantType
|
||||
|
||||
def get_schema_model_stub_departure():
|
||||
schemaModel = {'id': 0, 'eta_berth': None, 'eta_berth_fixed': None, 'etd_berth': datetime.datetime(2024, 9, 7, 15, 12, 58), 'etd_berth_fixed': None, 'lock_time': None, 'lock_time_fixed': None, 'zone_entry': None, 'zone_entry_fixed': None, 'operations_start': None, 'operations_end': None, 'remarks': 'test', 'participant_id': 10, 'berth_id': 146, 'berth_info': '', 'pier_side': None, 'shipcall_id': 115, 'participant_type': ParticipantType.AGENCY, 'ata': None, 'atd': None, 'eta_interval_end': None, 'etd_interval_end': None, 'created': None, 'modified': None}
|
||||
return schemaModel
|
||||
|
||||
def get_schema_model_stub_arrival():
|
||||
schemaModel = {'id': 0, 'eta_berth': datetime.datetime(2024, 9, 7, 15, 12, 58), 'eta_berth_fixed': None, 'etd_berth': None, 'etd_berth_fixed': None, 'lock_time': None, 'lock_time_fixed': None, 'zone_entry': None, 'zone_entry_fixed': None, 'operations_start': None, 'operations_end': None, 'remarks': 'test', 'participant_id': 10, 'berth_id': 146, 'berth_info': '', 'pier_side': None, 'shipcall_id': 115, 'participant_type': ParticipantType.AGENCY, 'ata': None, 'atd': None, 'eta_interval_end': None, 'etd_interval_end': None, 'created': None, 'modified': None}
|
||||
return schemaModel
|
||||
|
||||
@ -35,24 +35,24 @@ def validate_posted_shipcall_data(user_data:dict, loadedModel:dict, content:dict
|
||||
# as ParticipantType is an IntFlag, a user belonging to multiple groups is properly evaluated.
|
||||
is_bsmd = check_if_user_is_bsmd_type(user_data)
|
||||
if not is_bsmd:
|
||||
raise ValidationError(f"current user does not belong to BSMD. Cannot post shipcalls. Found user data: {user_data}")
|
||||
raise ValidationError({"user_participant_type":f"current user does not belong to BSMD. Cannot post shipcalls. Found user data: {user_data}"})
|
||||
|
||||
##### Section 2: check loadedModel #####
|
||||
valid_ship_id = check_if_ship_id_is_valid(ship_id=loadedModel.get("ship_id", None))
|
||||
if not valid_ship_id:
|
||||
raise ValidationError(f"provided an invalid ship id, which is not found in the database: {loadedModel.get('ship_id', None)}")
|
||||
raise ValidationError({"ship_id":f"provided an invalid ship id, which is not found in the database: {loadedModel.get('ship_id', None)}"})
|
||||
|
||||
valid_arrival_berth_id = check_if_berth_id_is_valid(berth_id=loadedModel.get("arrival_berth_id", None))
|
||||
if not valid_arrival_berth_id:
|
||||
raise ValidationError(f"provided an invalid arrival berth id, which is not found in the database: {loadedModel.get('arrival_berth_id', None)}")
|
||||
raise ValidationError({"arrival_berth_id":f"provided an invalid arrival berth id, which is not found in the database: {loadedModel.get('arrival_berth_id', None)}"})
|
||||
|
||||
valid_departure_berth_id = check_if_berth_id_is_valid(berth_id=loadedModel.get("departure_berth_id", None))
|
||||
if not valid_departure_berth_id:
|
||||
raise ValidationError(f"provided an invalid departure berth id, which is not found in the database: {loadedModel.get('departure_berth_id', None)}")
|
||||
raise ValidationError({"departure_berth_id":f"provided an invalid departure berth id, which is not found in the database: {loadedModel.get('departure_berth_id', None)}"})
|
||||
|
||||
valid_participant_ids = check_if_participant_ids_are_valid(participants=loadedModel.get("participants",[]))
|
||||
if not valid_participant_ids:
|
||||
raise ValidationError(f"one of the provided participant ids is invalid. Could not find one of these in the database: {loadedModel.get('participants', None)}")
|
||||
raise ValidationError({"participants":f"one of the provided participant ids is invalid. Could not find one of these in the database: {loadedModel.get('participants', None)}"})
|
||||
|
||||
|
||||
##### Section 3: check content #####
|
||||
@ -62,11 +62,11 @@ def validate_posted_shipcall_data(user_data:dict, loadedModel:dict, content:dict
|
||||
for forbidden_key in ["canceled", "evaluation", "evaluation_message"]:
|
||||
value = content.get(forbidden_key, None)
|
||||
if value is not None:
|
||||
raise ValidationError(f"'{forbidden_key}' may not be set on POST. Found: {value}")
|
||||
raise ValidationError({"forbidden_key":f"'{forbidden_key}' may not be set on POST. Found: {value}"})
|
||||
|
||||
voyage_str_is_invalid = check_if_string_has_special_characters(text=content.get("voyage",""))
|
||||
if voyage_str_is_invalid:
|
||||
raise ValidationError(f"there are invalid characters in the 'voyage'-string. Please use only digits and ASCII letters. Allowed: {ascii_letters+digits}. Found: {content.get('voyage')}")
|
||||
raise ValidationError({"voyage":f"there are invalid characters in the 'voyage'-string. Please use only digits and ASCII letters. Allowed: {ascii_letters+digits}. Found: {content.get('voyage')}"})
|
||||
|
||||
|
||||
##### Section 4: check loadedModel & content #####
|
||||
@ -77,43 +77,43 @@ def validate_posted_shipcall_data(user_data:dict, loadedModel:dict, content:dict
|
||||
time_now = datetime.datetime.now()
|
||||
type_ = loadedModel.get("type", int(ShipcallType.undefined))
|
||||
if int(type_)==int(ShipcallType.undefined):
|
||||
raise ValidationError(f"providing 'type' is mandatory. Missing key!")
|
||||
raise ValidationError({"type":f"providing 'type' is mandatory. Missing key!"})
|
||||
elif int(type_)==int(ShipcallType.arrival):
|
||||
eta = loadedModel.get("eta")
|
||||
if (content.get("eta", None) is None):
|
||||
raise ValidationError(f"providing 'eta' is mandatory. Missing key!")
|
||||
raise ValidationError({"eta":f"providing 'eta' is mandatory. Missing key!"})
|
||||
if content.get("arrival_berth_id", None) is None:
|
||||
raise ValidationError(f"providing 'arrival_berth_id' is mandatory. Missing key!")
|
||||
raise ValidationError({"arrival_berth_id":f"providing 'arrival_berth_id' is mandatory. Missing key!"})
|
||||
if not eta >= time_now:
|
||||
raise ValidationError(f"'eta' must be in the future. Incorrect datetime provided.")
|
||||
raise ValidationError({"eta":f"'eta' must be in the future. Incorrect datetime provided."})
|
||||
elif int(type_)==int(ShipcallType.departure):
|
||||
etd = loadedModel.get("etd")
|
||||
if (content.get("etd", None) is None):
|
||||
raise ValidationError(f"providing 'etd' is mandatory. Missing key!")
|
||||
raise ValidationError({"etd":f"providing 'etd' is mandatory. Missing key!"})
|
||||
if content.get("departure_berth_id", None) is None:
|
||||
raise ValidationError(f"providing 'departure_berth_id' is mandatory. Missing key!")
|
||||
raise ValidationError({"departure_berth_id":f"providing 'departure_berth_id' is mandatory. Missing key!"})
|
||||
if not etd >= time_now:
|
||||
raise ValidationError(f"'etd' must be in the future. Incorrect datetime provided.")
|
||||
raise ValidationError({"etd":f"'etd' must be in the future. Incorrect datetime provided."})
|
||||
elif int(type_)==int(ShipcallType.shifting):
|
||||
eta = loadedModel.get("eta")
|
||||
etd = loadedModel.get("etd")
|
||||
# * arrival_berth_id / departure_berth_id (depending on type, see above)
|
||||
if (content.get("eta", None) is None) or (content.get("etd", None) is None):
|
||||
raise ValidationError(f"providing 'eta' and 'etd' is mandatory. Missing one of those keys!")
|
||||
raise ValidationError({"eta_or_etd":f"providing 'eta' and 'etd' is mandatory. Missing one of those keys!"})
|
||||
if (content.get("arrival_berth_id", None) is None) or (content.get("departure_berth_id", None) is None):
|
||||
raise ValidationError(f"providing 'arrival_berth_id' & 'departure_berth_id' is mandatory. Missing key!")
|
||||
raise ValidationError({"arrival_berth_id_or_departure_berth_id":f"providing 'arrival_berth_id' & 'departure_berth_id' is mandatory. Missing key!"})
|
||||
if (not eta >= time_now) or (not etd >= time_now) or (not eta >= etd):
|
||||
raise ValidationError(f"'eta' and 'etd' must be in the future. Incorrect datetime provided.")
|
||||
raise ValidationError({"eta_or_etd":f"'eta' and 'etd' must be in the future. Incorrect datetime provided."})
|
||||
|
||||
tidal_window_from = loadedModel.get("tidal_window_from", None)
|
||||
tidal_window_to = loadedModel.get("tidal_window_to", None)
|
||||
if tidal_window_to is not None:
|
||||
if not tidal_window_to >= time_now:
|
||||
raise ValidationError(f"'tidal_window_to' must be in the future. Incorrect datetime provided.")
|
||||
raise ValidationError({"tidal_window_to":f"'tidal_window_to' must be in the future. Incorrect datetime provided."})
|
||||
|
||||
if tidal_window_from is not None:
|
||||
if not tidal_window_from >= time_now:
|
||||
raise ValidationError(f"'tidal_window_from' must be in the future. Incorrect datetime provided.")
|
||||
raise ValidationError({"tidal_window_from":f"'tidal_window_from' must be in the future. Incorrect datetime provided."})
|
||||
|
||||
# #TODO: assert tidal_window_from > tidal_window_to
|
||||
|
||||
|
||||
@ -6,6 +6,8 @@ from marshmallow import ValidationError
|
||||
from string import ascii_letters, digits
|
||||
|
||||
from BreCal.schemas.model import Ship, Shipcall, Berth, User, Participant, ShipcallType
|
||||
from BreCal.database.sql_handler import execute_sql_query_standalone
|
||||
from BreCal.database.sql_queries import SQLQuery
|
||||
from BreCal.impl.participant import GetParticipant
|
||||
from BreCal.impl.ships import GetShips
|
||||
from BreCal.impl.berths import GetBerths
|
||||
@ -47,18 +49,22 @@ class InputValidationShip():
|
||||
# 1.) Only users of type BSMD are allowed to PUT
|
||||
InputValidationShip.check_user_is_bsmd_type(user_data)
|
||||
|
||||
# 2.) The IMO number field may not be changed
|
||||
# 2.) ID field is mandatory
|
||||
InputValidationShip.content_contains_ship_id(content)
|
||||
|
||||
# 3.) The IMO number field may not be changed
|
||||
InputValidationShip.put_content_may_not_contain_imo_number(content)
|
||||
|
||||
# 3.) Check for reasonable Values (see BreCal.schemas.model.ShipSchema)
|
||||
# 4.) Check for reasonable Values (see BreCal.schemas.model.ShipSchema)
|
||||
InputValidationShip.optionally_evaluate_bollard_pull_value(content)
|
||||
|
||||
# 4.) ID field is mandatory
|
||||
InputValidationShip.content_contains_ship_id(content)
|
||||
return
|
||||
|
||||
@staticmethod
|
||||
def evaluate_delete_data(user_data:dict, ship_id:int):
|
||||
def evaluate_delete_data(user_data:dict, ship_id:typing.Optional[int]):
|
||||
if ship_id is None:
|
||||
raise ValidationError({"id":f"The ship id must be provided."})
|
||||
ship_id = int(ship_id)
|
||||
|
||||
# 1.) Only users of type BSMD are allowed to PUT
|
||||
InputValidationShip.check_user_is_bsmd_type(user_data)
|
||||
|
||||
@ -73,16 +79,16 @@ class InputValidationShip():
|
||||
|
||||
if bollard_pull is not None:
|
||||
if not is_tug:
|
||||
raise ValidationError(f"'bollard_pull' is only allowed, when a ship is a tug ('is_tug').")
|
||||
raise ValidationError({"bollard_pull":f"'bollard_pull' is only allowed, when a ship is a tug ('is_tug')."})
|
||||
|
||||
if (not (0 < bollard_pull < 500)) & (is_tug):
|
||||
raise ValidationError(f"when a ship is a tug, the bollard pull must be 0 < value < 500. ")
|
||||
raise ValidationError({"bollard_pull":f"when a ship is a tug, the bollard pull must be 0 < value < 500. "})
|
||||
|
||||
@staticmethod
|
||||
def check_user_is_bsmd_type(user_data:dict):
|
||||
is_bsmd = check_if_user_is_bsmd_type(user_data)
|
||||
if not is_bsmd:
|
||||
raise ValidationError(f"current user does not belong to BSMD. Cannot post, put or delete ships. Found user data: {user_data}")
|
||||
raise ValidationError({"participant_type":f"current user does not belong to BSMD. Cannot post, put or delete ships. Found user data: {user_data}"})
|
||||
|
||||
@staticmethod
|
||||
def check_ship_imo_already_exists(loadedModel:dict):
|
||||
@ -96,47 +102,48 @@ class InputValidationShip():
|
||||
# check, if the imo in the POST-request already exists in the list
|
||||
imo_already_exists = loadedModel.get("imo") in ship_imos
|
||||
if imo_already_exists:
|
||||
raise ValidationError(f"the provided ship IMO {loadedModel.get('imo')} already exists. A ship may only be added, if there is no other ship with the same IMO number.")
|
||||
raise ValidationError({"imo":f"the provided ship IMO {loadedModel.get('imo')} already exists. A ship may only be added, if there is no other ship with the same IMO number."})
|
||||
return
|
||||
|
||||
@staticmethod
|
||||
def put_content_may_not_contain_imo_number(content:dict):
|
||||
# IMO is a required field, so it will never be None outside of tests. If so, there is no violation
|
||||
put_data_ship_imo = content.get("imo",None)
|
||||
if put_data_ship_imo is None:
|
||||
return
|
||||
|
||||
# #TODO: Aktuelle IMO abfragen und nach Änderung suchen, bevor eine Fehlermeldung erstellt wird
|
||||
# grab the ship by its ID and compare, whether the IMO is unchanged
|
||||
ship = execute_sql_query_standalone(SQLQuery.get_ship_by_id(), param={"id":content.get("id")}, command_type="single", model=Ship)
|
||||
|
||||
if put_data_ship_imo is not None:
|
||||
raise ValidationError(f"The IMO number field may not be changed since it serves the purpose of a primary (matching) key.")
|
||||
if put_data_ship_imo != ship.imo:
|
||||
raise ValidationError({"imo":f"The IMO number field may not be changed since it serves the purpose of a primary (matching) key."})
|
||||
return
|
||||
|
||||
@staticmethod
|
||||
def content_contains_ship_id(content:dict):
|
||||
put_data_ship_id = content.get('id',None)
|
||||
if put_data_ship_id is None:
|
||||
raise ValidationError(f"The id field is required.")
|
||||
raise ValidationError({"id":f"The id field is required."})
|
||||
return
|
||||
|
||||
@staticmethod
|
||||
def check_if_entry_is_already_deleted(ship_id:int):
|
||||
def check_if_entry_is_already_deleted(ship_id:typing.Optional[int]):
|
||||
"""
|
||||
When calling a delete request for ships, the dataset may not be deleted already. This method
|
||||
makes sure, that the request contains and ID, has a matching entry in the database, and the
|
||||
database entry may not have a deletion state already.
|
||||
"""
|
||||
if ship_id is None:
|
||||
raise ValidationError(f"The ship_id must be provided.")
|
||||
|
||||
response, status_code, header = GetShips(token=None)
|
||||
ships = json.loads(response)
|
||||
existing_database_entries = [ship for ship in ships if ship.get("id")==ship_id]
|
||||
existing_database_entries = [ship for ship in ships if ship.get("id")==int(ship_id)]
|
||||
if len(existing_database_entries)==0:
|
||||
raise ValidationError(f"Could not find a ship with the specified ID. Selected: {ship_id}")
|
||||
raise ValidationError({"id":f"Could not find a ship with the specified ID. Selected: {ship_id}"})
|
||||
|
||||
existing_database_entry = existing_database_entries[0]
|
||||
|
||||
deletion_state = existing_database_entry.get("deleted",None)
|
||||
if deletion_state:
|
||||
raise ValidationError(f"The selected ship entry is already deleted.")
|
||||
raise ValidationError({"deleted":f"The selected ship entry is already deleted."})
|
||||
return
|
||||
|
||||
|
||||
|
||||
@ -90,7 +90,7 @@ class InputValidationShipcall():
|
||||
InputValidationShipcall.check_referenced_ids(loadedModel)
|
||||
|
||||
# check for reasonable values in the shipcall fields and checks for forbidden keys.
|
||||
InputValidationShipcall.check_shipcall_values(loadedModel, content, forbidden_keys=["evaluation", "evaluation_message"])
|
||||
InputValidationShipcall.check_shipcall_values(loadedModel, content, forbidden_keys=["evaluation", "evaluation_message"], is_put_data=True)
|
||||
|
||||
# a canceled shipcall cannot be selected
|
||||
# Note: 'canceled' is allowed in PUT-requests, if it is not already set (which is checked by InputValidationShipcall.check_shipcall_is_cancel)
|
||||
@ -98,12 +98,15 @@ class InputValidationShipcall():
|
||||
return
|
||||
|
||||
@staticmethod
|
||||
def check_shipcall_values(loadedModel:dict, content:dict, forbidden_keys:list=["evaluation", "evaluation_message"]):
|
||||
def check_shipcall_values(loadedModel:dict, content:dict, forbidden_keys:list=["evaluation", "evaluation_message"], is_put_data:bool=False):
|
||||
"""
|
||||
individually checks each value provided in the loadedModel/content.
|
||||
This function validates, whether the values are reasonable.
|
||||
|
||||
Also, some data may not be set in a POST-request.
|
||||
|
||||
options:
|
||||
is_put_data: bool. Some validation rules do not apply to POST data, but apply to PUT data. This flag separates the two.
|
||||
"""
|
||||
# Note: BreCal.schemas.model.ShipcallSchema has an internal validation, which the marshmallow library provides. This is used
|
||||
# to verify values individually, when the schema is loaded with data.
|
||||
@ -113,15 +116,19 @@ class InputValidationShipcall():
|
||||
# voyage shall not contain special characters
|
||||
voyage_str_is_invalid = check_if_string_has_special_characters(text=content.get("voyage",""))
|
||||
if voyage_str_is_invalid:
|
||||
raise ValidationError(f"there are invalid characters in the 'voyage'-string. Please use only digits and ASCII letters. Allowed: {ascii_letters+digits}. Found: {content.get('voyage')}")
|
||||
raise ValidationError({"voyage":f"there are invalid characters in the 'voyage'-string. Please use only digits and ASCII letters. Allowed: {ascii_letters+digits}. Found: {content.get('voyage')}"})
|
||||
|
||||
# the 'flags' integer must be valid
|
||||
flags_value = content.get("flags", 0)
|
||||
if check_if_int_is_valid_flag(flags_value, enum_object=ParticipantFlag):
|
||||
raise ValidationError(f"incorrect value provided for 'flags'. Must be a valid combination of the flags.")
|
||||
raise ValidationError({"flags":f"incorrect value provided for 'flags'. Must be a valid combination of the flags."})
|
||||
|
||||
# time values must use future-dates
|
||||
InputValidationShipcall.check_times_are_in_future(loadedModel, content)
|
||||
if is_put_data:
|
||||
# the type of a shipcall may not be changed. It can only be set with the initial POST-request.
|
||||
InputValidationShipcall.check_shipcall_type_is_unchanged(loadedModel)
|
||||
else:
|
||||
# time values must use future-dates
|
||||
InputValidationShipcall.check_times_are_in_future(loadedModel, content)
|
||||
|
||||
# some arguments must not be provided
|
||||
InputValidationShipcall.check_forbidden_arguments(content, forbidden_keys=forbidden_keys)
|
||||
@ -209,7 +216,7 @@ class InputValidationShipcall():
|
||||
is_bsmd_or_agency = (is_bsmd) or (is_agency)
|
||||
|
||||
if not is_bsmd_or_agency:
|
||||
raise ValidationError(f"current user must be either of participant type BSMD or AGENCY. Cannot post or put shipcalls. Found user data: {user_data} and participant_type: {participant_type}")
|
||||
raise ValidationError({"participant_type":f"current user must be either of participant type BSMD or AGENCY. Cannot post or put shipcalls. Found user data: {user_data} and participant_type: {participant_type}"})
|
||||
return
|
||||
|
||||
@staticmethod
|
||||
@ -229,23 +236,33 @@ class InputValidationShipcall():
|
||||
|
||||
valid_ship_id = check_if_ship_id_is_valid(ship_id=ship_id)
|
||||
if not valid_ship_id:
|
||||
raise ValidationError(f"provided an invalid ship id, which is not found in the database: {ship_id}")
|
||||
raise ValidationError({"ship_id":f"provided an invalid ship id, which is not found in the database: {ship_id}"})
|
||||
|
||||
valid_arrival_berth_id = check_if_berth_id_is_valid(berth_id=arrival_berth_id)
|
||||
if not valid_arrival_berth_id:
|
||||
raise ValidationError(f"provided an invalid arrival berth id, which is not found in the database: {arrival_berth_id}")
|
||||
raise ValidationError({"arrival_berth_id":f"provided an invalid arrival berth id, which is not found in the database: {arrival_berth_id}"})
|
||||
|
||||
valid_departure_berth_id = check_if_berth_id_is_valid(berth_id=departure_berth_id)
|
||||
if not valid_departure_berth_id:
|
||||
raise ValidationError(f"provided an invalid departure berth id, which is not found in the database: {departure_berth_id}")
|
||||
raise ValidationError({"departure_berth_id":f"provided an invalid departure berth id, which is not found in the database: {departure_berth_id}"})
|
||||
|
||||
valid_participant_ids = check_if_participant_ids_are_valid(participants=participants)
|
||||
if not valid_participant_ids:
|
||||
raise ValidationError(f"one of the provided participant ids is invalid. Could not find one of these in the database: {participants}")
|
||||
raise ValidationError({"participants":f"one of the provided participant ids is invalid. Could not find one of these in the database: {participants}"})
|
||||
|
||||
valid_participant_types = check_if_participant_ids_and_types_are_valid(participants=participants)
|
||||
if not valid_participant_types: # #TODO: according to Daniel, there may eventually be multi-assignment of participants for the same role
|
||||
raise ValidationError(f"every participant id and type should be listed only once. Found multiple entries for one of the participants.")
|
||||
raise ValidationError({"participants":f"every participant id and type should be listed only once. Found multiple entries for one of the participants."})
|
||||
|
||||
@staticmethod
|
||||
def check_shipcall_type_is_unchanged(loadedModel:dict):
|
||||
# the type of a shipcall may only be set on POST requests. Afterwards, shipcall types may not be changed.
|
||||
query = SQLQuery.get_shipcall_by_id()
|
||||
shipcall = execute_sql_query_standalone(query=query, model=Shipcall, param={"id":loadedModel.get("id")}, command_type="single")
|
||||
|
||||
if int(loadedModel["type"]) != int(shipcall.type):
|
||||
raise ValidationError({"type":f"The shipcall type may only be set in the initial POST-request. Afterwards, changing the shipcall type is not allowed."}) # @pytest.raises
|
||||
return
|
||||
|
||||
@staticmethod
|
||||
def check_forbidden_arguments(content:dict, forbidden_keys=["evaluation", "evaluation_message"]):
|
||||
@ -258,7 +275,7 @@ class InputValidationShipcall():
|
||||
for forbidden_key in forbidden_keys:
|
||||
value = content.get(forbidden_key, None)
|
||||
if value is not None:
|
||||
raise ValidationError(f"'{forbidden_key}' may not be set on POST. Found: {value}")
|
||||
raise ValidationError({"forbidden_key":f"'{forbidden_key}' may not be set on POST. Found: {value}"})
|
||||
return
|
||||
|
||||
@staticmethod
|
||||
@ -274,36 +291,36 @@ class InputValidationShipcall():
|
||||
departure_berth_id = content.get("departure_berth_id", None)
|
||||
|
||||
if ship_id is None:
|
||||
raise ValidationError(f"providing 'ship_id' is mandatory. Missing key!")
|
||||
raise ValidationError({"ship_id":f"providing 'ship_id' is mandatory. Missing key!"})
|
||||
|
||||
if int(type_)==int(ShipcallType.undefined):
|
||||
raise ValidationError(f"providing 'type' is mandatory. Missing key!")
|
||||
raise ValidationError({"type":f"providing 'type' is mandatory. Missing key!"})
|
||||
|
||||
# arrival: arrival_berth_id & eta must exist
|
||||
elif int(type_)==int(ShipcallType.arrival):
|
||||
if eta is None:
|
||||
raise ValidationError(f"providing 'eta' is mandatory. Missing key!")
|
||||
raise ValidationError({"eta":f"providing 'eta' is mandatory. Missing key!"})
|
||||
|
||||
if arrival_berth_id is None:
|
||||
raise ValidationError(f"providing 'arrival_berth_id' is mandatory. Missing key!")
|
||||
raise ValidationError({"arrival_berth_id":f"providing 'arrival_berth_id' is mandatory. Missing key!"})
|
||||
|
||||
# departure: departive_berth_id and etd must exist
|
||||
elif int(type_)==int(ShipcallType.departure):
|
||||
if etd is None:
|
||||
raise ValidationError(f"providing 'etd' is mandatory. Missing key!")
|
||||
raise ValidationError({"etd":f"providing 'etd' is mandatory. Missing key!"})
|
||||
|
||||
if departure_berth_id is None:
|
||||
raise ValidationError(f"providing 'departure_berth_id' is mandatory. Missing key!")
|
||||
raise ValidationError({"departure_berth_id":f"providing 'departure_berth_id' is mandatory. Missing key!"})
|
||||
|
||||
# shifting: arrival_berth_id, departure_berth_id, eta and etd must exist
|
||||
elif int(type_)==int(ShipcallType.shifting):
|
||||
if (eta is None) or (etd is None):
|
||||
raise ValidationError(f"providing 'eta' and 'etd' is mandatory. Missing one of those keys!")
|
||||
raise ValidationError({"eta_or_etd":f"providing 'eta' and 'etd' is mandatory. Missing one of those keys!"})
|
||||
if (arrival_berth_id is None) or (departure_berth_id is None):
|
||||
raise ValidationError(f"providing 'arrival_berth_id' & 'departure_berth_id' is mandatory. Missing key!")
|
||||
raise ValidationError({"arrival_berth_id_or_departure_berth_id":f"providing 'arrival_berth_id' & 'departure_berth_id' is mandatory. Missing key!"})
|
||||
|
||||
else:
|
||||
raise ValidationError(f"incorrect 'type' provided!")
|
||||
raise ValidationError({"type":f"incorrect 'type' provided!"})
|
||||
return
|
||||
|
||||
@staticmethod
|
||||
@ -350,32 +367,32 @@ class InputValidationShipcall():
|
||||
return
|
||||
|
||||
if type_ is None:
|
||||
raise ValidationError(f"when providing 'eta' or 'etd', one must provide the type of the shipcall, so the datetimes can be verified.")
|
||||
raise ValidationError({"type":f"when providing 'eta' or 'etd', one must provide the type of the shipcall, so the datetimes can be verified."})
|
||||
|
||||
if not isinstance(type_, (int, ShipcallType)):
|
||||
type_ = ShipcallType[type_]
|
||||
|
||||
# #TODO: properly handle what happens, when eta or etd (or both) are None
|
||||
if int(type_)==int(ShipcallType.undefined):
|
||||
raise ValidationError(f"providing 'type' is mandatory. Missing key!")
|
||||
raise ValidationError({"type":f"providing 'type' is mandatory. Missing key!"})
|
||||
elif int(type_)==int(ShipcallType.arrival):
|
||||
if eta is None: # null values -> no violation
|
||||
return
|
||||
|
||||
if not eta > time_now:
|
||||
raise ValidationError(f"'eta' must be in the future. Incorrect datetime provided. Current Time: {time_now}. ETA: {eta}.")
|
||||
raise ValidationError({"eta":f"'eta' must be in the future. Incorrect datetime provided. Current Time: {time_now}. ETA: {eta}."})
|
||||
if etd is not None:
|
||||
raise ValidationError(f"'etd' should not be set when the shipcall type is 'arrival'.")
|
||||
raise ValidationError({"etd":f"'etd' should not be set when the shipcall type is 'arrival'."})
|
||||
|
||||
elif int(type_)==int(ShipcallType.departure):
|
||||
if etd is None: # null values -> no violation
|
||||
return
|
||||
|
||||
if not etd > time_now:
|
||||
raise ValidationError(f"'etd' must be in the future. Incorrect datetime provided. Current Time: {time_now}. ETD: {etd}.")
|
||||
raise ValidationError({"etd":f"'etd' must be in the future. Incorrect datetime provided. Current Time: {time_now}. ETD: {etd}."})
|
||||
|
||||
if eta is not None:
|
||||
raise ValidationError(f"'eta' should not be set when the shipcall type is 'departure'.")
|
||||
raise ValidationError({"eta":f"'eta' should not be set when the shipcall type is 'departure'."})
|
||||
|
||||
elif int(type_)==int(ShipcallType.shifting):
|
||||
if (eta is None) and (etd is None): # null values -> no violation
|
||||
@ -384,30 +401,30 @@ class InputValidationShipcall():
|
||||
if not ((eta is not None) and (etd is not None)):
|
||||
# for PUT-requests, a user could try modifying only 'eta' or only 'etd'. To simplify the
|
||||
# rules, a user is only allowed to provide *both* values.
|
||||
raise ValidationError(f"For shifting shipcalls one should always provide, both, eta and etd.")
|
||||
raise ValidationError({"eta_or_etd":f"For shifting shipcalls one should always provide, both, eta and etd."})
|
||||
|
||||
if (not eta > time_now) or (not etd > time_now):
|
||||
raise ValidationError(f"'eta' and 'etd' must be in the future. Incorrect datetime provided. Current Time: {time_now}. ETA: {eta}. ETD: {etd}")
|
||||
raise ValidationError({"eta_or_etd":f"'eta' and 'etd' must be in the future. Incorrect datetime provided. Current Time: {time_now}. ETA: {eta}. ETD: {etd}"})
|
||||
if (not etd < eta):
|
||||
raise ValidationError(f"The estimated time of departure ('etd') must take place *before the estimated time of arrival ('eta'). The ship cannot arrive, before it has departed. Found: ETD: {etd}, ETA: {eta}")
|
||||
raise ValidationError({"eta_or_etd":f"The estimated time of departure ('etd') must take place *before the estimated time of arrival ('eta'). The ship cannot arrive, before it has departed. Found: ETD: {etd}, ETA: {eta}"})
|
||||
|
||||
if (eta is not None and etd is None) or (eta is None and etd is not None):
|
||||
raise ValidationError(f"'eta' and 'etd' must both be provided when the shipcall type is 'shifting'.")
|
||||
raise ValidationError({"eta_or_etd":f"'eta' and 'etd' must both be provided when the shipcall type is 'shifting'."})
|
||||
return
|
||||
|
||||
@staticmethod
|
||||
def check_tidal_window_in_future(type_, time_now, tidal_window_from, tidal_window_to):
|
||||
if tidal_window_to is not None:
|
||||
if not tidal_window_to >= time_now:
|
||||
raise ValidationError(f"'tidal_window_to' must be in the future. Incorrect datetime provided.")
|
||||
raise ValidationError({"tidal_window_to":f"'tidal_window_to' must be in the future. Incorrect datetime provided."})
|
||||
|
||||
if tidal_window_from is not None:
|
||||
if not tidal_window_from >= time_now:
|
||||
raise ValidationError(f"'tidal_window_from' must be in the future. Incorrect datetime provided.")
|
||||
raise ValidationError({"tidal_window_from":f"'tidal_window_from' must be in the future. Incorrect datetime provided."})
|
||||
|
||||
if (tidal_window_to is not None) and (tidal_window_from is not None):
|
||||
if tidal_window_to < tidal_window_from:
|
||||
raise ValidationError(f"'tidal_window_to' must take place after 'tidal_window_from'. Incorrect datetime provided. Found 'tidal_window_to': {tidal_window_to}, 'tidal_window_from': {tidal_window_to}.")
|
||||
raise ValidationError({"tidal_window_to_or_tidal_window_from":f"'tidal_window_to' must take place after 'tidal_window_from'. Incorrect datetime provided. Found 'tidal_window_to': {tidal_window_to}, 'tidal_window_from': {tidal_window_to}."})
|
||||
return
|
||||
|
||||
@staticmethod
|
||||
@ -419,7 +436,7 @@ class InputValidationShipcall():
|
||||
is_agency_participant = [ParticipantType.AGENCY in ParticipantType(participant.get("type")) for participant in participants]
|
||||
|
||||
if not any(is_agency_participant):
|
||||
raise ValidationError(f"One of the assigned participants *must* be of type 'ParticipantType.AGENCY'. Found list of participants: {participants}")
|
||||
raise ValidationError({"participants":f"One of the assigned participants *must* be of type 'ParticipantType.AGENCY'. Found list of participants: {participants}"})
|
||||
return
|
||||
|
||||
@staticmethod
|
||||
@ -435,14 +452,14 @@ class InputValidationShipcall():
|
||||
|
||||
# if the *existing* shipcall in the database is canceled, it may not be changed
|
||||
if shipcall.get("canceled", False):
|
||||
raise ValidationError(f"The shipcall with id 'shipcall_id' is canceled. A canceled shipcall may not be changed.")
|
||||
raise ValidationError({"canceled":f"The shipcall with id 'shipcall_id' is canceled. A canceled shipcall may not be changed."})
|
||||
return
|
||||
|
||||
@staticmethod
|
||||
def check_required_fields_of_put_request(content:dict):
|
||||
shipcall_id = content.get("id", None)
|
||||
if shipcall_id is None:
|
||||
raise ValidationError(f"A PUT request requires an 'id' to refer to.")
|
||||
raise ValidationError({"id":f"A PUT request requires an 'id' to refer to."})
|
||||
|
||||
@staticmethod
|
||||
def check_shipcall_id_exists(loadedModel):
|
||||
@ -451,7 +468,7 @@ class InputValidationShipcall():
|
||||
query = 'SELECT * FROM shipcall where (id = ?shipcall_id?)'
|
||||
shipcalls = execute_sql_query_standalone(query=query, model=Shipcall, param={"shipcall_id" : shipcall_id})
|
||||
if len(shipcalls)==0:
|
||||
raise ValidationError(f"unknown shipcall_id. There are no shipcalls with the ID {shipcall_id}")
|
||||
raise ValidationError({"id":f"unknown shipcall_id. There are no shipcalls with the ID {shipcall_id}"})
|
||||
return
|
||||
|
||||
@staticmethod
|
||||
@ -484,7 +501,7 @@ class InputValidationShipcall():
|
||||
|
||||
an_agency_is_assigned = len(assigned_agency)==1
|
||||
if len(assigned_agency)>1:
|
||||
raise ValidationError(f"Internal error? Found more than one assigned agency for the shipcall with ID {shipcall_id}. Found: {assigned_agency}")
|
||||
raise ValidationError({"internal_error":f"Internal error? Found more than one assigned agency for the shipcall with ID {shipcall_id}. Found: {assigned_agency}"})
|
||||
|
||||
if an_agency_is_assigned:
|
||||
# Agency assigned? User must belong to the assigned agency or be a BSMD user, in case the flag is set
|
||||
|
||||
@ -29,32 +29,32 @@ def build_post_data_type_dependent_required_fields_dict()->dict[ShipcallType,dic
|
||||
ShipcallType.arrival:{
|
||||
ParticipantType.undefined:[], # should not be set in POST requests
|
||||
ParticipantType.BSMD:[], # should not be set in POST requests
|
||||
ParticipantType.TERMINAL:["operations_start"],
|
||||
ParticipantType.AGENCY:["eta_berth"],
|
||||
ParticipantType.MOORING:["eta_berth"],
|
||||
ParticipantType.PILOT:["eta_berth"],
|
||||
ParticipantType.PORT_ADMINISTRATION:["eta_berth"],
|
||||
ParticipantType.TUG:["eta_berth"],
|
||||
ParticipantType.TERMINAL:[],
|
||||
ParticipantType.AGENCY:[],
|
||||
ParticipantType.MOORING:[],
|
||||
ParticipantType.PILOT:[],
|
||||
ParticipantType.PORT_ADMINISTRATION:[],
|
||||
ParticipantType.TUG:[],
|
||||
},
|
||||
ShipcallType.departure:{
|
||||
ParticipantType.undefined:[], # should not be set in POST requests
|
||||
ParticipantType.BSMD:[], # should not be set in POST requests
|
||||
ParticipantType.TERMINAL:["operations_end"],
|
||||
ParticipantType.AGENCY:["etd_berth"],
|
||||
ParticipantType.MOORING:["etd_berth"],
|
||||
ParticipantType.PILOT:["etd_berth"],
|
||||
ParticipantType.PORT_ADMINISTRATION:["etd_berth"],
|
||||
ParticipantType.TUG:["etd_berth"],
|
||||
ParticipantType.TERMINAL:[],
|
||||
ParticipantType.AGENCY:[],
|
||||
ParticipantType.MOORING:[],
|
||||
ParticipantType.PILOT:[],
|
||||
ParticipantType.PORT_ADMINISTRATION:[],
|
||||
ParticipantType.TUG:[],
|
||||
},
|
||||
ShipcallType.shifting:{
|
||||
ParticipantType.undefined:[], # should not be set in POST requests
|
||||
ParticipantType.BSMD:[], # should not be set in POST requests
|
||||
ParticipantType.TERMINAL:["operations_start", "operations_end"],
|
||||
ParticipantType.AGENCY:["eta_berth", "etd_berth"],
|
||||
ParticipantType.MOORING:["eta_berth", "etd_berth"],
|
||||
ParticipantType.PILOT:["eta_berth", "etd_berth"],
|
||||
ParticipantType.PORT_ADMINISTRATION:["eta_berth", "etd_berth"],
|
||||
ParticipantType.TUG:["eta_berth", "etd_berth"],
|
||||
ParticipantType.TERMINAL:[],
|
||||
ParticipantType.AGENCY:[],
|
||||
ParticipantType.MOORING:[],
|
||||
ParticipantType.PILOT:[],
|
||||
ParticipantType.PORT_ADMINISTRATION:[],
|
||||
ParticipantType.TUG:[],
|
||||
},
|
||||
}
|
||||
return post_data_type_dependent_required_fields_dict
|
||||
@ -85,13 +85,10 @@ class InputValidationTimes():
|
||||
# 2.) datasets may only be created, if the respective participant type did not already create one.
|
||||
InputValidationTimes.check_if_entry_already_exists_for_participant_type(user_data, loadedModel, content)
|
||||
|
||||
# 3.) only users who are *not* of type BSMD may post times datasets.
|
||||
InputValidationTimes.check_user_is_not_bsmd_type(user_data)
|
||||
|
||||
# 4.) Reference checking
|
||||
# 3.) Reference checking
|
||||
InputValidationTimes.check_dataset_references(content)
|
||||
|
||||
# 5.) Value checking
|
||||
# 4.) Value checking
|
||||
InputValidationTimes.check_dataset_values(user_data, loadedModel, content)
|
||||
return
|
||||
|
||||
@ -111,8 +108,10 @@ class InputValidationTimes():
|
||||
return
|
||||
|
||||
@staticmethod
|
||||
def evaluate_delete_data(user_data:dict, times_id:int):
|
||||
# #TODO_determine: is times_id always an int or does the request.args call provide a string?
|
||||
def evaluate_delete_data(user_data:dict, times_id:typing.Optional[int]):
|
||||
# 0.) an ID reference must be provided and will be converted to int
|
||||
if times_id is None:
|
||||
raise ValidationError({"id":"no times id provided"})
|
||||
times_id = int(times_id) if not isinstance(times_id, int) else times_id
|
||||
|
||||
# 1.) The dataset entry may not be deleted already
|
||||
@ -137,7 +136,7 @@ class InputValidationTimes():
|
||||
pdata = execute_sql_query_standalone(query=query, param={"id":times_id}, pooledConnection=None)
|
||||
|
||||
if len(pdata)==0:
|
||||
raise ValidationError(f"The selected time entry is already deleted. ID: {times_id}")
|
||||
raise ValidationError({"deleted":f"The selected time entry is already deleted. ID: {times_id}"})
|
||||
return
|
||||
|
||||
@staticmethod
|
||||
@ -145,7 +144,7 @@ class InputValidationTimes():
|
||||
"""a new dataset may only be created by a user who is *not* belonging to participant group BSMD"""
|
||||
is_bsmd = check_if_user_is_bsmd_type(user_data)
|
||||
if is_bsmd:
|
||||
raise ValidationError(f"current user belongs to BSMD. Cannot post 'times' datasets. Found user data: {user_data}")
|
||||
raise ValidationError({"participant_type":f"current user belongs to BSMD. Cannot post 'times' datasets. Found user data: {user_data}"})
|
||||
return
|
||||
|
||||
@staticmethod
|
||||
@ -166,7 +165,17 @@ class InputValidationTimes():
|
||||
loadedModel["participant_type"] = ParticipantType(loadedModel["participant_type"])
|
||||
|
||||
if ParticipantType.BSMD in loadedModel["participant_type"]:
|
||||
raise ValidationError(f"current user belongs to BSMD. Cannot post times datasets. Found user data: {user_data}")
|
||||
raise ValidationError({"participant_type":f"current user belongs to BSMD. Cannot post times datasets. Found user data: {user_data}"})
|
||||
|
||||
if (loadedModel["etd_interval_end"] is not None) and (loadedModel["etd_berth"] is not None):
|
||||
time_end_after_time_start = loadedModel["etd_interval_end"] >= loadedModel["etd_berth"]
|
||||
if not time_end_after_time_start:
|
||||
raise ValidationError({"etd":f"The provided time interval for the estimated departure time is invalid. The interval end takes place before the interval start. Found interval data: {loadedModel['etd_berth']} to {loadedModel['etd_interval_end']}"})
|
||||
|
||||
if (loadedModel["eta_interval_end"] is not None) and (loadedModel["eta_berth"] is not None):
|
||||
time_end_after_time_start = loadedModel["eta_interval_end"] >= loadedModel["eta_berth"]
|
||||
if not time_end_after_time_start:
|
||||
raise ValidationError({"eta":f"The provided time interval for the estimated arrival time is invalid. The interval begin takes place after the interval end. Found interval data: {loadedModel['eta_berth']} to {loadedModel['eta_interval_end']}"})
|
||||
return
|
||||
|
||||
@staticmethod
|
||||
@ -178,19 +187,19 @@ class InputValidationTimes():
|
||||
Note: whenever an ID is 'None', there is no exception, because a different method is supposed to capture non-existant mandatory fields.
|
||||
"""
|
||||
# extract the IDs
|
||||
berth_id, participant_id, shipcall_id = content.get("berth_id"), content.get("participant_id"), content.get("shipcall_id")
|
||||
berth_id, shipcall_id, participant_id = content.get("berth_id"), content.get("shipcall_id"), content.get("participant_id")
|
||||
|
||||
valid_berth_id_reference = check_if_berth_id_is_valid(berth_id)
|
||||
if not valid_berth_id_reference:
|
||||
raise ValidationError(f"The referenced berth_id '{berth_id}' does not exist in the database.")
|
||||
raise ValidationError({"berth_id":f"The referenced berth_id '{berth_id}' does not exist in the database."})
|
||||
|
||||
valid_shipcall_id_reference = check_if_shipcall_id_is_valid(shipcall_id)
|
||||
if not valid_shipcall_id_reference:
|
||||
raise ValidationError(f"The referenced shipcall_id '{shipcall_id}' does not exist in the database.")
|
||||
raise ValidationError({"shipcall_id":f"The referenced shipcall_id '{shipcall_id}' does not exist in the database."})
|
||||
|
||||
valid_participant_id_reference = check_if_participant_id_is_valid_standalone(participant_id, participant_type=None)
|
||||
if not valid_participant_id_reference:
|
||||
raise ValidationError(f"The referenced participant_id '{participant_id}' does not exist in the database.")
|
||||
raise ValidationError({"participant_id":f"The referenced participant_id '{participant_id}' does not exist in the database."})
|
||||
|
||||
return
|
||||
|
||||
@ -212,7 +221,7 @@ class InputValidationTimes():
|
||||
shipcall_type = ShipcallType[shipcalls.get(shipcall_id,{}).get("type",ShipcallType.undefined.name)]
|
||||
|
||||
if (participant_type is None) or (int(shipcall_type) == int(ShipcallType.undefined)):
|
||||
raise ValidationError(f"At least one of the required fields is missing. Missing: 'participant_type' or 'shipcall_type'")
|
||||
raise ValidationError({"required_fields":f"At least one of the required fields is missing. Missing: 'participant_type' or 'shipcall_type'"})
|
||||
|
||||
|
||||
# build a list of required fields based on shipcall and participant type, as well as type-independent fields
|
||||
@ -229,14 +238,14 @@ class InputValidationTimes():
|
||||
if any(missing_required_fields):
|
||||
# create a tuple of (field_key, bool) to describe to a user, which one of the fields may be missing
|
||||
verbosity_tuple = [(field, missing) for field, missing in zip(required_fields, missing_required_fields) if missing]
|
||||
raise ValidationError(f"At least one of the required fields is missing. Missing: {verbosity_tuple}")
|
||||
raise ValidationError({"required_fields":f"At least one of the required fields is missing. Missing: {verbosity_tuple}"})
|
||||
return
|
||||
|
||||
@staticmethod
|
||||
def check_times_required_fields_put_data(content:dict):
|
||||
"""in a PUT request, only the 'id' is a required field. All other fields are simply ignored, when they are not provided."""
|
||||
if content.get("id") is None:
|
||||
raise ValidationError(f"A PUT-request requires an 'id' reference, which was not found.")
|
||||
raise ValidationError({"id":f"A PUT-request requires an 'id' reference, which was not found."})
|
||||
return
|
||||
|
||||
@staticmethod
|
||||
@ -326,7 +335,7 @@ class InputValidationTimes():
|
||||
]
|
||||
|
||||
if not len(matching_spm)>0:
|
||||
raise ValidationError(f'The participant group with id {user_participant_id} is not assigned to the shipcall. Found ShipcallParticipantMap: {spm_shipcall_data}') # part of a pytest.raises
|
||||
raise ValidationError({"participant_id":f'The participant group with id {user_participant_id} is not assigned to the shipcall. Found ShipcallParticipantMap: {spm_shipcall_data}'}) # part of a pytest.raises
|
||||
return
|
||||
|
||||
@staticmethod
|
||||
@ -345,7 +354,7 @@ class InputValidationTimes():
|
||||
# check, if there is already a dataset for the participant type
|
||||
participant_type_exists_already = any([ParticipantType(time_.get("participant_type",0)) in participant_type for time_ in times])
|
||||
if participant_type_exists_already:
|
||||
raise ValidationError(f"A dataset for the participant type is already present. Participant Type: {participant_type}. Times Datasets: {times}")
|
||||
raise ValidationError({"participant_type":f"A dataset for the participant type is already present. Participant Type: {participant_type}. Times Datasets: {times}"})
|
||||
return
|
||||
|
||||
@staticmethod
|
||||
@ -388,7 +397,7 @@ class InputValidationTimes():
|
||||
# extracts the participant_id from the first matching entry, if applicable
|
||||
if not len(pdata)>0:
|
||||
# this case is usually covered by the InputValidationTimes.check_if_entry_is_already_deleted method already
|
||||
raise ValidationError(f"Unknown times_id. Could not find a matching entry for ID: {times_id}")
|
||||
raise ValidationError({"times_id":f"Unknown times_id. Could not find a matching entry for ID: {times_id}"})
|
||||
else:
|
||||
participant_type = pdata[0].get("participant_type")
|
||||
shipcall_id = pdata[0].get("shipcall_id")
|
||||
@ -404,14 +413,14 @@ class InputValidationTimes():
|
||||
if special_case__bsmd_may_edit_agency_dataset:
|
||||
return
|
||||
else:
|
||||
raise ValidationError(f"The dataset may only be changed by a user belonging to the same participant group as the times dataset is referring to. User participant_id: {user_participant_id}; Dataset participant_id: {participant_id_of_times_dataset}")
|
||||
raise ValidationError({"user_participant_type":f"The dataset may only be changed by a user belonging to the same participant group as the times dataset is referring to. User participant_id: {user_participant_id}; Dataset participant_id: {participant_id_of_times_dataset}"})
|
||||
return
|
||||
|
||||
@staticmethod
|
||||
def get_participant_id_from_shipcall_participant_map(shipcall_id:int, participant_type:int, spm_shipcall_data=None)->int:
|
||||
"""use shipcall_id and participant_type to identify the matching participant_id"""
|
||||
if shipcall_id is None:
|
||||
raise ValidationError(f"Could not find a referenced shipcall_id within the request.")
|
||||
raise ValidationError({"shipcall_id":f"Could not find a referenced shipcall_id within the request."})
|
||||
|
||||
if spm_shipcall_data is None:
|
||||
spm_shipcall_data = execute_sql_query_standalone(
|
||||
@ -421,7 +430,7 @@ class InputValidationTimes():
|
||||
|
||||
# raise an error when there are no matches
|
||||
if len(spm_shipcall_data)==0:
|
||||
raise ValidationError(f"Could not find a matching time dataset for the provided participant_type: {participant_type} at shipcall with id {shipcall_id}.")
|
||||
raise ValidationError({"participant_type":f"Could not find a matching time dataset for the provided participant_type: {participant_type} at shipcall with id {shipcall_id}."})
|
||||
|
||||
participant_id_of_times_dataset = spm_shipcall_data[0].get("participant_id")
|
||||
return participant_id_of_times_dataset
|
||||
@ -471,3 +480,44 @@ class InputValidationTimes():
|
||||
has_bsmd_flag = ParticipantFlag.BSMD in [ParticipantFlag(participant.get("flags"))]
|
||||
return has_bsmd_flag
|
||||
|
||||
|
||||
def deprecated_build_post_data_type_dependent_required_fields_dict()->dict[ShipcallType,dict[ParticipantType,typing.Optional[list[str]]]]:
|
||||
"""
|
||||
The required fields of a POST-request depend on ShipcallType and ParticipantType. This function creates
|
||||
a dictionary, which maps those types to a list of required fields.
|
||||
|
||||
The participant types 'undefined' and 'bsmd' should not be used in POST-requests. They return 'None'.
|
||||
"""
|
||||
post_data_type_dependent_required_fields_dict = {
|
||||
ShipcallType.arrival:{
|
||||
ParticipantType.undefined:[], # should not be set in POST requests
|
||||
ParticipantType.BSMD:[], # should not be set in POST requests
|
||||
ParticipantType.TERMINAL:[],
|
||||
ParticipantType.AGENCY:["eta_berth"],
|
||||
ParticipantType.MOORING:["eta_berth"],
|
||||
ParticipantType.PILOT:["eta_berth"],
|
||||
ParticipantType.PORT_ADMINISTRATION:["eta_berth"],
|
||||
ParticipantType.TUG:["eta_berth"],
|
||||
},
|
||||
ShipcallType.departure:{
|
||||
ParticipantType.undefined:[], # should not be set in POST requests
|
||||
ParticipantType.BSMD:[], # should not be set in POST requests
|
||||
ParticipantType.TERMINAL:[],
|
||||
ParticipantType.AGENCY:["etd_berth"],
|
||||
ParticipantType.MOORING:["etd_berth"],
|
||||
ParticipantType.PILOT:["etd_berth"],
|
||||
ParticipantType.PORT_ADMINISTRATION:["etd_berth"],
|
||||
ParticipantType.TUG:["etd_berth"],
|
||||
},
|
||||
ShipcallType.shifting:{
|
||||
ParticipantType.undefined:[], # should not be set in POST requests
|
||||
ParticipantType.BSMD:[], # should not be set in POST requests
|
||||
ParticipantType.TERMINAL:[],
|
||||
ParticipantType.AGENCY:["etd_berth"],
|
||||
ParticipantType.MOORING:["etd_berth"],
|
||||
ParticipantType.PILOT:["etd_berth"],
|
||||
ParticipantType.PORT_ADMINISTRATION:["etd_berth"],
|
||||
ParticipantType.TUG:["etd_berth"],
|
||||
},
|
||||
}
|
||||
return post_data_type_dependent_required_fields_dict
|
||||
|
||||
@ -158,7 +158,7 @@ def check_if_participant_id_is_valid_standalone(participant_id:int, participant_
|
||||
|
||||
if participant_type is not None:
|
||||
if participant_id not in list(participants.keys()):
|
||||
raise ValidationError(f"the provided participant_id {participant_id} does not exist in the database.")
|
||||
raise ValidationError({"participant_id":f"the provided participant_id {participant_id} does not exist in the database."})
|
||||
|
||||
# IntFlag object
|
||||
participant_type_in_db = ParticipantType(int(participants.get(participant_id).get("type", ParticipantType.undefined)))
|
||||
|
||||
@ -19,9 +19,9 @@ def validate_time_is_in_future(value:datetime.datetime):
|
||||
|
||||
def validate_time_is_in_not_too_distant_future(raise_validation_error:bool, value:datetime.datetime, seconds:int=60, minutes:int=60, hours:int=24, days:int=30, months:int=12)->bool:
|
||||
"""
|
||||
combines two boolean operations. Returns True when both conditions are met.
|
||||
a) value is in the future
|
||||
b) value is not too distant (e.g., at max. 1 year in the future)
|
||||
A time entry is considerd valid, when it meets the following condition(s):
|
||||
a) value is not too distant (e.g., at max. 1 year in the future)
|
||||
Previous variants of this function also included validating that a time must be in the future. This is deprecated.
|
||||
|
||||
When the value is 'None', the validation will be skipped. A ValidationError is never issued, but the method returns 'False'.
|
||||
|
||||
@ -31,17 +31,17 @@ def validate_time_is_in_not_too_distant_future(raise_validation_error:bool, valu
|
||||
if value is None:
|
||||
return False
|
||||
|
||||
is_in_future = validate_time_is_in_future(value)
|
||||
# is_in_future = validate_time_is_in_future(value)
|
||||
is_too_distant = validate_time_exceeds_threshold(value, seconds, minutes, hours, days, months)
|
||||
|
||||
if raise_validation_error:
|
||||
if not is_in_future:
|
||||
raise ValidationError(f"The provided value must be in the future. Current Time: {datetime.datetime.now()}, Value: {value}")
|
||||
#if not is_in_future:
|
||||
#raise ValidationError({"any_date":f"The provided value must be in the future. Current Time: {datetime.datetime.now()}, Value: {value}"})
|
||||
|
||||
if is_too_distant:
|
||||
raise ValidationError(f"The provided value is in the too distant future and exceeds a threshold for 'reasonable' entries. Found: {value}")
|
||||
raise ValidationError({"any_date":f"The provided value is in the too distant future and exceeds a threshold for 'reasonable' entries. Found: {value}"})
|
||||
|
||||
return is_in_future & (not is_too_distant)
|
||||
return (not is_too_distant) # & is_in_future
|
||||
|
||||
class TimeLogic():
|
||||
def __init__(self):
|
||||
|
||||
@ -1,34 +1,87 @@
|
||||
import logging
|
||||
import typing
|
||||
import json
|
||||
import sys
|
||||
from marshmallow import ValidationError
|
||||
import werkzeug
|
||||
from werkzeug.exceptions import Forbidden
|
||||
|
||||
|
||||
|
||||
def create_validation_error_response(ex:ValidationError, status_code:int=400)->typing.Tuple[str,int]:
|
||||
# generate an overview the errors
|
||||
#example:
|
||||
# {'lock_time': ['The provided value must be in the future. Current Time: 2024-09-02 08:23:32.600791, Value: 2024-09-01 08:20:41.853000']}
|
||||
errors = ex.messages
|
||||
|
||||
# example:
|
||||
# "Valid Data": {
|
||||
# "id": 2894,
|
||||
# "eta_berth": "datetime.datetime(2024, 9, 2, 11, 11, 43)",
|
||||
# "eta_berth_fixed": false
|
||||
# }
|
||||
valid_data = ex.valid_data
|
||||
|
||||
|
||||
def create_default_json_response_format(error_field:str, error_description:str):
|
||||
"""
|
||||
The default format of a JSON response for exceptions is:
|
||||
{
|
||||
"error_field":str,
|
||||
"error_description":str
|
||||
}
|
||||
"""
|
||||
json_response = {
|
||||
"message":"ValidationError",
|
||||
"errors":errors,
|
||||
"valid_data":valid_data
|
||||
"error_field":error_field,
|
||||
"error_description":error_description
|
||||
}
|
||||
return (json.dumps(json_response), status_code)
|
||||
return json_response
|
||||
|
||||
def create_werkzeug_error_response(ex:Forbidden, status_code:int=403)->typing.Tuple[str,int]:
|
||||
return json.dumps({"message":ex.description}), status_code
|
||||
def unbundle_(errors, unbundled=[]):
|
||||
"""
|
||||
unbundles a dictionary entry into separate items and appends them to the list {unbundled}.
|
||||
Example:
|
||||
errors = {"key1":{"keya":"keyb","keyc":{"keyc12":12}}}
|
||||
Returns:
|
||||
[{'error_field':'keya', 'error_description':['keyb']}, {'error_field':'keyc12', 'error_description':[12]}]
|
||||
As can be seen, only the subkeys and their respective value are received. Each value is *always* a list.
|
||||
"""
|
||||
{k:unbundle_(v,unbundled=unbundled) if isinstance(v,dict) else unbundled.append({"error_field":k, "error_description":v[0] if isinstance(v,list) else str(v)}) for k,v in errors.items()}
|
||||
return
|
||||
|
||||
def unbundle_validation_error_message(message):
|
||||
"""
|
||||
inputs:
|
||||
message: ValidationError.messages object. A str, list or dictionary
|
||||
"""
|
||||
unbundled = []
|
||||
unbundle_(message, unbundled=unbundled)
|
||||
if len(unbundled)>0:
|
||||
error_field = "ValidationError in the following field(s): " + " & ".join([unb["error_field"] for unb in unbundled])
|
||||
error_description = "Error Description(s): " + " & ".join([unb["error_description"] for unb in unbundled])
|
||||
else:
|
||||
error_field = "ValidationError"
|
||||
error_description = "unknown validation error"
|
||||
return (error_field, error_description)
|
||||
|
||||
def create_validation_error_response(ex:ValidationError, status_code:int=400, create_log:bool=True)->typing.Tuple[str,int]:
|
||||
# unbundles ValidationError into a dictionary of {'error_field':str, 'error_description':str}-format
|
||||
message = ex.messages
|
||||
(error_field, error_description) = unbundle_validation_error_message(message)
|
||||
json_response = create_default_json_response_format(error_field=error_field, error_description=error_description)
|
||||
|
||||
# json.dumps with default=str automatically converts non-serializable values to strings. Hence, datetime objects (which are not)
|
||||
# natively serializable are properly serialized.
|
||||
serialized_response = json.dumps(json_response, default=str)
|
||||
|
||||
if create_log:
|
||||
logging.warning(ex) if ex is not None else logging.warning(message)
|
||||
print(ex) if ex is not None else print(message)
|
||||
return (serialized_response, status_code)
|
||||
|
||||
def create_werkzeug_error_response(ex:Forbidden, status_code:int=403, create_log:bool=True)->typing.Tuple[str,int]:
|
||||
# json.dumps with default=str automatically converts non-serializable values to strings. Hence, datetime objects (which are not)
|
||||
# natively serializable are properly serialized.
|
||||
message = ex.description
|
||||
json_response = create_default_json_response_format(error_field=str(repr(ex)), error_description=message)
|
||||
serialized_response = json.dumps(json_response, default=str)
|
||||
|
||||
if create_log:
|
||||
logging.warning(ex) if ex is not None else logging.warning(message)
|
||||
print(ex) if ex is not None else print(message)
|
||||
return serialized_response, status_code
|
||||
|
||||
def create_dynamic_exception_response(ex, status_code:int=400, message:typing.Optional[str]=None, create_log:bool=True):
|
||||
message = repr(ex) if message is None else message
|
||||
json_response = create_default_json_response_format(error_field="Exception", error_description=message)
|
||||
json_response["message"] = "call failed"
|
||||
|
||||
serialized_response = json.dumps(json_response, default=str)
|
||||
|
||||
if create_log:
|
||||
logging.warning(ex) if ex is not None else logging.warning(message)
|
||||
print(ex) if ex is not None else print(message)
|
||||
return (serialized_response, status_code)
|
||||
|
||||
@ -27,7 +27,7 @@ error_message_dict = {
|
||||
# 0002 A+B+C
|
||||
"validation_rule_fct_shipcall_incoming_participants_disagree_on_eta":"There are deviating times between agency, mooring, port authority, pilot and tug for the estimated time of arrival (ETA) {Rule #0002A}",
|
||||
"validation_rule_fct_shipcall_outgoing_participants_disagree_on_etd":"There are deviating times between agency, mooring, port authority, pilot and tug for the estimated time of departure (ETD) {Rule #0002B}",
|
||||
"validation_rule_fct_shipcall_shifting_participants_disagree_on_eta_or_etd":"There are deviating times between agency, mooring, port authority, pilot and tug for ETA and ETD {Rule #0002C}",
|
||||
"validation_rule_fct_shipcall_shifting_participants_disagree_on_etd":"There are deviating times between agency, mooring, port authority, pilot and tug for the estimated time of departure (ETD) {Rule #0002C}",
|
||||
|
||||
# 0003 A+B
|
||||
"validation_rule_fct_eta_time_not_in_operation_window":"The estimated time of arrival will be AFTER the planned start of operations. {Rule #0003A}",
|
||||
@ -723,21 +723,13 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
|
||||
else:
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
def validation_rule_fct_shipcall_shifting_participants_disagree_on_eta_or_etd(self, shipcall, df_times, *args, **kwargs):
|
||||
def validation_rule_fct_shipcall_shifting_participants_disagree_on_etd(self, shipcall, df_times, *args, **kwargs):
|
||||
"""
|
||||
Code: #0002-C
|
||||
Type: Local Rule
|
||||
Description: this validation checks, whether the participants expect different ETA or ETD times
|
||||
Description: this validation checks, whether the participants expect different ETD times
|
||||
Filter: only applies to shifting shipcalls
|
||||
"""
|
||||
violation_state_eta = self.check_participants_agree_on_estimated_time(
|
||||
shipcall = shipcall,
|
||||
|
||||
query="eta_berth",
|
||||
df_times=df_times,
|
||||
applicable_shipcall_type=ShipcallType.SHIFTING
|
||||
)
|
||||
|
||||
violation_state_etd = self.check_participants_agree_on_estimated_time(
|
||||
shipcall = shipcall,
|
||||
|
||||
@ -746,16 +738,14 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
|
||||
applicable_shipcall_type=ShipcallType.SHIFTING
|
||||
)
|
||||
|
||||
# apply 'eta_berth' check
|
||||
# apply 'etd_berth'
|
||||
# violation: if either 'eta_berth' or 'etd_berth' is violated
|
||||
# violation: if either 'etd_berth' is violated
|
||||
# functionally, this is the same as individually comparing all times for the participants
|
||||
# times_agency.eta_berth==times_mooring.eta_berth==times_portadministration.eta_berth==times_pilot.eta_berth==times_tug.eta_berth
|
||||
# times_agency.etd_berth==times_mooring.etd_berth==times_portadministration.etd_berth==times_pilot.etd_berth==times_tug.etd_berth
|
||||
violation_state = (violation_state_eta) or (violation_state_etd)
|
||||
violation_state = (violation_state_etd)
|
||||
|
||||
if violation_state:
|
||||
validation_name = "validation_rule_fct_shipcall_shifting_participants_disagree_on_eta_or_etd"
|
||||
validation_name = "validation_rule_fct_shipcall_shifting_participants_disagree_on_etd"
|
||||
return (StatusFlags.RED, validation_name)
|
||||
else:
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
@ -2,7 +2,7 @@ import os
|
||||
import sys
|
||||
import logging
|
||||
|
||||
sys.path.insert(0, '/var/www/brecal_devel/src/server')
|
||||
sys.path.insert(0, '/var/www/brecal_test/src/server')
|
||||
sys.path.insert(0, '/var/www/venv/lib/python3.10/site-packages/')
|
||||
|
||||
import schedule
|
||||
|
||||
@ -2,7 +2,7 @@ from setuptools import find_packages, setup
|
||||
|
||||
setup(
|
||||
name='BreCal',
|
||||
version='1.4.0',
|
||||
version='1.5.0',
|
||||
packages=find_packages(),
|
||||
include_package_data=True,
|
||||
zip_safe=False,
|
||||
|
||||
@ -87,6 +87,13 @@ def test_sql_get_ships():
|
||||
assert all([isinstance(ship, model.Ship) for ship in ships])
|
||||
return
|
||||
|
||||
def test_sql_get_ship_by_id():
|
||||
query = #"SELECT * FROM ship where id = ?id?" # #TODO_refactor: put into the SQLQuery object
|
||||
ship = execute_sql_query_standalone(SQLQuery.get_ship_by_id(), param={"id":1}, command_type="single", model=model.Ship)
|
||||
assert isinstance(ship, model.Ship)
|
||||
return
|
||||
|
||||
|
||||
def test_sql_get_times():
|
||||
options = {'shipcall_id':153}
|
||||
times = execute_sql_query_standalone(query=SQLQuery.get_times(), model=model.Times, param={"scid" : options["shipcall_id"]})
|
||||
|
||||
@ -901,3 +901,35 @@ def test_post_data_with_valid_data(get_stub_token):
|
||||
assert response.status_code==201
|
||||
return
|
||||
|
||||
def test_input_validation_shipcall_put_fails_when_type_differs():
|
||||
from marshmallow import ValidationError
|
||||
from BreCal.stubs.shipcall import get_stub_valid_shipcall_arrival
|
||||
from BreCal.schemas import model
|
||||
from BreCal.database.sql_queries import SQLQuery
|
||||
from BreCal.database.sql_handler import execute_sql_query_standalone
|
||||
|
||||
from BreCal.validators.input_validation_shipcall import InputValidationShipcall
|
||||
|
||||
# stub data
|
||||
put_data = get_stub_valid_shipcall_arrival()
|
||||
put_data["id"] = 3
|
||||
loadedModel = model.ShipcallSchema().load(data=put_data, many=False, partial=True)
|
||||
|
||||
# load data from DB
|
||||
shipcall_id = loadedModel.get("id")
|
||||
query = SQLQuery.get_shipcall_by_id()
|
||||
shipcall = execute_sql_query_standalone(query=query, model=model.Shipcall, param={"id":shipcall_id}, command_type="single")
|
||||
|
||||
# create failure case. Ensures that the type is not the same as in the loaded shipcall from the DB
|
||||
failure_case = 1 if shipcall.type==2 else 2
|
||||
loadedModel["type"] = model.ShipcallType(failure_case)
|
||||
|
||||
# should fail: different 'type'
|
||||
with pytest.raises(ValidationError, match="The shipcall type may only be set in the initial POST-request. Afterwards, changing the shipcall type is not allowed"):
|
||||
InputValidationShipcall.check_shipcall_type_is_unchanged(loadedModel)
|
||||
|
||||
# should pass: same 'type'
|
||||
loadedModel["type"] = shipcall.type
|
||||
InputValidationShipcall.check_shipcall_type_is_unchanged(loadedModel)
|
||||
return
|
||||
|
||||
|
||||
@ -409,3 +409,60 @@ def test_input_validation_times_delete_request_fails_when_user_belongs_to_wrong_
|
||||
InputValidationTimes.check_user_belongs_to_same_group_as_dataset_determines(user_data, loadedModel=None, times_id=times_id, pdata=pdata)
|
||||
return
|
||||
|
||||
def test_input_validation_times_check_dataset_values_for_time_intervals():
|
||||
import datetime
|
||||
from BreCal.schemas import model
|
||||
from BreCal.validators.input_validation_times import InputValidationTimes
|
||||
from BreCal.stubs.times import get_schema_model_stub_arrival, get_schema_model_stub_departure
|
||||
from marshmallow import ValidationError
|
||||
|
||||
### ETD (departure)
|
||||
# expected to pass
|
||||
schemaModel = get_schema_model_stub_departure()
|
||||
schemaModel["etd_interval_end"] = schemaModel["etd_berth"] + datetime.timedelta(minutes=2)
|
||||
|
||||
schemaModel["etd_berth"] = schemaModel["etd_berth"].isoformat()
|
||||
schemaModel["etd_interval_end"] = schemaModel["etd_interval_end"].isoformat()
|
||||
content = schemaModel
|
||||
loadedModel = model.TimesSchema().load(data=schemaModel, many=False, partial=True)
|
||||
|
||||
InputValidationTimes.check_dataset_values(user_data={}, loadedModel=loadedModel, content=content)
|
||||
|
||||
# expected to fail: the from-to-interval is incorrectly set.
|
||||
schemaModel = get_schema_model_stub_departure()
|
||||
schemaModel["etd_interval_end"] = schemaModel["etd_berth"] - datetime.timedelta(minutes=2)
|
||||
|
||||
schemaModel["etd_berth"] = schemaModel["etd_berth"].isoformat()
|
||||
schemaModel["etd_interval_end"] = schemaModel["etd_interval_end"].isoformat()
|
||||
content = schemaModel
|
||||
loadedModel = model.TimesSchema().load(data=schemaModel, many=False, partial=True)
|
||||
|
||||
with pytest.raises(ValidationError, match="The provided time interval for the estimated departure time is invalid"):
|
||||
InputValidationTimes.check_dataset_values(user_data={}, loadedModel=loadedModel, content=content)
|
||||
|
||||
|
||||
### ETA (arrival)
|
||||
# expected to pass
|
||||
schemaModel = get_schema_model_stub_arrival()
|
||||
schemaModel["eta_interval_end"] = schemaModel["eta_berth"] + datetime.timedelta(minutes=2)
|
||||
|
||||
schemaModel["eta_berth"] = schemaModel["eta_berth"].isoformat()
|
||||
schemaModel["eta_interval_end"] = schemaModel["eta_interval_end"].isoformat()
|
||||
content = schemaModel
|
||||
loadedModel = model.TimesSchema().load(data=schemaModel, many=False, partial=True)
|
||||
|
||||
InputValidationTimes.check_dataset_values(user_data={}, loadedModel=loadedModel, content=content)
|
||||
|
||||
# expected to fail: the from-to-interval is incorrectly set.
|
||||
schemaModel = get_schema_model_stub_arrival()
|
||||
schemaModel["eta_interval_end"] = schemaModel["eta_berth"] - datetime.timedelta(minutes=2)
|
||||
|
||||
schemaModel["eta_berth"] = schemaModel["eta_berth"].isoformat()
|
||||
schemaModel["eta_interval_end"] = schemaModel["eta_interval_end"].isoformat()
|
||||
content = schemaModel
|
||||
loadedModel = model.TimesSchema().load(data=schemaModel, many=False, partial=True)
|
||||
|
||||
with pytest.raises(ValidationError, match="The provided time interval for the estimated arrival time is invalid"):
|
||||
InputValidationTimes.check_dataset_values(user_data={}, loadedModel=loadedModel, content=content)
|
||||
return
|
||||
|
||||
|
||||
19
src/server/tests/validators/test_validation_error.py
Normal file
19
src/server/tests/validators/test_validation_error.py
Normal file
@ -0,0 +1,19 @@
|
||||
import pytest
|
||||
|
||||
def test_create_validation_error_response_is_serializable():
|
||||
from BreCal.stubs.times_full import get_valid_stub_times
|
||||
from BreCal.schemas import model
|
||||
from BreCal.validators.validation_error import create_validation_error_response
|
||||
|
||||
content = get_valid_stub_times()
|
||||
|
||||
import datetime
|
||||
content["operations_end"] = (datetime.datetime.now()-datetime.timedelta(minutes=14)).isoformat()
|
||||
|
||||
content["id"] = 3
|
||||
try:
|
||||
loadedModel = model.TimesSchema().load(data=content, many=False, partial=True)
|
||||
except Exception as ex:
|
||||
my_var = ex
|
||||
create_validation_error_response(ex=ex, status_code=400) # this function initially created errors, as datetime objects were not serializable. Corrected now.
|
||||
return
|
||||
Loading…
Reference in New Issue
Block a user