diff --git a/misc/BreCalApi.cs b/misc/BreCalApi.cs
index b7f4ce0..700002d 100644
--- a/misc/BreCalApi.cs
+++ b/misc/BreCalApi.cs
@@ -1,7 +1,7 @@
//----------------------
//
-// Generated REST API Client Code Generator v1.9.8.0 on 12.09.2024 16:39:15
+// Generated REST API Client Code Generator v1.9.8.0 on 13.09.2024 16:04:34
// Using the tool OpenAPI Generator v7.4.0
//
//----------------------
@@ -7729,10 +7729,11 @@ namespace BreCalClient.misc.Model
/// city.
/// a logical combination (bitflag) of possible values. This cannot be encoded in a Enumeration type with discrete values in OpenAPI version < 3.1..
/// Bit-encoded flag array for internal use.
+ /// ports.
/// Readonly field set by the database when participant was created.
/// Readonly field set by the database when participant was last modified.
/// marks the participant as logically deleted (default to false).
- public Participant(int id = default(int), string name = default(string), string street = default(string), string postalCode = default(string), string city = default(string), int type = default(int), int? flags = default(int?), DateTime created = default(DateTime), DateTime? modified = default(DateTime?), bool deleted = false)
+ public Participant(int id = default(int), string name = default(string), string street = default(string), string postalCode = default(string), string city = default(string), int type = default(int), int? flags = default(int?), List ports = default(List), DateTime created = default(DateTime), DateTime? modified = default(DateTime?), bool deleted = false)
{
this.Id = id;
this.Name = name;
@@ -7741,6 +7742,7 @@ namespace BreCalClient.misc.Model
this.City = city;
this.Type = type;
this.Flags = flags;
+ this.Ports = ports;
this.Created = created;
this.Modified = modified;
this.Deleted = deleted;
@@ -7790,6 +7792,12 @@ namespace BreCalClient.misc.Model
[DataMember(Name = "flags", EmitDefaultValue = true)]
public int? Flags { get; set; }
///
+ /// Gets or Sets Ports
+ ///
+ /// [1,2]
+ [DataMember(Name = "ports", EmitDefaultValue = true)]
+ public List Ports { get; set; }
+ ///
/// Readonly field set by the database when participant was created
///
/// Readonly field set by the database when participant was created
@@ -7825,6 +7833,7 @@ namespace BreCalClient.misc.Model
sb.Append(" City: ").Append(City).Append("\n");
sb.Append(" Type: ").Append(Type).Append("\n");
sb.Append(" Flags: ").Append(Flags).Append("\n");
+ sb.Append(" Ports: ").Append(Ports).Append("\n");
sb.Append(" Created: ").Append(Created).Append("\n");
sb.Append(" Modified: ").Append(Modified).Append("\n");
sb.Append(" Deleted: ").Append(Deleted).Append("\n");
@@ -7893,6 +7902,12 @@ namespace BreCalClient.misc.Model
(this.Flags != null &&
this.Flags.Equals(input.Flags))
) &&
+ (
+ this.Ports == input.Ports ||
+ this.Ports != null &&
+ input.Ports != null &&
+ this.Ports.SequenceEqual(input.Ports)
+ ) &&
(
this.Created == input.Created ||
(this.Created != null &&
@@ -7939,6 +7954,10 @@ namespace BreCalClient.misc.Model
{
hashCode = (hashCode * 59) + this.Flags.GetHashCode();
}
+ if (this.Ports != null)
+ {
+ hashCode = (hashCode * 59) + this.Ports.GetHashCode();
+ }
if (this.Created != null)
{
hashCode = (hashCode * 59) + this.Created.GetHashCode();
diff --git a/misc/BreCalApi.yaml b/misc/BreCalApi.yaml
index 7c86277..5fc9cfb 100644
--- a/misc/BreCalApi.yaml
+++ b/misc/BreCalApi.yaml
@@ -766,7 +766,7 @@ paths:
description: Returns a list of ports
tags:
- static
- operationId: get-ports
+ operationId: get-ports
responses:
'200':
description: list of ports
@@ -780,7 +780,7 @@ paths:
country: Germany
- id: 4
name: Hamburg
- country: Germany
+ country: Germany
'401':
$ref: '#/components/responses/401'
'403':
@@ -789,7 +789,6 @@ paths:
$ref: '#/components/responses/500'
'503':
$ref: '#/components/responses/503'
-
components:
schemas:
credentials:
@@ -1593,6 +1592,17 @@ components:
participant:
type: object
description: A organisational entity that participates in Bremen Calling
+ example:
+ id: 42
+ name: BSMD
+ street: Hermann-Hollerith-Str. 7
+ postal code: '28359'
+ city: Bremen
+ type: 10
+ flags: 0
+ created: '2023-08-21T08:23:35Z'
+ modified: '2023-08-21T08:23:35Z'
+ deleted: false
properties:
id:
type: integer
@@ -1620,8 +1630,21 @@ components:
flags:
description: Bit-encoded flag array for internal use
type: integer
- nullable: true
example: 0
+ nullable: true
+ ports:
+ type: array
+ x-stoplight:
+ id: cj29rdqeg15b3
+ items:
+ x-stoplight:
+ id: is7kj0fmlc1nz
+ type: integer
+ minimum: 1
+ example: 1
+ example:
+ - 1
+ - 2
created:
type: string
format: date-time
@@ -1630,25 +1653,14 @@ components:
modified:
type: string
format: date-time
- nullable: true
description: Readonly field set by the database when participant was last modified
example: '2023-08-21T08:23:35Z'
+ nullable: true
deleted:
description: marks the participant as logically deleted
type: boolean
default: false
example: false
- example:
- id: 42
- name: BSMD
- street: Hermann-Hollerith-Str. 7
- postal code: '28359'
- city: Bremen
- type: 10
- flags: 0
- created: '2023-08-21T08:23:35Z'
- modified: '2023-08-21T08:23:35Z'
- deleted: false
participant_list:
description: List of participants
type: array
@@ -1856,7 +1868,7 @@ components:
x-stoplight:
id: it0cu6ivurgii
type: array
- items:
+ items:
$ref: '#/components/schemas/port'
example:
- id: 2
diff --git a/src/server/BreCal/__init__.py b/src/server/BreCal/__init__.py
index 263203e..0d2ff39 100644
--- a/src/server/BreCal/__init__.py
+++ b/src/server/BreCal/__init__.py
@@ -13,6 +13,7 @@ from .api import ships
from .api import login
from .api import user
from .api import history
+from .api import ports
from BreCal.brecal_utils.file_handling import get_project_root, ensure_path
from BreCal.brecal_utils.test_handling import execute_test_with_pytest, execute_coverage_test
@@ -65,6 +66,7 @@ def create_app(test_config=None, instance_path=None):
app.register_blueprint(login.bp)
app.register_blueprint(user.bp)
app.register_blueprint(history.bp)
+ app.register_blueprint(ports.bp)
logging.basicConfig(filename='brecaldevel.log', level=logging.DEBUG, format='%(asctime)s | %(name)s | %(levelname)s | %(message)s')
local_db.initPool(os.path.dirname(app.instance_path))
diff --git a/src/server/BreCal/api/berths.py b/src/server/BreCal/api/berths.py
index 114f893..9249dd9 100644
--- a/src/server/BreCal/api/berths.py
+++ b/src/server/BreCal/api/berths.py
@@ -3,6 +3,7 @@ from flask import Blueprint, request
from webargs.flaskparser import parser
from .. import impl
from ..services.auth_guard import auth_guard
+from ..services.jwt_handler import decode_jwt
import json
from BreCal.validators.validation_error import create_dynamic_exception_response
@@ -15,8 +16,11 @@ def GetBerths():
try:
if 'Authorization' in request.headers:
- token = request.headers.get('Authorization')
- return impl.berths.GetBerths(token)
+ token = request.headers.get('Authorization')
+ payload = decode_jwt(token.split("Bearer ")[-1])
+ options = {}
+ options["participant_id"] = payload["participant_id"]
+ return impl.berths.GetBerths(options)
else:
return create_dynamic_exception_response(ex=None, status_code=403, message="not authenticated")
diff --git a/src/server/BreCal/api/participant.py b/src/server/BreCal/api/participant.py
index 814a3dd..475d0f6 100644
--- a/src/server/BreCal/api/participant.py
+++ b/src/server/BreCal/api/participant.py
@@ -2,6 +2,7 @@ import logging
from flask import Blueprint, request
from .. import impl
from ..services.auth_guard import auth_guard
+from ..services.jwt_handler import decode_jwt
import json
from BreCal.validators.validation_error import create_dynamic_exception_response
@@ -12,10 +13,15 @@ bp = Blueprint('participants', __name__)
def GetParticipant():
try:
- if 'Authorization' in request.headers:
- token = request.headers.get('Authorization')
+ if 'Authorization' in request.headers:
+ payload = decode_jwt(request.headers.get("Authorization").split("Bearer ")[-1])
options = {}
options["user_id"] = request.args.get("user_id")
+ if "participant_id" in payload:
+ options["participant_id"] = payload["participant_id"]
+ else:
+ return create_dynamic_exception_response(ex=None, status_code=403, message="not authorized")
+
return impl.participant.GetParticipant(options)
else:
return create_dynamic_exception_response(ex=None, status_code=403, message="not authenticated")
diff --git a/src/server/BreCal/api/ports.py b/src/server/BreCal/api/ports.py
new file mode 100644
index 0000000..6f450d1
--- /dev/null
+++ b/src/server/BreCal/api/ports.py
@@ -0,0 +1,24 @@
+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('ports', __name__)
+
+
+@bp.route('/ports', methods=['get'])
+@auth_guard() # no restriction by role
+def GetPorts():
+
+ try:
+ if 'Authorization' in request.headers:
+ token = request.headers.get('Authorization')
+ return impl.ports.GetPorts(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)
diff --git a/src/server/BreCal/api/shipcalls.py b/src/server/BreCal/api/shipcalls.py
index b21908e..b5b8898 100644
--- a/src/server/BreCal/api/shipcalls.py
+++ b/src/server/BreCal/api/shipcalls.py
@@ -7,6 +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.services.jwt_handler import decode_jwt
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
@@ -32,9 +33,10 @@ def GetShipcalls():
# oneline:
payload = decode_jwt(request.headers.get("Authorization").split("Bearer ")[-1])
"""
- options = {}
- options["participant_id"] = request.args.get("participant_id")
+ payload = decode_jwt(request.headers.get("Authorization").split("Bearer ")[-1])
+ options = {}
options["past_days"] = request.args.get("past_days", default=1, type=int)
+ options["participant_id"] = payload["participant_id"]
return impl.shipcalls.GetShipcalls(options)
else:
diff --git a/src/server/BreCal/database/sql_queries.py b/src/server/BreCal/database/sql_queries.py
index e0e9b7e..5917081 100644
--- a/src/server/BreCal/database/sql_queries.py
+++ b/src/server/BreCal/database/sql_queries.py
@@ -10,20 +10,22 @@ def create_sql_query_shipcall_get(options:dict)->str:
options : dict. A dictionary, which must contains the 'past_days' key (int). Determines the range
by which shipcalls are filtered.
"""
- query = ("SELECT s.id as id, ship_id, type, eta, voyage, etd, arrival_berth_id, departure_berth_id, tug_required, pilot_required, " +
+ query = ("SELECT s.id as id, ship_id, port_id, type, eta, voyage, etd, arrival_berth_id, departure_berth_id, tug_required, pilot_required, " +
"flags, s.pier_side, bunkering, replenishing_terminal, replenishing_lock, draft, tidal_window_from, " +
"tidal_window_to, rain_sensitive_cargo, recommended_tugs, anchored, moored_lock, canceled, evaluation, " +
"evaluation_message, evaluation_time, evaluation_notifications_sent, s.created as created, s.modified as modified, time_ref_point " +
"FROM shipcall s " +
"LEFT JOIN times t ON t.shipcall_id = s.id AND t.participant_type = 8 " +
"WHERE " +
+ "port_id in (SELECT port_id FROM participant_port_map WHERE participant_id = %d)" +
+ " AND (" +
"(type = 1 AND " +
"((t.id IS NOT NULL AND t.eta_berth >= DATE(NOW() - INTERVAL %d DAY)) OR " +
"(eta >= DATE(NOW() - INTERVAL %d DAY)))) OR " +
"((type = 2 OR type = 3) AND " +
"((t.id IS NOT NULL AND t.etd_berth >= DATE(NOW() - INTERVAL %d DAY)) OR " +
- "(etd >= DATE(NOW() - INTERVAL %d DAY)))) " +
- "ORDER BY eta") % (options["past_days"], options["past_days"], options["past_days"], options["past_days"])
+ "(etd >= DATE(NOW() - INTERVAL %d DAY))))) " +
+ "ORDER BY eta") % (options["participant_id"], options["past_days"], options["past_days"], options["past_days"], options["past_days"])
return query
diff --git a/src/server/BreCal/impl/__init__.py b/src/server/BreCal/impl/__init__.py
index 93ec9fc..8cea758 100644
--- a/src/server/BreCal/impl/__init__.py
+++ b/src/server/BreCal/impl/__init__.py
@@ -6,4 +6,5 @@ from . import times
from . import ships
from . import login
from . import user
-from . import history
\ No newline at end of file
+from . import history
+from . import ports
\ No newline at end of file
diff --git a/src/server/BreCal/impl/berths.py b/src/server/BreCal/impl/berths.py
index 0ed2ca9..aefdd87 100644
--- a/src/server/BreCal/impl/berths.py
+++ b/src/server/BreCal/impl/berths.py
@@ -4,9 +4,8 @@ import pydapper
from ..schemas import model
from .. import local_db
-from BreCal.database.sql_queries import SQLQuery
-def GetBerths(token):
+def GetBerths(options):
"""
No parameters, gets all entries
"""
@@ -14,9 +13,13 @@ def GetBerths(token):
try:
pooledConnection = local_db.getPoolConnection()
commands = pydapper.using(pooledConnection)
- # query = SQLQuery.get_berth()
- # data = commands.query(query, model=model.Berth)
- data = commands.query("SELECT id, name, `lock`, owner_id, authority_id, created, modified, deleted FROM berth WHERE deleted = 0 ORDER BY name", model=model.Berth)
+
+ # only load berths to ports that the participant is assigned to
+ query = ("SELECT id, name, `lock`, owner_id, port_id, authority_id, created, modified, deleted FROM berth WHERE " +
+ "deleted = 0 AND + "
+ "port_id IN (SELECT port_id FROM participant_port_map WHERE participant_id = %d) " +
+ "ORDER BY name") % (options["participant_id"])
+ data = commands.query(query, model=model.Berth)
return json.dumps(data, default=model.obj_dict), 200, {'Content-Type': 'application/json; charset=utf-8'}
except Exception as ex:
diff --git a/src/server/BreCal/impl/participant.py b/src/server/BreCal/impl/participant.py
index 7f9cc48..4e38031 100644
--- a/src/server/BreCal/impl/participant.py
+++ b/src/server/BreCal/impl/participant.py
@@ -10,18 +10,35 @@ def GetParticipant(options):
"""
:param options: A dictionary containing all the paramters for the Operations
options["user_id"]: **Id of user**. *Example: 2*. User id returned by login call.
-
- """
+ """
try:
pooledConnection = local_db.getPoolConnection()
commands = pydapper.using(pooledConnection)
if "user_id" in options and options["user_id"]:
# query = SQLQuery.get_participant_by_user_id()
- data = commands.query("SELECT p.id as id, p.name as name, p.street as street, p.postal_code as postal_code, p.city as city, p.type as type, p.flags as flags, p.created as created, p.modified as modified, p.deleted as deleted FROM participant p INNER JOIN user u WHERE u.participant_id = p.id and u.id = ?userid?", model=model.Participant, param={"userid" : options["user_id"]})
+ query = ("SELECT p.id as id, p.name as name, p.street as street, p.postal_code as postal_code, p.city as city, p.type as type, p.flags as flags, " +
+ "p.created as created, p.modified as modified, p.deleted as deleted FROM participant p " +
+ "INNER JOIN user u WHERE u.participant_id = p.id and u.id = %d") % options["user_id"]
+ data = commands.query(query, model=model.Participant)
else:
- # query = SQLQuery.get_participants()
- data = commands.query("SELECT id, name, street, postal_code, city, type, flags, created, modified, deleted FROM participant p ORDER BY p.name", model=model.Participant)
+ # query = SQLQuery.get_participants()
+
+ # list only participants that are assigned to the same ports than participant of caller
+ query = ("SELECT p.id as id, name, street, postal_code, city, type, flags, p.created, p.modified, p.deleted " +
+ "FROM participant p " +
+ "JOIN participant_port_map ON p.id = participant_port_map.participant_id " +
+ "WHERE participant_port_map.port_id IN " +
+ "(SELECT port_id FROM participant_port_map where participant_id = %d) " +
+ "GROUP BY id " +
+ "ORDER BY p.name") % options["participant_id"]
+
+ data = commands.query(query, model=model.Participant)
+ for participant in data:
+ port_query = "SELECT port_id FROM participant_port_map WHERE participant_id=?id?";
+ for record in commands.query(port_query, model=model.Port_Assignment, param={"id" : participant.id}, buffered=False):
+ pa = model.Port_Assignment(record.port_id)
+ participant.ports.append(pa.port_id)
return json.dumps(data, default=model.obj_dict), 200, {'Content-Type': 'application/json; charset=utf-8'}
diff --git a/src/server/BreCal/impl/ports.py b/src/server/BreCal/impl/ports.py
new file mode 100644
index 0000000..46450e0
--- /dev/null
+++ b/src/server/BreCal/impl/ports.py
@@ -0,0 +1,33 @@
+import json
+import logging
+import pydapper
+
+from ..schemas import model
+from .. import local_db
+from BreCal.database.sql_queries import SQLQuery
+
+def GetPorts(token):
+ """
+ No parameters, gets all entries
+ """
+
+ try:
+ pooledConnection = local_db.getPoolConnection()
+ commands = pydapper.using(pooledConnection)
+ data = commands.query("SELECT id, name, locode, created, modified, deleted FROM port WHERE deleted = 0 ORDER BY name", model=model.Port)
+ return json.dumps(data, default=model.obj_dict), 200, {'Content-Type': 'application/json; charset=utf-8'}
+
+ except Exception as ex:
+ logging.error(ex)
+ print(ex)
+ result = {}
+ result["message"] = "call failed"
+ return json.dumps(result), 500
+
+ finally:
+ if pooledConnection is not None:
+ pooledConnection.close()
+
+
+
+
diff --git a/src/server/BreCal/schemas/model.py b/src/server/BreCal/schemas/model.py
index c3eed38..e0352a6 100644
--- a/src/server/BreCal/schemas/model.py
+++ b/src/server/BreCal/schemas/model.py
@@ -29,6 +29,15 @@ class Berth(Schema):
lock: bool
owner_id: int
authority_id: int
+ port_id: int
+ created: datetime
+ modified: datetime
+ deleted: bool
+
+class Port(Schema):
+ id: int
+ name: str
+ locode: str
created: datetime
modified: datetime
deleted: bool
@@ -164,10 +173,11 @@ class Participant(Schema):
postal_code: str
city: str
type: int # fields.Enum(ParticipantType ...)
- flags: int
+ flags: int
created: datetime
modified: datetime
deleted: bool
+ ports: List[int] = field(default_factory=list)
@validates("type")
def validate_type(self, value):
@@ -269,6 +279,16 @@ class Participant_Assignment:
def to_json(self):
return self.__dict__
+@dataclass
+class Port_Assignment:
+ def __init__(self, port_id):
+ self.port_id = port_id
+ pass
+
+ port_id: int
+
+ def to_json(self):
+ return self.__dict__
@dataclass
class Shipcall:
@@ -301,6 +321,7 @@ class Shipcall:
evaluation_time: datetime
evaluation_notifications_sent: bool
time_ref_point: int
+ port_id: int
created: datetime
modified: datetime
participants: List[Participant_Assignment] = field(default_factory=list)
@@ -335,6 +356,7 @@ class Shipcall:
"evaluation_time": self.evaluation_time.isoformat() if self.evaluation_time else "",
"evaluation_notifications_sent": self.evaluation_notifications_sent,
"time_ref_point": self.time_ref_point,
+ "port_id": self.port_id,
"created": self.created.isoformat() if self.created else "",
"modified": self.modified.isoformat() if self.modified else "",
"participants": [participant.__dict__ for participant in self.participants]
@@ -343,8 +365,8 @@ class Shipcall:
@classmethod
- def from_query_row(self, id, ship_id, type, eta, voyage, etd, arrival_berth_id, departure_berth_id, tug_required, pilot_required, flags, pier_side, bunkering, replenishing_terminal, replenishing_lock, draft, tidal_window_from, tidal_window_to, rain_sensitive_cargo, recommended_tugs, anchored, moored_lock, canceled, evaluation, evaluation_message, evaluation_time, evaluation_notifications_sent, time_ref_point, created, modified):
- return self(id, ship_id, ShipcallType(type), eta, voyage, etd, arrival_berth_id, departure_berth_id, tug_required, pilot_required, flags, pier_side, bunkering, replenishing_terminal, replenishing_lock, draft, tidal_window_from, tidal_window_to, rain_sensitive_cargo, recommended_tugs, anchored, moored_lock, canceled, EvaluationType(evaluation), evaluation_message, evaluation_time, evaluation_notifications_sent, time_ref_point, created, modified)
+ def from_query_row(self, id, ship_id, type, eta, voyage, etd, arrival_berth_id, departure_berth_id, tug_required, pilot_required, flags, pier_side, bunkering, replenishing_terminal, replenishing_lock, draft, tidal_window_from, tidal_window_to, rain_sensitive_cargo, recommended_tugs, anchored, moored_lock, canceled, evaluation, evaluation_message, evaluation_time, evaluation_notifications_sent, time_ref_point, port_id, created, modified):
+ return self(id, ship_id, ShipcallType(type), eta, voyage, etd, arrival_berth_id, departure_berth_id, tug_required, pilot_required, flags, pier_side, bunkering, replenishing_terminal, replenishing_lock, draft, tidal_window_from, tidal_window_to, rain_sensitive_cargo, recommended_tugs, anchored, moored_lock, canceled, EvaluationType(evaluation), evaluation_message, evaluation_time, evaluation_notifications_sent, time_ref_point, port_id, created, modified)
class ShipcallId(Schema):
pass