added port_id and ports endpoint to flask app (no validation yet!), reading works

This commit is contained in:
Daniel Schick 2024-09-13 17:45:31 +02:00
parent c796be2892
commit 489dfc2ed6
13 changed files with 189 additions and 42 deletions

View File

@ -1,7 +1,7 @@
//----------------------
// <auto-generated>
// 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
// </auto-generated>
//----------------------
@ -7729,10 +7729,11 @@ namespace BreCalClient.misc.Model
/// <param name="city">city.</param>
/// <param name="type">a logical combination (bitflag) of possible values. This cannot be encoded in a Enumeration type with discrete values in OpenAPI version &lt; 3.1..</param>
/// <param name="flags">Bit-encoded flag array for internal use.</param>
/// <param name="ports">ports.</param>
/// <param name="created">Readonly field set by the database when participant was created.</param>
/// <param name="modified">Readonly field set by the database when participant was last modified.</param>
/// <param name="deleted">marks the participant as logically deleted (default to false).</param>
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<int> ports = default(List<int>), 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; }
/// <summary>
/// Gets or Sets Ports
/// </summary>
/// <example>[1,2]</example>
[DataMember(Name = "ports", EmitDefaultValue = true)]
public List<int> Ports { get; set; }
/// <summary>
/// Readonly field set by the database when participant was created
/// </summary>
/// <value>Readonly field set by the database when participant was created</value>
@ -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();

View File

@ -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

View File

@ -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))

View File

@ -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")

View File

@ -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")

View File

@ -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)

View File

@ -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:

View File

@ -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

View File

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

View File

@ -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:

View File

@ -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'}

View File

@ -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()

View File

@ -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