From addddd85f188e201d690789406212fd92d53cdf0 Mon Sep 17 00:00:00 2001 From: TX-RX Date: Sat, 23 May 2026 12:10:51 -0500 Subject: [PATCH 1/8] fix(api): case-insensitive sort on string columns in paginate helper The paginate() helper centralizes server-side sorting for every paginated endpoint (Users, Groups, EUDs, Missions, Tokens, Markers, etc.). Postgres default collation is byte-ordered, so a username sort by ascending puts 'ALICE', 'BOB', 'Charlie' all before 'alice', 'bob' -- making it hard to find a user by name when case is uncertain. For string columns (String/Text/Unicode/UnicodeText), wrap the sort expression with func.lower() so 'alice', 'Bob', 'charlie', 'DAVID' sort in natural mixed-case order. Non-string columns (timestamps, IDs, etc.) keep their existing behavior. Co-Authored-By: Claude Opus 4.7 (1M context) --- opentakserver/blueprints/ots_api/api.py | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/opentakserver/blueprints/ots_api/api.py b/opentakserver/blueprints/ots_api/api.py index 9853f062..099bd2d5 100644 --- a/opentakserver/blueprints/ots_api/api.py +++ b/opentakserver/blueprints/ots_api/api.py @@ -18,7 +18,7 @@ from flask_babel import gettext from flask_ldap3_login import AuthenticationResponseStatus from flask_security import auth_required, current_user, verify_password -from sqlalchemy import select +from sqlalchemy import String, Text, Unicode, UnicodeText, func, select from opentakserver import __version__ as version from opentakserver.certificate_authority import CertificateAuthority @@ -68,10 +68,22 @@ def paginate(query: db.Query, model=None): if model: sort_by = request.args.get("sort_by") sort_direction = request.args.get("sort_direction") - if sort_by and (sort_direction == "asc" or not sort_direction): - query = query.order_by(getattr(model, sort_by).asc()) - elif sort_by and sort_direction == "desc": - query = query.order_by(getattr(model, sort_by).desc()) + if sort_by: + column = getattr(model, sort_by) + # Sort string columns case-insensitively so "alice" sorts next + # to "Alice" instead of after every uppercase entry (Postgres + # default collation is byte-ordered: Z < a). + try: + is_string_col = isinstance( + column.type, (String, Text, Unicode, UnicodeText) + ) + except AttributeError: + is_string_col = False + sort_expr = func.lower(column) if is_string_col else column + if sort_direction == "desc": + query = query.order_by(sort_expr.desc()) + else: + query = query.order_by(sort_expr.asc()) except BaseException as e: return ( jsonify( From e99a87c3a5b290b8bda5e8e44c9b99e212de61f4 Mon Sep 17 00:00:00 2001 From: TX-RX Date: Sun, 24 May 2026 20:28:35 -0500 Subject: [PATCH 2/8] fix(mission-api): correct typo in mission contents DELETE route The route was registered as `/Marti/api/missions/gui//contents` (missing the `d` in `guid`), so any DELETE request using the guid-form URL returned 405 Method Not Allowed. The companion routes (PUT, GET, etc.) on the same handler all use the correct `/guid/` segment, and the handler itself accepts `mission_guid`, confirming the typo was unintentional. Clients that follow the TAK Server Marti API spec (e.g. CloudTAK via node-tak's `Mission.detachContents`) only emit the guid form, so removing individual CoTs from a mission via the API silently failed for all such clients prior to this fix. --- opentakserver/blueprints/marti_api/mission_marti_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opentakserver/blueprints/marti_api/mission_marti_api.py b/opentakserver/blueprints/marti_api/mission_marti_api.py index 7c09f8a8..1cfe616d 100644 --- a/opentakserver/blueprints/marti_api/mission_marti_api.py +++ b/opentakserver/blueprints/marti_api/mission_marti_api.py @@ -2041,7 +2041,7 @@ def mission_contents(mission_name: str | None = None, mission_guid: str | None = ) -@mission_marti_api.route("/Marti/api/missions/gui//contents", methods=["DELETE"]) +@mission_marti_api.route("/Marti/api/missions/guid//contents", methods=["DELETE"]) @mission_marti_api.route("/Marti/api/missions//contents", methods=["DELETE"]) def delete_content(mission_name: str | None = None, mission_guid: str | None = None): if "iTAK" not in request.user_agent.string: From c7cdd06083e400e16e2c9bd6fa6c432a153d25e1 Mon Sep 17 00:00:00 2001 From: Brian Date: Tue, 26 May 2026 23:09:29 -0400 Subject: [PATCH 3/8] Fixed an import error in eud_handler.py --- opentakserver/eud_handler/eud_handler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opentakserver/eud_handler/eud_handler.py b/opentakserver/eud_handler/eud_handler.py index 314c721b..b82993bf 100644 --- a/opentakserver/eud_handler/eud_handler.py +++ b/opentakserver/eud_handler/eud_handler.py @@ -17,7 +17,7 @@ from opentakserver.EmailValidator import EmailValidator from opentakserver.PasswordValidator import PasswordValidator from opentakserver.defaultconfig import DefaultConfig -from opentakserver.eud_handler import EudHandler +from opentakserver.eud_handler.EudHandler import EudHandler from opentakserver.eud_handler.EudHandlerSSL import EudHandlerSSL from opentakserver.eud_handler.EudServer import EudServer from opentakserver.eud_handler.EudServerSSL import EudServerSSL From 8580ab7344e36a1a8342d4231207535f3a76b127 Mon Sep 17 00:00:00 2001 From: Brian Date: Mon, 8 Jun 2026 21:21:07 -0400 Subject: [PATCH 4/8] Added Hungarian --- .../translations/hu/LC_MESSAGES/messages.po | 728 ++++++++++++++++++ 1 file changed, 728 insertions(+) create mode 100644 opentakserver/translations/hu/LC_MESSAGES/messages.po diff --git a/opentakserver/translations/hu/LC_MESSAGES/messages.po b/opentakserver/translations/hu/LC_MESSAGES/messages.po new file mode 100644 index 00000000..c9f059ca --- /dev/null +++ b/opentakserver/translations/hu/LC_MESSAGES/messages.po @@ -0,0 +1,728 @@ +# Hungarian translations for PROJECT. +# Copyright (C) 2026 ORGANIZATION +# This file is distributed under the same license as the PROJECT project. +# FIRST AUTHOR , 2026. +# +msgid "" +msgstr "" +"Project-Id-Version: PROJECT VERSION\n" +"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" +"POT-Creation-Date: 2026-06-08 21:20-0400\n" +"PO-Revision-Date: 2026-06-08 21:20-0400\n" +"Last-Translator: FULL NAME \n" +"Language: hu\n" +"Language-Team: hu \n" +"Plural-Forms: nplurals=1; plural=0;\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.17.0\n" + +#: PasswordValidator.py:13 blueprints/ots_api/user_api.py:195 +msgid "Passwords should not include @ or : characters" +msgstr "" + +#: blueprints/marti_api/cot_marti_api.py:32 +#, python-format +msgid "No CoT found for UID %(uid)s" +msgstr "" + +#: blueprints/marti_api/cot_marti_api.py:57 +#, python-format +msgid "Invalid secago value: %(sec_ago)s" +msgstr "" + +#: blueprints/marti_api/data_package_marti_api.py:92 +msgid "This data package has already been uploaded" +msgstr "" + +#: blueprints/marti_api/data_package_marti_api.py:181 +msgid "A file is required" +msgstr "" + +#: blueprints/marti_api/data_package_marti_api.py:201 +#, python-format +msgid "Invalid file extension: %(extension)s" +msgstr "" + +#: blueprints/marti_api/data_package_marti_api.py:385 +#, python-format +msgid "File not found: %(filename)s}" +msgstr "" + +#: blueprints/marti_api/group_marti_api.py:40 +msgid "Groups are only supported on SSL connections" +msgstr "" + +#: blueprints/marti_api/group_marti_api.py:109 +#: blueprints/marti_api/group_marti_api.py:167 +msgid "Please specify a groupNameFilter" +msgstr "" + +#: blueprints/marti_api/group_marti_api.py:121 +#: blueprints/marti_api/group_marti_api.py:179 +#, python-format +msgid "Please specify a groupNameFilter that starts with %(prefix)s" +msgstr "" + +#: blueprints/marti_api/group_marti_api.py:134 +#: blueprints/marti_api/group_marti_api.py:192 +msgid "groupNameFilter must end with either _READ or _WRITE" +msgstr "" + +#: blueprints/marti_api/group_marti_api.py:294 +#: blueprints/ots_api/group_api.py:365 blueprints/ots_api/user_api.py:587 +msgid "Direction must be IN or OUT" +msgstr "" + +#: blueprints/marti_api/group_marti_api.py:303 +msgid "The active attribute must be true or false" +msgstr "" + +#: blueprints/marti_api/group_marti_api.py:312 +msgid "Group name is required" +msgstr "" + +#: blueprints/marti_api/group_marti_api.py:355 +#, python-format +msgid "%(username)s is not in the %(group_name)s group" +msgstr "" + +#: blueprints/marti_api/group_marti_api.py:377 +#, python-format +msgid "Failed to update group subscriptions for %(username)s: %(e)s" +msgstr "" + +#: blueprints/marti_api/group_marti_api.py:406 +msgid "Please provide a group name and direction" +msgstr "" + +#: blueprints/marti_api/group_marti_api.py:428 +#, python-format +msgid "No group found: %(group_name)s, %(direction)s" +msgstr "" + +#: blueprints/marti_api/marti_api.py:107 +#, python-format +msgid "No such UID: %(uid)s" +msgstr "" + +#: blueprints/marti_api/mission_marti_api.py:324 +#: blueprints/marti_api/mission_marti_api.py:1268 +#, python-format +msgid "No mission found with guid: %(mission_guid)s" +msgstr "" + +#: blueprints/marti_api/mission_marti_api.py:333 +#: blueprints/marti_api/mission_marti_api.py:1467 +#: blueprints/ots_api/mission_api.py:415 +msgid "Invalid password" +msgstr "" + +#: blueprints/marti_api/mission_marti_api.py:450 +msgid "Missions are only supported on SSL connections" +msgstr "" + +#: blueprints/marti_api/mission_marti_api.py:464 +msgid "Please provide a mission name and creatorUid" +msgstr "" + +#: blueprints/marti_api/mission_marti_api.py:478 +#, python-format +msgid "Invalid creatorUid: %(creator_uid)s" +msgstr "" + +#: blueprints/marti_api/mission_marti_api.py:529 +#, python-format +msgid "Group not found: %(group_name)s" +msgstr "" + +#: blueprints/marti_api/mission_marti_api.py:615 +#: blueprints/ots_api/mission_api.py:197 +#, python-format +msgid "Failed to add mission: %(e)s" +msgstr "" + +#: blueprints/marti_api/mission_marti_api.py:643 +msgid "Invalid mission name" +msgstr "" + +#: blueprints/marti_api/mission_marti_api.py:655 +#: blueprints/marti_api/mission_marti_api.py:746 +#: blueprints/marti_api/mission_marti_api.py:823 +#: blueprints/marti_api/mission_marti_api.py:952 +#: blueprints/marti_api/mission_marti_api.py:1662 +#: blueprints/marti_api/mission_marti_api.py:2075 +#: blueprints/ots_api/mission_api.py:311 +#, python-format +msgid "Mission %(mission_name)s not found" +msgstr "" + +#: blueprints/marti_api/mission_marti_api.py:686 +#: blueprints/marti_api/mission_marti_api.py:760 +#: blueprints/marti_api/mission_marti_api.py:1129 +#: blueprints/marti_api/mission_marti_api.py:1526 +#: blueprints/marti_api/mission_marti_api.py:2050 +#: blueprints/marti_api/mission_marti_api.py:2212 +msgid "Missing or invalid token" +msgstr "" + +#: blueprints/marti_api/mission_marti_api.py:710 +msgid "Only mission owners can delete missions" +msgstr "" + +#: blueprints/marti_api/mission_marti_api.py:770 +msgid "Please provide the password" +msgstr "" + +#: blueprints/marti_api/mission_marti_api.py:781 +msgid "You do not have permission to change this mission's password" +msgstr "" + +#: blueprints/marti_api/mission_marti_api.py:842 +#, python-format +msgid "No EUD found with UID %(invitee)s" +msgstr "" + +#: blueprints/marti_api/mission_marti_api.py:857 +#, python-format +msgid "No EUD found with callsign %(invitee)s" +msgstr "" + +#: blueprints/marti_api/mission_marti_api.py:873 +#, python-format +msgid "No user found with username %(invitee)s" +msgstr "" + +#: blueprints/marti_api/mission_marti_api.py:889 +#, python-format +msgid "No group found: %(invitee)s" +msgstr "" + +#: blueprints/marti_api/mission_marti_api.py:904 +#, python-format +msgid "Team not found: %(invitee)s" +msgstr "" + +#: blueprints/marti_api/mission_marti_api.py:967 +#, python-format +msgid "Invalid invitation type: %(invitation_type)s" +msgstr "" + +#: blueprints/marti_api/mission_marti_api.py:1015 +#: blueprints/marti_api/mission_marti_api.py:1603 +#: blueprints/ots_api/mission_api.py:391 +#, python-format +msgid "Mission not found: %(mission_name)s" +msgstr "" + +#: blueprints/marti_api/mission_marti_api.py:1053 +#, python-format +msgid "Invalid invitation type: %(invitation)s" +msgstr "" + +#: blueprints/marti_api/mission_marti_api.py:1145 +#, python-format +msgid "No such mission found: %(mission_name)s" +msgstr "" + +#: blueprints/marti_api/mission_marti_api.py:1161 +msgid "Only mission owners can change EUD roles" +msgstr "" + +#: blueprints/marti_api/mission_marti_api.py:1168 +#: blueprints/ots_api/marker_api.py:86 +msgid "Please provide a UID" +msgstr "" + +#: blueprints/marti_api/mission_marti_api.py:1176 +#, python-format +msgid "Invalid UID: %(client_uid)s" +msgstr "" + +#: blueprints/marti_api/mission_marti_api.py:1190 +#, python-format +msgid "Invalid role: %(new_role)s" +msgstr "" + +#: blueprints/marti_api/mission_marti_api.py:1321 +#: blueprints/marti_api/mission_marti_api.py:1364 +#, python-format +msgid "Cannot find mission %(mission_name)s" +msgstr "" + +#: blueprints/marti_api/mission_marti_api.py:1376 +#, python-format +msgid "Cannot find mission %(mission_guid)s" +msgstr "" + +#: blueprints/marti_api/mission_marti_api.py:1402 +#, python-format +msgid "%(username)s and mission %(mission_name)s are not in the same group" +msgstr "" + +#: blueprints/marti_api/mission_marti_api.py:1421 +#: blueprints/ots_api/mediamtx_api.py:58 +msgid "Invalid token" +msgstr "" + +#: blueprints/marti_api/mission_marti_api.py:1454 +msgid "Missing UID" +msgstr "" + +#: blueprints/marti_api/mission_marti_api.py:1460 +#, python-format +msgid "Invalid UID: %(uid)s" +msgstr "" + +#: blueprints/marti_api/mission_marti_api.py:1694 +msgid "Missing or invalid certificate" +msgstr "" + +#: blueprints/marti_api/mission_marti_api.py:1710 +msgid "File name cannot be blank" +msgstr "" + +#: blueprints/marti_api/mission_marti_api.py:1744 +#, python-format +msgid "%(extension)s is not an allowed file extension" +msgstr "" + +#: blueprints/marti_api/mission_marti_api.py:1820 +#, python-format +msgid "No content found with hash: %(content_hash)s" +msgstr "" + +#: blueprints/marti_api/mission_marti_api.py:1865 +#, python-format +msgid "No such mission: %(mission_name)s" +msgstr "" + +#: blueprints/marti_api/mission_marti_api.py:1887 +#, python-format +msgid "No such file with hash %(content_hash)s" +msgstr "" + +#: blueprints/marti_api/mission_marti_api.py:2103 +#, python-format +msgid "UID %(uid)s not found" +msgstr "" + +#: blueprints/marti_api/mission_marti_api.py:2133 +#, python-format +msgid "No content found with hash %(hash)s" +msgstr "" + +#: blueprints/marti_api/mission_marti_api.py:2160 +#, python-format +msgid "Failed to delete content with hash %(hash)s: %(e)s" +msgstr "" + +#: blueprints/marti_api/video_marti_api.py:146 +msgid "Invalid Certificate" +msgstr "" + +#: blueprints/marti_api/video_marti_api.py:209 +msgid "Video not found" +msgstr "" + +#: blueprints/marti_api/video_marti_api.py:216 +#: blueprints/ots_api/group_api.py:209 blueprints/ots_api/user_api.py:521 +#, python-format +msgid "User %(username)s not found" +msgstr "" + +#: blueprints/marti_api/video_marti_api.py:233 +#, python-format +msgid "Video stream with uid %(uid)s not found" +msgstr "" + +#: blueprints/ots_api/api.py:93 +#, python-format +msgid "Invalid sort column: %(sort_by)s" +msgstr "" + +#: blueprints/ots_api/api.py:294 +#, python-format +msgid "Invalid username: %(username)s" +msgstr "" + +#: blueprints/ots_api/api.py:336 +#, python-format +msgid "Certificate already exists for %(username)s" +msgstr "" + +#: blueprints/ots_api/api.py:371 +msgid "Please specify a callsign" +msgstr "" + +#: blueprints/ots_api/casevac_api.py:134 +#, python-format +msgid "Unknown UID: %(uid)s" +msgstr "" + +#: blueprints/ots_api/data_package_api.py:48 +msgid "" +"Server connection data packages can't be installed on enrollment or " +"connection" +msgstr "" + +#: blueprints/ots_api/data_package_api.py:83 +msgid "Invalid/unknown hash" +msgstr "" + +#: blueprints/ots_api/data_package_api.py:134 +msgid "Please provide a data package hash" +msgstr "" + +#: blueprints/ots_api/data_package_api.py:150 +#, python-format +msgid "Data package with hash '%(hash)s' not found" +msgstr "" + +#: blueprints/ots_api/device_profile_api.py:68 +msgid "Please specify the preference_key" +msgstr "" + +#: blueprints/ots_api/device_profile_api.py:82 +#, python-format +msgid "Unknown preference_key: %(preference_key)s" +msgstr "" + +#: blueprints/ots_api/group_api.py:42 blueprints/ots_api/group_api.py:125 +#: blueprints/ots_api/group_api.py:166 +msgid "LDAP is enabled. Please view and edit groups on your LDAP server" +msgstr "" + +#: blueprints/ots_api/group_api.py:182 +msgid "Please provide the username, group name, and direction" +msgstr "" + +#: blueprints/ots_api/group_api.py:197 +#, python-format +msgid "Invalid direction: %(direction)s" +msgstr "" + +#: blueprints/ots_api/group_api.py:217 +#, python-format +msgid "Group %(group_name)s not found" +msgstr "" + +#: blueprints/ots_api/group_api.py:250 +#, python-format +msgid "Failed to remove %(username)s from %(group_name)s: %(e)s" +msgstr "" + +#: blueprints/ots_api/group_api.py:274 +msgid "LDAP is enabled, please use your LDAP server to add groups" +msgstr "" + +#: blueprints/ots_api/group_api.py:281 +msgid "Missing name" +msgstr "" + +#: blueprints/ots_api/group_api.py:303 +#, python-format +msgid "%(name)s group already exists" +msgstr "" + +#: blueprints/ots_api/group_api.py:315 +#, python-format +msgid "Failed to add %(name)s group: %(e)s" +msgstr "" + +#: blueprints/ots_api/group_api.py:342 blueprints/ots_api/user_api.py:562 +msgid "LDAP is enabled, please use your LDAP server to add users to groups" +msgstr "" + +#: blueprints/ots_api/group_api.py:358 +msgid "Please provide a list of users, group name, and direction" +msgstr "" + +#: blueprints/ots_api/group_api.py:374 blueprints/ots_api/user_api.py:249 +#: blueprints/ots_api/user_api.py:299 blueprints/ots_api/user_api.py:365 +#: blueprints/ots_api/user_api.py:403 +#, python-format +msgid "User %(username)s does not exist" +msgstr "" + +#: blueprints/ots_api/group_api.py:387 +#, python-format +msgid "Group %(group_name)s does not exist" +msgstr "" + +#: blueprints/ots_api/group_api.py:417 +msgid "LDAP is enabled, please use your LDAP server to delete groups" +msgstr "" + +#: blueprints/ots_api/group_api.py:425 +msgid "Missing group name" +msgstr "" + +#: blueprints/ots_api/group_api.py:430 +msgid "The __ANON__ group cannot be deleted" +msgstr "" + +#: blueprints/ots_api/group_api.py:442 +#, python-format +msgid "No such group: %(group_name)s" +msgstr "" + +#: blueprints/ots_api/group_api.py:463 +#, python-format +msgid "Failed to delete %(group_name)s: %(e)s" +msgstr "" + +#: blueprints/ots_api/language_api.py:13 +msgid "This is a test" +msgstr "" + +#: blueprints/ots_api/marker_api.py:48 +msgid "Please provide a latitude and longitude" +msgstr "" + +#: blueprints/ots_api/marker_api.py:58 +#, python-format +msgid "Invalid latitude: %(latitude)s" +msgstr "" + +#: blueprints/ots_api/marker_api.py:70 +#, python-format +msgid "Invalid longitude: %(longitude)s" +msgstr "" + +#: blueprints/ots_api/marker_api.py:80 +#, python-format +msgid "Failed to parse lat/lon: %(e)s" +msgstr "" + +#: blueprints/ots_api/marker_api.py:88 +msgid "Please provide a name" +msgstr "" + +#: blueprints/ots_api/marker_api.py:96 +msgid "Invalid UID. UIDs need to be in UUID4 format" +msgstr "" + +#: blueprints/ots_api/marker_api.py:112 +#, python-format +msgid "Invalid type: %(cot_type)s" +msgstr "" + +#: blueprints/ots_api/marker_api.py:269 +#, python-format +msgid "Failed to parse data: %(e)s" +msgstr "" + +#: blueprints/ots_api/marker_api.py:283 +msgid "Please provide the UID of the marker to delete" +msgstr "" + +#: blueprints/ots_api/marker_api.py:294 +msgid "Unknown UID" +msgstr "" + +#: blueprints/ots_api/mediamtx_api.py:305 +#: blueprints/ots_api/mediamtx_api.py:404 +msgid "Please specify a path name" +msgstr "" + +#: blueprints/ots_api/mediamtx_api.py:309 +msgid "Path cannot begin with a slash" +msgstr "" + +#: blueprints/ots_api/mediamtx_api.py:327 +#: blueprints/ots_api/mediamtx_api.py:413 +#, python-format +msgid "Path %(path)s not found" +msgstr "" + +#: blueprints/ots_api/mediamtx_api.py:421 +msgid "MediaMTX is not running" +msgstr "" + +#: blueprints/ots_api/meshtastic_api.py:167 +msgid "Please provide the URL of the channel to delete" +msgstr "" + +#: blueprints/ots_api/mission_api.py:163 blueprints/ots_api/mission_api.py:283 +#, python-format +msgid "Invalid property: %(key)s" +msgstr "" + +#: blueprints/ots_api/mission_api.py:255 +msgid "Only an admin or the mission creator can change this mission" +msgstr "" + +#: blueprints/ots_api/mission_api.py:300 +msgid "Please specify a mission name" +msgstr "" + +#: blueprints/ots_api/mission_api.py:406 +msgid "Please provide the mission password" +msgstr "" + +#: blueprints/ots_api/plugin_api.py:51 +msgid "Server plugins must be zip, whl, or tar.gz files" +msgstr "" + +#: blueprints/ots_api/plugin_api.py:66 blueprints/ots_api/plugin_api.py:99 +#: blueprints/ots_api/plugin_api.py:115 blueprints/ots_api/plugin_api.py:130 +msgid "Plugins are disabled" +msgstr "" + +#: blueprints/ots_api/plugin_api.py:92 +#, python-format +msgid "Plugin %(plugin_name)s not found" +msgstr "" + +#: blueprints/ots_api/token_api.py:194 +#, python-format +msgid "No token found for %(username)s" +msgstr "" + +#: blueprints/ots_api/token_api.py:225 +#, python-format +msgid "Failed to delete token: %(e)s" +msgstr "" + +#: blueprints/ots_api/user_api.py:37 +msgid "LDAP is enabled, please use your LDAP server to create users" +msgstr "" + +#: blueprints/ots_api/user_api.py:53 +msgid "Passwords do not match" +msgstr "" + +#: blueprints/ots_api/user_api.py:67 blueprints/ots_api/user_api.py:351 +#, python-format +msgid "Role %(role)s does not exist" +msgstr "" + +#: blueprints/ots_api/user_api.py:78 +msgid "Only administrators can add users to the administrators role" +msgstr "" + +#: blueprints/ots_api/user_api.py:101 +#, python-format +msgid "User %(username)s already exists" +msgstr "" + +#: blueprints/ots_api/user_api.py:117 +msgid "LDAP is enabled, please use your LDAP server to delete users" +msgstr "" + +#: blueprints/ots_api/user_api.py:128 +msgid "You can't delete your own account" +msgstr "" + +#: blueprints/ots_api/user_api.py:141 +#, python-format +msgid "Failed to delete user: %(e)s" +msgstr "" + +#: blueprints/ots_api/user_api.py:158 +msgid "LDAP is enabled, please use your LDAP server to reset passwords" +msgstr "" + +#: blueprints/ots_api/user_api.py:171 +msgid "Please specify a username and new password" +msgstr "" + +#: blueprints/ots_api/user_api.py:182 +#, python-format +msgid "Your password must be at least %(characters)s characters long" +msgstr "" + +#: blueprints/ots_api/user_api.py:211 +#, python-format +msgid "Could not find user %(username)s" +msgstr "" + +#: blueprints/ots_api/user_api.py:227 +msgid "LDAP is enabled, please use your LDAP server to deactivate users" +msgstr "" + +#: blueprints/ots_api/user_api.py:238 +msgid "Please specify the username to deactivate" +msgstr "" + +#: blueprints/ots_api/user_api.py:263 +#, python-format +msgid "%(username)s is already deactivated" +msgstr "" + +#: blueprints/ots_api/user_api.py:277 +msgid "LDAP is enabled, please use your LDAP server to activate users" +msgstr "" + +#: blueprints/ots_api/user_api.py:288 +msgid "Please specify the username to activate" +msgstr "" + +#: blueprints/ots_api/user_api.py:313 +#, python-format +msgid "%(username)s is already activated" +msgstr "" + +#: blueprints/ots_api/user_api.py:327 +msgid "LDAP is enabled, please use your LDAP server to assign roles" +msgstr "" + +#: blueprints/ots_api/user_api.py:340 +msgid "Please specify a username and roles" +msgstr "" + +#: blueprints/ots_api/user_api.py:414 +#, python-format +msgid "EUD %(eud_uid)s not found" +msgstr "" + +#: blueprints/ots_api/user_api.py:427 +#, python-format +msgid "%(uid)s is already assigned to another user" +msgstr "" + +#: blueprints/ots_api/user_api.py:449 blueprints/ots_api/user_api.py:471 +msgid "LDAP is enabled, please use your LDAP server to manage users" +msgstr "" + +#: blueprints/ots_api/user_api.py:502 +msgid "LDAP is enabled, please use your LDAP server to manage groups" +msgstr "" + +#: blueprints/ots_api/user_api.py:511 +msgid "Please provide a username" +msgstr "" + +#: blueprints/ots_api/user_api.py:579 +msgid "Please provide a list of groups, a username, and a direction" +msgstr "" + +#: blueprints/ots_api/user_api.py:595 +#, python-format +msgid "User %(username)s doesn't exist" +msgstr "" + +#: blueprints/ots_api/user_api.py:610 +#, python-format +msgid "Group %(group_name)s doesn't exist" +msgstr "" + +#: blueprints/ots_api/video_api.py:46 +msgid "Please specify a valid path" +msgstr "" + +#: blueprints/ots_api/video_api.py:73 +#, python-format +msgid "Invalid recording_id: %(recording_id)s" +msgstr "" + +#: blueprints/ots_api/video_api.py:97 +msgid "Recording not found" +msgstr "" + +#: eud_handler/eud_handler.py:34 +msgid "UDP Server" +msgstr "" + From d56d94a76c4d885504ecc944c40d02875c1aa840 Mon Sep 17 00:00:00 2001 From: Brian Date: Mon, 8 Jun 2026 21:33:04 -0400 Subject: [PATCH 5/8] Added Hungarian to the default config --- opentakserver/defaultconfig.py | 1 + 1 file changed, 1 insertion(+) diff --git a/opentakserver/defaultconfig.py b/opentakserver/defaultconfig.py index ed881b18..65d7b932 100644 --- a/opentakserver/defaultconfig.py +++ b/opentakserver/defaultconfig.py @@ -28,6 +28,7 @@ class DefaultConfig: "CN": {"name": "简化字", "language_code": "zh_Hans"}, "SE": {"name": "Svenska", "language_code": "sv"}, "TH": {"name": "ภาษาไทย", "language_code": "th"}, + "HU": {"name": "Magyar", "language_code": "hu"}, } OTS_DATA_FOLDER = os.getenv("OTS_DATA_FOLDER", os.path.join(Path.home(), "ots")) From a87b89b6ece18acf986b8a003746201ba621f42f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Visnitz=20Tam=C3=A1s?= Date: Tue, 9 Jun 2026 10:38:09 +0000 Subject: [PATCH 6/8] Translated using Weblate (Hungarian) Currently translated at 10.6% (16 of 150 strings) Translation: OpenTAKServer/OpenTAKServer Translate-URL: https://weblate.opentakserver.io/projects/opentakserver/opentakserver/hu/ --- .../translations/hu/LC_MESSAGES/messages.po | 43 +++++++++++-------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/opentakserver/translations/hu/LC_MESSAGES/messages.po b/opentakserver/translations/hu/LC_MESSAGES/messages.po index c9f059ca..e7249df0 100644 --- a/opentakserver/translations/hu/LC_MESSAGES/messages.po +++ b/opentakserver/translations/hu/LC_MESSAGES/messages.po @@ -8,90 +8,96 @@ msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2026-06-08 21:20-0400\n" -"PO-Revision-Date: 2026-06-08 21:20-0400\n" -"Last-Translator: FULL NAME \n" +"PO-Revision-Date: 2026-06-09 11:48+0000\n" +"Last-Translator: Visnitz Tamás \n" +"Language-Team: Hungarian \n" "Language: hu\n" -"Language-Team: hu \n" -"Plural-Forms: nplurals=1; plural=0;\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Weblate 5.13.3\n" "Generated-By: Babel 2.17.0\n" #: PasswordValidator.py:13 blueprints/ots_api/user_api.py:195 msgid "Passwords should not include @ or : characters" -msgstr "" +msgstr "A jelszavak nem tartalmazhatnak @ or : characters" #: blueprints/marti_api/cot_marti_api.py:32 #, python-format msgid "No CoT found for UID %(uid)s" msgstr "" +"Nem található Cursor on Target (CoT) adat a megadott egyedi azonosítóhoz " +"(UID) %(uid)s" #: blueprints/marti_api/cot_marti_api.py:57 #, python-format msgid "Invalid secago value: %(sec_ago)s" -msgstr "" +msgstr "Érvénytelen idő érték: %(sec_ago)s" #: blueprints/marti_api/data_package_marti_api.py:92 msgid "This data package has already been uploaded" -msgstr "" +msgstr "Ez az adatcsomag már fel van töltve" #: blueprints/marti_api/data_package_marti_api.py:181 msgid "A file is required" -msgstr "" +msgstr "Egy fájl megadása szükséges" #: blueprints/marti_api/data_package_marti_api.py:201 #, python-format msgid "Invalid file extension: %(extension)s" -msgstr "" +msgstr "Érvénytelen fájlkiterjesztés (%(extension)s)" #: blueprints/marti_api/data_package_marti_api.py:385 #, python-format msgid "File not found: %(filename)s}" -msgstr "" +msgstr "%(filename)s Fájl nem található" #: blueprints/marti_api/group_marti_api.py:40 msgid "Groups are only supported on SSL connections" -msgstr "" +msgstr "A csoportok kizárólag SSL-kapcsolattal támogatottak (%(fájlnév)s)" #: blueprints/marti_api/group_marti_api.py:109 #: blueprints/marti_api/group_marti_api.py:167 msgid "Please specify a groupNameFilter" -msgstr "" +msgstr "Kérjük, adjon meg egy csoportnév-szűrőt" #: blueprints/marti_api/group_marti_api.py:121 #: blueprints/marti_api/group_marti_api.py:179 #, python-format msgid "Please specify a groupNameFilter that starts with %(prefix)s" -msgstr "" +msgstr "Kérjük, adjon meg egy %(prefix)s előtaggal kezdődő Csoportnév szűrőt" #: blueprints/marti_api/group_marti_api.py:134 #: blueprints/marti_api/group_marti_api.py:192 msgid "groupNameFilter must end with either _READ or _WRITE" msgstr "" +"A Csoportnév szűrő karakterláncnak _READ vagy _WRITE karakterlánccal kell " +"végződnie" #: blueprints/marti_api/group_marti_api.py:294 #: blueprints/ots_api/group_api.py:365 blueprints/ots_api/user_api.py:587 msgid "Direction must be IN or OUT" -msgstr "" +msgstr "Az iránynak BE vagy KI-nek kell lennie" #: blueprints/marti_api/group_marti_api.py:303 msgid "The active attribute must be true or false" -msgstr "" +msgstr "Az active attribútumnak igaznak vagy hamisnak kell lennie" #: blueprints/marti_api/group_marti_api.py:312 msgid "Group name is required" -msgstr "" +msgstr "Csoportnév megadása kötelező" #: blueprints/marti_api/group_marti_api.py:355 #, python-format msgid "%(username)s is not in the %(group_name)s group" -msgstr "" +msgstr "%(username)s nincs a(z) %(group_name)s csoportban" #: blueprints/marti_api/group_marti_api.py:377 #, python-format msgid "Failed to update group subscriptions for %(username)s: %(e)s" -msgstr "" +msgstr "Nem sikerült frissíteni a(z) %(username)s csoporttagságait: %(e)s" #: blueprints/marti_api/group_marti_api.py:406 msgid "Please provide a group name and direction" @@ -725,4 +731,3 @@ msgstr "" #: eud_handler/eud_handler.py:34 msgid "UDP Server" msgstr "" - From 1f91916f93a7cdeb7639423b476d3336f945ad13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Visnitz=20Tam=C3=A1s?= Date: Tue, 9 Jun 2026 13:34:52 +0000 Subject: [PATCH 7/8] Translated using Weblate (Hungarian) Currently translated at 46.0% (69 of 150 strings) Translation: OpenTAKServer/OpenTAKServer Translate-URL: https://weblate.opentakserver.io/projects/opentakserver/opentakserver/hu/ --- .../translations/hu/LC_MESSAGES/messages.po | 107 ++++++++++-------- 1 file changed, 58 insertions(+), 49 deletions(-) diff --git a/opentakserver/translations/hu/LC_MESSAGES/messages.po b/opentakserver/translations/hu/LC_MESSAGES/messages.po index e7249df0..ce65e21a 100644 --- a/opentakserver/translations/hu/LC_MESSAGES/messages.po +++ b/opentakserver/translations/hu/LC_MESSAGES/messages.po @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2026-06-08 21:20-0400\n" -"PO-Revision-Date: 2026-06-09 11:48+0000\n" +"PO-Revision-Date: 2026-06-09 14:05+0000\n" "Last-Translator: Visnitz Tamás \n" "Language-Team: Hungarian \n" @@ -101,57 +101,57 @@ msgstr "Nem sikerült frissíteni a(z) %(username)s csoporttagságait: %(e)s" #: blueprints/marti_api/group_marti_api.py:406 msgid "Please provide a group name and direction" -msgstr "" +msgstr "Kérjük, adja meg a csoport nevét és a kommunikációs irányt" #: blueprints/marti_api/group_marti_api.py:428 #, python-format msgid "No group found: %(group_name)s, %(direction)s" -msgstr "" +msgstr "Nem található csoport: %(group_name)s, %(direction)s" #: blueprints/marti_api/marti_api.py:107 #, python-format msgid "No such UID: %(uid)s" -msgstr "" +msgstr "Nincs egyező azonosító (UID): %(uid)s" #: blueprints/marti_api/mission_marti_api.py:324 #: blueprints/marti_api/mission_marti_api.py:1268 #, python-format msgid "No mission found with guid: %(mission_guid)s" -msgstr "" +msgstr "Nem találhatók küldetések a következő leírással: %(mission_guid)s" #: blueprints/marti_api/mission_marti_api.py:333 #: blueprints/marti_api/mission_marti_api.py:1467 #: blueprints/ots_api/mission_api.py:415 msgid "Invalid password" -msgstr "" +msgstr "Hibás jelszó" #: blueprints/marti_api/mission_marti_api.py:450 msgid "Missions are only supported on SSL connections" -msgstr "" +msgstr "A küldetések csak SSL-kapcsolattal támogatottak" #: blueprints/marti_api/mission_marti_api.py:464 msgid "Please provide a mission name and creatorUid" -msgstr "" +msgstr "Kérjük, adjon meg egy küldetésnevet és egy készítői azonosítót (Uid))" #: blueprints/marti_api/mission_marti_api.py:478 #, python-format msgid "Invalid creatorUid: %(creator_uid)s" -msgstr "" +msgstr "Érvénytelen készítői azonosító (creatorUid): %(creator_uid)s" #: blueprints/marti_api/mission_marti_api.py:529 #, python-format msgid "Group not found: %(group_name)s" -msgstr "" +msgstr "Nem található csoport: %(group_name)s" #: blueprints/marti_api/mission_marti_api.py:615 #: blueprints/ots_api/mission_api.py:197 #, python-format msgid "Failed to add mission: %(e)s" -msgstr "" +msgstr "Sikertelen küldetés hozzáadás:%(e)s" #: blueprints/marti_api/mission_marti_api.py:643 msgid "Invalid mission name" -msgstr "" +msgstr "Érvénytelen küldetésnév" #: blueprints/marti_api/mission_marti_api.py:655 #: blueprints/marti_api/mission_marti_api.py:746 @@ -162,7 +162,7 @@ msgstr "" #: blueprints/ots_api/mission_api.py:311 #, python-format msgid "Mission %(mission_name)s not found" -msgstr "" +msgstr "A %(mission_name)s küldetés nem található" #: blueprints/marti_api/mission_marti_api.py:686 #: blueprints/marti_api/mission_marti_api.py:760 @@ -171,201 +171,210 @@ msgstr "" #: blueprints/marti_api/mission_marti_api.py:2050 #: blueprints/marti_api/mission_marti_api.py:2212 msgid "Missing or invalid token" -msgstr "" +msgstr "Érvénytelen vagy hiányzó token" #: blueprints/marti_api/mission_marti_api.py:710 msgid "Only mission owners can delete missions" -msgstr "" +msgstr "Csak a küldetés tulajdonosai törölhetnek küldetéseket" #: blueprints/marti_api/mission_marti_api.py:770 msgid "Please provide the password" -msgstr "" +msgstr "Kérjük, adja meg a jelszavát" #: blueprints/marti_api/mission_marti_api.py:781 msgid "You do not have permission to change this mission's password" -msgstr "" +msgstr "Ennél a küldetésnél nem módosíthatod a jelszót" #: blueprints/marti_api/mission_marti_api.py:842 #, python-format msgid "No EUD found with UID %(invitee)s" msgstr "" +"Nem található végberendezés (EUD) a megadott azonosítóval (UID)%(invitee)s " +"azonosítóval" #: blueprints/marti_api/mission_marti_api.py:857 #, python-format msgid "No EUD found with callsign %(invitee)s" -msgstr "" +msgstr "Nem található végberendezés (EUD) a %(invitee)s hívójellel" #: blueprints/marti_api/mission_marti_api.py:873 #, python-format msgid "No user found with username %(invitee)s" -msgstr "" +msgstr "Nem található felhasználó ezzel a felhasználónévvel: %(meghívott)s" #: blueprints/marti_api/mission_marti_api.py:889 #, python-format msgid "No group found: %(invitee)s" -msgstr "" +msgstr "Nem található csoport: %(invitee)s" #: blueprints/marti_api/mission_marti_api.py:904 #, python-format msgid "Team not found: %(invitee)s" -msgstr "" +msgstr "Nem található csapat: %(invitee)s" #: blueprints/marti_api/mission_marti_api.py:967 #, python-format msgid "Invalid invitation type: %(invitation_type)s" -msgstr "" +msgstr "Érvénytelen meghívótípus: %(invitation_type)s" #: blueprints/marti_api/mission_marti_api.py:1015 #: blueprints/marti_api/mission_marti_api.py:1603 #: blueprints/ots_api/mission_api.py:391 #, python-format msgid "Mission not found: %(mission_name)s" -msgstr "" +msgstr "Küldetés nem található: %(mission_name)s" #: blueprints/marti_api/mission_marti_api.py:1053 #, python-format msgid "Invalid invitation type: %(invitation)s" -msgstr "" +msgstr "Érvénytelen meghívótípus: %(invitation)s" #: blueprints/marti_api/mission_marti_api.py:1145 #, python-format msgid "No such mission found: %(mission_name)s" -msgstr "" +msgstr "Nem található küldetés: %(mission_name)s" #: blueprints/marti_api/mission_marti_api.py:1161 msgid "Only mission owners can change EUD roles" msgstr "" +"Csak a küldetés tulajdonosai módosíthatják végfelhasználói (EUD) szerepköreit" #: blueprints/marti_api/mission_marti_api.py:1168 #: blueprints/ots_api/marker_api.py:86 msgid "Please provide a UID" -msgstr "" +msgstr "Kérjük, adjon meg egy egyedi azonosítót (UID)" #: blueprints/marti_api/mission_marti_api.py:1176 #, python-format msgid "Invalid UID: %(client_uid)s" -msgstr "" +msgstr "Érvénytelen azonosító (UID): %(client_uid)s" #: blueprints/marti_api/mission_marti_api.py:1190 #, python-format msgid "Invalid role: %(new_role)s" -msgstr "" +msgstr "Érvénytelen szerepkör: %(new_role)s" #: blueprints/marti_api/mission_marti_api.py:1321 #: blueprints/marti_api/mission_marti_api.py:1364 #, python-format msgid "Cannot find mission %(mission_name)s" -msgstr "" +msgstr "Nem található a(z) %(mission_name)s küldetés" #: blueprints/marti_api/mission_marti_api.py:1376 #, python-format msgid "Cannot find mission %(mission_guid)s" -msgstr "" +msgstr "Nem található a(z) %(mission_guid)s küldetés" #: blueprints/marti_api/mission_marti_api.py:1402 #, python-format msgid "%(username)s and mission %(mission_name)s are not in the same group" msgstr "" +"A %(username)s és a %(mission_name)s küldetés nem ugyanabban a csoportban " +"található" #: blueprints/marti_api/mission_marti_api.py:1421 #: blueprints/ots_api/mediamtx_api.py:58 msgid "Invalid token" -msgstr "" +msgstr "Érvénytelen token" #: blueprints/marti_api/mission_marti_api.py:1454 msgid "Missing UID" -msgstr "" +msgstr "Hiányzó azonosító (UID)" #: blueprints/marti_api/mission_marti_api.py:1460 #, python-format msgid "Invalid UID: %(uid)s" -msgstr "" +msgstr "Érvénytelen azonosító (UID): %(uid)s" #: blueprints/marti_api/mission_marti_api.py:1694 msgid "Missing or invalid certificate" -msgstr "" +msgstr "Hiányzó vagy érvénytelen tanúsítvány" #: blueprints/marti_api/mission_marti_api.py:1710 msgid "File name cannot be blank" -msgstr "" +msgstr "A fájlnév nem lehet üres" #: blueprints/marti_api/mission_marti_api.py:1744 #, python-format msgid "%(extension)s is not an allowed file extension" -msgstr "" +msgstr "A(z) %(extension)s nem engedélyezett fájlkiterjesztés" #: blueprints/marti_api/mission_marti_api.py:1820 #, python-format msgid "No content found with hash: %(content_hash)s" msgstr "" +"Nem található tartalom a következő ujjlenyomattal (hash) értékkel: " +"%(content_hash)s" #: blueprints/marti_api/mission_marti_api.py:1865 #, python-format msgid "No such mission: %(mission_name)s" -msgstr "" +msgstr "Nincs ilyen küldetés: %(mission_name)s" #: blueprints/marti_api/mission_marti_api.py:1887 #, python-format msgid "No such file with hash %(content_hash)s" -msgstr "" +msgstr "Nincsenek %(content_hash)s ujjlenyomattal (hash)-sel rendelkező fájlok" #: blueprints/marti_api/mission_marti_api.py:2103 #, python-format msgid "UID %(uid)s not found" -msgstr "" +msgstr "Az %(uid)s egyedi azonosító (UID) nem található" #: blueprints/marti_api/mission_marti_api.py:2133 #, python-format msgid "No content found with hash %(hash)s" -msgstr "" +msgstr "Nem található tartalom a következő ujjlenyomattal (hash): %(hash)s" #: blueprints/marti_api/mission_marti_api.py:2160 #, python-format msgid "Failed to delete content with hash %(hash)s: %(e)s" msgstr "" +"Nem sikerült törölni a %(hash)s: %(e)s ujjlenyomattal (hash) rendelkező " +"tartalmat" #: blueprints/marti_api/video_marti_api.py:146 msgid "Invalid Certificate" -msgstr "" +msgstr "Érvénytelen tanúsítvány" #: blueprints/marti_api/video_marti_api.py:209 msgid "Video not found" -msgstr "" +msgstr "Videó nem található" #: blueprints/marti_api/video_marti_api.py:216 #: blueprints/ots_api/group_api.py:209 blueprints/ots_api/user_api.py:521 #, python-format msgid "User %(username)s not found" -msgstr "" +msgstr "A(z) %(username)s felhasználó nem található" #: blueprints/marti_api/video_marti_api.py:233 #, python-format msgid "Video stream with uid %(uid)s not found" -msgstr "" +msgstr "Nem található a(z) %(uid)s azonosítójú (UID) videofolyam" #: blueprints/ots_api/api.py:93 #, python-format msgid "Invalid sort column: %(sort_by)s" -msgstr "" +msgstr "Érvénytelen rendezési oszlop: %(sort_by)s" #: blueprints/ots_api/api.py:294 #, python-format msgid "Invalid username: %(username)s" -msgstr "" +msgstr "Érvénytelen felhasználónév: %(username)s" #: blueprints/ots_api/api.py:336 #, python-format msgid "Certificate already exists for %(username)s" -msgstr "" +msgstr "A tanúsítvány már létezik ehhez a felhasználónévhez: %(username)s" #: blueprints/ots_api/api.py:371 msgid "Please specify a callsign" -msgstr "" +msgstr "Kérjük, adjon meg egy hívójelet" #: blueprints/ots_api/casevac_api.py:134 #, python-format msgid "Unknown UID: %(uid)s" -msgstr "" +msgstr "Ismeretlen egyedi azonosító (UID): %(uid)s" #: blueprints/ots_api/data_package_api.py:48 msgid "" From 0d1b22ac77826cab7e515147d4f8220dbaeeea90 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 17 Jul 2026 03:19:00 +0000 Subject: [PATCH 8/8] fix: apply black formatting in ots API pagination helper --- opentakserver/blueprints/ots_api/api.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/opentakserver/blueprints/ots_api/api.py b/opentakserver/blueprints/ots_api/api.py index 099bd2d5..4b39b315 100644 --- a/opentakserver/blueprints/ots_api/api.py +++ b/opentakserver/blueprints/ots_api/api.py @@ -74,9 +74,7 @@ def paginate(query: db.Query, model=None): # to "Alice" instead of after every uppercase entry (Postgres # default collation is byte-ordered: Z < a). try: - is_string_col = isinstance( - column.type, (String, Text, Unicode, UnicodeText) - ) + is_string_col = isinstance(column.type, (String, Text, Unicode, UnicodeText)) except AttributeError: is_string_col = False sort_expr = func.lower(column) if is_string_col else column