FreeDATA/freedata_server/api/freedata.py

679 lines
22 KiB
Python

from fastapi import APIRouter, Request
from api.common import api_response, api_abort, api_ok, validate
from api.command_helpers import enqueue_tx_command
from message_system_db_messages import DatabaseManagerMessages
from message_system_db_attachments import DatabaseManagerAttachments
from message_system_db_beacon import DatabaseManagerBeacon
from message_system_db_station import DatabaseManagerStations
import command_message_send
import adif_udp_logger
import wavelog_api_logger
router = APIRouter()
@router.get("/messages/{message_id}", summary="Get Message by ID", tags=["FreeDATA"], responses={
200: {"description": "Message found and returned."},
404: {"description": "Message not found."}
})
async def get_freedata_message(message_id: str, request: Request):
message = DatabaseManagerMessages(request.app.event_manager).get_message_by_id_json(message_id)
return api_response(message)
async def post_freedata_message(request: Request):
"""
Transmit a FreeDATA message.
Parameters:
request (Request): The HTTP request containing the message data in JSON format.
Returns:
dict: A JSON object containing the transmitted message details.
"""
data = await request.json()
await enqueue_tx_command(request.app, command_message_send.SendMessageCommand, data)
return api_response(data)
@router.post("/messages/{message_id}/adif", summary="Send Message ADIF Log", tags=["FreeDATA"], responses={
200: {
"description": "ADIF log sent successfully.",
"content": {
"application/json": {
"example": {
"adif_output": "ADIF data..."
}
}
}
},
400: {
"description": "Bad Request: The request was malformed or missing required parameters.",
"content": {
"application/json": {
"example": {
"error": "Invalid message ID."
}
}
}
},
404: {
"description": "The requested resource was not found.",
"content": {
"application/json": {
"example": {
"error": "Message not found."
}
}
}
},
503: {
"description": "Modem not running.",
"content": {
"application/json": {
"example": {
"error": "Modem not running."
}
}
}
}
})
async def post_freedata_message_adif_log(message_id: str, request:Request):
adif_output = DatabaseManagerMessages(request.app.event_manager).get_message_by_id_adif(message_id)
# if message not found do not send adif as the return then is not valid
if not adif_output:
return
# Send the ADIF data via UDP and/or wavelog
adif_udp_logger.send_adif_qso_data(request.app.config_manager.read(), request.app.event_manager, adif_output)
wavelog_api_logger.send_wavelog_qso_data(request.app.config_manager.read(), request.app.event_manager, adif_output)
return api_response(adif_output)
@router.patch("/messages/{message_id}", summary="Update Message by ID", tags=["FreeDATA"], responses={
200: {
"description": "Message updated successfully.",
"content": {
"application/json": {
"example": {
"is_read": True
}
}
}
},
400: {
"description": "Bad Request: The request was malformed or missing required parameters.",
"content": {
"application/json": {
"example": {
"error": "Invalid parameters."
}
}
}
},
404: {
"description": "Message not found.",
"content": {
"application/json": {
"example": {
"error": "Message not found."
}
}
}
}
})
async def patch_freedata_message(message_id: str, request: Request):
"""
Update a FreeDATA message by its ID.
Parameters:
message_id (str): The ID of the message to update.
request (Request): The HTTP request containing the update data in JSON format.
Returns:
dict: A JSON object containing the updated message details.
"""
data = await request.json()
if data.get("action") == "retransmit":
result = DatabaseManagerMessages(request.app.event_manager).update_message(message_id, update_data={'status': 'queued'})
DatabaseManagerMessages(request.app.event_manager).increment_message_attempts(message_id)
else:
result = DatabaseManagerMessages(request.app.event_manager).update_message(message_id, update_data=data)
return api_response(result)
@router.get("/messages", summary="Get All Messages", tags=["FreeDATA"], responses={
200: {
"description": "List of all messages.",
"content": {
"application/json": {
"example": {
"total_messages": 1,
"messages": [
{
"id": "DXCALL-6_MYCALL-0_2024-04-12T20:39:05.302479",
"timestamp": "2024-04-12T20:39:05.302479",
"origin": "DXCALL-6",
"via": None,
"destination": "MYCALL-0",
"direction": "receive",
"body": "Hello !",
"attachments": [],
"status": "received",
"priority": 10,
"is_read": False,
"statistics": {
"total_bytes": 120,
"duration": 29.76698660850525,
"bytes_per_minute": 241,
"time_histogram": {
"0": "2024-04-12T20:39:23.423169",
"1": "2024-04-12T20:39:30.504638",
"2": "2024-04-12T20:39:37.745075"
},
"snr_histogram": {
"0": -6,
"1": -6,
"2": -6
},
"bpm_histogram": {
"0": 198,
"1": 265,
"2": 252
}
}
}
]
}
}
}
},
404: {
"description": "The requested resource was not found.",
"content": {
"application/json": {
"example": {
"error": "Resource not found."
}
}
}
}
})
async def get_freedata_messages(request: Request):
filters = {k: v for k, v in request.query_params.items() if v}
result = DatabaseManagerMessages(request.app.event_manager).get_all_messages_json(filters=filters)
return api_response(result)
@router.post("/messages", summary="Transmit Message", tags=["FreeDATA"], responses={
200: {
"description": "Message transmitted successfully.",
"content": {
"application/json": {
"example": {
"destination": "XX1XXX-6",
"body": "Hello FreeDATA"
}
}
}
},
404: {
"description": "The requested resource was not found.",
"content": {
"application/json": {
"example": {
"error": "Resource not found."
}
}
}
}
})
async def post_freedata_message(request: Request):
"""
Transmit a FreeDATA message.
Parameters:
request (Request): The HTTP request containing the message data in JSON format.
Returns:
dict: A JSON object containing the transmitted message details.
"""
data = await request.json()
await enqueue_tx_command(request.app, command_message_send.SendMessageCommand, data)
return api_response(data)
@router.delete("/messages/{message_id}", summary="Delete Message by ID", tags=["FreeDATA"], responses={
200: {
"description": "Message deleted successfully.",
"content": {
"application/json": {
"example": {
"message": "DXCALL-0_MYCALL-5_2024-04-04T17:22:14.002502 deleted",
"status": "success"
}
}
}
},
404: {
"description": "Message not found.",
"content": {
"application/json": {
"example": {
"message": "Message not found",
"status": "failure"
}
}
}
}
})
async def delete_freedata_message(message_id: str, request:Request):
result = DatabaseManagerMessages(request.app.event_manager).delete_message(message_id)
if result:
return api_response({"message": f"{message_id} deleted", "status": "success"})
else:
return api_response({"message": "Message not found", "status": "failure"}, status_code=404)
@router.get("/messages/{message_id}/attachments", summary="Get Attachments by Message ID", tags=["FreeDATA"], responses={
200: {
"description": "List of attachments for the specified message.",
"content": {
"application/json": {
"example": {
"attachments": [
{
"id": "attachment1",
"filename": "file1.txt",
"file_size": 1024,
"file_type": "text/plain",
"data_sha512": "abcdef1234567890..."
},
{
"id": "attachment2",
"filename": "image.png",
"file_size": 2048,
"file_type": "image/png",
"data_sha512": "123456abcdef7890..."
}
]
}
}
}
},
404: {
"description": "The requested resource was not found.",
"content": {
"application/json": {
"example": {
"error": "Resource not found."
}
}
}
},
503: {
"description": "Modem not running.",
"content": {
"application/json": {
"example": {
"error": "Modem not running."
}
}
}
}
})
async def get_message_attachments(message_id: str, request:Request):
attachments = DatabaseManagerAttachments(request.app.event_manager).get_attachments_by_message_id_json(message_id)
return api_response(attachments)
@router.get("/messages/attachment/{data_sha512}", summary="Get Attachment by SHA512", tags=["FreeDATA"], responses={
200: {
"description": "Retrieve a specific attachment by its SHA512 hash.",
"content": {
"application/json": {
"example": {
"id": "attachment1",
"filename": "file1.txt",
"file_size": 1024,
"file_type": "text/plain",
"data_sha512": "abcdef1234567890..."
}
}
}
},
404: {
"description": "The requested attachment was not found.",
"content": {
"application/json": {
"example": {
"error": "Attachment not found."
}
}
}
},
503: {
"description": "Modem not running.",
"content": {
"application/json": {
"example": {
"error": "Modem not running."
}
}
}
}
})
async def get_message_attachment(data_sha512: str, request:Request):
attachment = DatabaseManagerAttachments(request.app.event_manager).get_attachment_by_sha512(data_sha512)
return api_response(attachment)
@router.get("/beacons", summary="Get Received Beacons", tags=["FreeDATA"], responses={
200: {
"description": "List of received beacons.",
"content": {
"application/json": {
"example": {
"total_beacons": 2,
"beacons": [
{
"id": "DXCALL-0_MYCALL-5_2024-04-04T17:22:14.002502",
"timestamp": "2024-04-04T17:22:14.002502",
"origin": "DXCALL-0",
"via": None,
"destination": "MYCALL-5",
"direction": "receive",
"body": "Hello FreeDATA",
"attachments": [],
"status": "received",
"priority": 10,
"is_read": False,
"statistics": {
"total_bytes": 120,
"duration": 29.77,
"bytes_per_minute": 241,
"time_histogram": {
"0": "2024-04-04T17:22:23.423169",
"1": "2024-04-04T17:22:30.504638",
"2": "2024-04-04T17:22:37.745075"
},
"snr_histogram": {
"0": -6,
"1": -6,
"2": -6
},
"bpm_histogram": {
"0": 198,
"1": 265,
"2": 252
}
}
}
]
}
}
}
},
400: {
"description": "Bad Request: The request was malformed or missing required parameters.",
"content": {
"application/json": {
"example": {
"error": "Invalid request."
}
}
}
},
404: {
"description": "The requested resource was not found.",
"content": {
"application/json": {
"example": {
"error": "Resource not found."
}
}
}
},
500: {
"description": "Internal Server Error: An unexpected error occurred on the server.",
"content": {
"application/json": {
"example": {
"error": "Internal server error."
}
}
}
},
503: {
"description": "Modem not running.",
"content": {
"application/json": {
"example": {
"error": "Modem not running."
}
}
}
}
})
async def get_all_beacons(request:Request):
beacons = DatabaseManagerBeacon(request.app.event_manager).get_all_beacons()
return api_response(beacons)
@router.get("/beacons/{callsign}", summary="Get Beacon by Callsign", tags=["FreeDATA"], responses={
200: {
"description": "List of beacons from the specified callsign.",
"content": {
"application/json": {
"example": {
"beacons": [
{
"id": "DXCALL-0_MYCALL-5_2024-04-04T17:22:14.002502",
"timestamp": "2024-04-04T17:22:14.002502",
"origin": "DXCALL-0",
"via": None,
"destination": "MYCALL-5",
"direction": "receive",
"body": "Hello FreeDATA",
"attachments": [],
"status": "received",
"priority": 10,
"is_read": False,
"statistics": {
"total_bytes": 120,
"duration": 29.77,
"bytes_per_minute": 241,
"time_histogram": {
"0": "2024-04-04T17:22:23.423169",
"1": "2024-04-04T17:22:30.504638",
"2": "2024-04-04T17:22:37.745075"
},
"snr_histogram": {
"0": -6,
"1": -6,
"2": -6
},
"bpm_histogram": {
"0": 198,
"1": 265,
"2": 252
}
}
}
]
}
}
}
},
400: {
"description": "Bad Request: The request was malformed or missing required parameters.",
"content": {
"application/json": {
"example": {
"error": "Invalid request."
}
}
}
},
404: {
"description": "The requested resource was not found.",
"content": {
"application/json": {
"example": {
"error": "Resource not found."
}
}
}
},
500: {
"description": "Internal Server Error: An unexpected error occurred on the server.",
"content": {
"application/json": {
"example": {
"error": "Internal server error."
}
}
}
},
503: {
"description": "Modem not running.",
"content": {
"application/json": {
"example": {
"error": "Modem not running."
}
}
}
}
})
async def get_beacons_by_callsign(callsign: str, request:Request):
beacons = DatabaseManagerBeacon(request.app.event_manager).get_beacons_by_callsign(callsign)
return api_response(beacons)
@router.get("/station/{callsign}", summary="Get Station Info", tags=["FreeDATA"], responses={
200: {
"description": "Retrieve station information by callsign.",
"content": {
"application/json": {
"example": {
"callsign": "MYCALL-0",
"location": "Springfield",
"frequency": "14093000",
"mode": "PKTUSB",
"status": "active",
"additional_info": "Station details here."
}
}
}
},
400: {
"description": "Bad Request: The request was malformed or missing required parameters.",
"content": {
"application/json": {
"example": {
"error": "Invalid callsign parameter."
}
}
}
},
404: {
"description": "The requested station was not found.",
"content": {
"application/json": {
"example": {
"error": "Station not found."
}
}
}
},
500: {
"description": "Internal Server Error: An unexpected error occurred on the server.",
"content": {
"application/json": {
"example": {
"error": "Internal server error."
}
}
}
},
503: {
"description": "Modem not running.",
"content": {
"application/json": {
"example": {
"error": "Modem not running."
}
}
}
}
})
async def get_station_info(callsign: str, request: Request):
station = DatabaseManagerStations(request.app.event_manager).get_station(callsign)
return api_response(station)
@router.post("/station/{callsign}", summary="Set Station Info", tags=["FreeDATA"], responses={
200: {
"description": "Station information updated successfully.",
"content": {
"application/json": {
"example": {
"callsign": "MYCALL-0",
"location": "Springfield",
"frequency": "14093000",
"mode": "PKTUSB",
"status": "active",
"additional_info": "Updated station details."
}
}
}
},
400: {
"description": "Bad Request: The request was malformed or missing required parameters.",
"content": {
"application/json": {
"example": {
"error": "Invalid input data."
}
}
}
},
404: {
"description": "The requested station was not found.",
"content": {
"application/json": {
"example": {
"error": "Station not found."
}
}
}
},
500: {
"description": "Internal Server Error: An unexpected error occurred on the server.",
"content": {
"application/json": {
"example": {
"error": "Internal server error."
}
}
}
},
503: {
"description": "Modem not running.",
"content": {
"application/json": {
"example": {
"error": "Modem not running."
}
}
}
}
})
async def set_station_info(callsign: str, request: Request):
data = await request.json()
result = DatabaseManagerStations(request.app.event_manager).update_station_info(callsign, new_info=data["info"])
return api_response(result)