diff --git a/core/command_service.py b/core/command_service.py
index eafec80..bffd764 100644
--- a/core/command_service.py
+++ b/core/command_service.py
@@ -134,7 +134,7 @@ class CommandService:
return
for channel, label in self.channels.items():
- row = self.db.query_single("SELECT access_level, module, enabled, verified "
+ row = self.db.query_single("SELECT access_level, module, enabled, verified, sub_command "
"FROM command_config "
"WHERE command = ? AND sub_command = ? AND channel = ?",
[command, sub_command, channel])
@@ -147,9 +147,9 @@ class CommandService:
"VALUES (?, ?, ?, ?, ?, 1, 1)",
[command, sub_command, access_level, channel, module])
elif row.verified:
- if row.module != module:
- self.logger.warning("module different for different forms of command '%s' and sub_command '%s'" % (
- command, sub_command))
+ if row.module != module and row.sub_command == sub_command:
+ self.logger.warning(f"module different for different forms of command '{command}' "
+ f"and sub_command '{sub_command}'")
else:
# mark command as verified
self.db.exec("UPDATE command_config SET verified = 1, module = ? "
diff --git a/core/igncore.py b/core/igncore.py
index abf93d4..44cee60 100644
--- a/core/igncore.py
+++ b/core/igncore.py
@@ -43,7 +43,7 @@ class IgnCore:
self.last_timer_event = 0
self.start_time = int(time.time())
self.major_version = "IGNCore v2.8"
- self.minor_version = "2"
+ self.minor_version = "3"
self.incoming_queue = FifoQueue()
self.mass_message_queue = None
self.conns = DictObject()
diff --git a/core/lookup/org_pork_service.py b/core/lookup/org_pork_service.py
index 1679e86..c010cec 100644
--- a/core/lookup/org_pork_service.py
+++ b/core/lookup/org_pork_service.py
@@ -185,3 +185,9 @@ class OrgPorkService:
# Dont use SSL, as its rather slow compared to normal requests....
# noinspection HttpUrlsUsage
return f"http://people.anarchy-online.com/org/stats/d/{dimension}/name/{org_id}/basicstats.xml?data_type=json"
+
+ def find_org(self, search, table="all_orgs"):
+ if search.isdigit():
+ return self.db.query("SELECT * FROM " + table + " where org_id = ?", [search])
+ elif isinstance(search, str):
+ return self.db.query("SELECT * FROM " + table + " where org_name LIKE ?", ["%" + search + "%"])
\ No newline at end of file
diff --git a/modules/core/accounting/account_log_controller.py b/modules/core/accounting/account_log_controller.py
index 91e7b1f..6ea2c25 100644
--- a/modules/core/accounting/account_log_controller.py
+++ b/modules/core/accounting/account_log_controller.py
@@ -55,7 +55,10 @@ class AccountLogController(BaseModule):
sub_command="moderate")
def account_add_log(self, request: command_request, _, log_type, user, text):
main = self.account_service.get_main(user.char_id).char_id
+ if not main:
+ return f"The main of {user} could not be found. Are you sure there is an account?"
self.account_service.add_log(main, log_type, text, request.sender.char_id)
+ return f"The log entry for {self.character_service.resolve_char_to_name(main)} has been added successfully."
# noinspection LongLine
@command(command="account", params=[Const("log"), Const("id"), Int("id")], access_level="moderator",
@@ -73,7 +76,14 @@ class AccountLogController(BaseModule):
f"Message: {entry.reason}\n"
f"Logging Time: {self.util.format_datetime(entry.created_at)}")
else:
- return f"No Logentry with ID {id} found."
+ return f"No Logentry with ID {log_id} found."
+
+ @command(command="account", params=[Const("log"), Const("recent")], access_level="moderator",
+ description="View the 20 most recent log entries",
+ sub_command="moderate")
+ def account_view_recent(self, _, _1, _2):
+ entries = self.account_service.get_logs(limit=20)
+ return ChatBlob(f"Most Recent logentries", self.display_logs(entries))
def display_logs(self, entries) -> str:
blob = ""
diff --git a/modules/core/accounting/services/account_service.py b/modules/core/accounting/services/account_service.py
index 4d70e90..b7cd900 100644
--- a/modules/core/accounting/services/account_service.py
+++ b/modules/core/accounting/services/account_service.py
@@ -532,14 +532,21 @@ class AccountService:
"VALUES (?, ?, ?, ?, ?, ?)",
[main, log_type, delta, leader, message, time.time()])
- def get_logs(self, user, log_type=None, limit=25) -> List[DictObject]:
- if not log_type:
- return self.db.query("SELECT * FROM account_log "
- "where char_id=? and type != 'admin' order by log_id desc LIMIT ? ", [user, limit])
-
+ def get_logs(self, user=None, log_type=None, limit=25) -> List[DictObject]:
+ if not user:
+ if not log_type:
+ return self.db.query("SELECT * FROM account_log "
+ "where type != 'admin' order by log_id desc LIMIT ? ", [limit])
+ else:
+ return self.db.query("SELECT * FROM account_log "
+ "where type=? order by log_id desc LIMIT ?", [log_type, limit])
else:
- return self.db.query("SELECT * FROM account_log "
- "where char_id=? and type=? order by log_id desc LIMIT ?", [user, log_type, limit])
+ if not log_type:
+ return self.db.query("SELECT * FROM account_log "
+ "where char_id=? and type != 'admin' order by log_id desc LIMIT ? ", [user, limit])
+ else:
+ return self.db.query("SELECT * FROM account_log "
+ "where char_id=? and type=? order by log_id desc LIMIT ?", [user, log_type, limit])
def get_log_by_id(self, log_id) -> DictObject:
return self.db.query_single("SELECT * FROM account_log where log_id=? ", [log_id])
diff --git a/modules/onlinebot/online/bot_controller.py b/modules/onlinebot/online/bot_controller.py
index c87f34f..f97a2dc 100644
--- a/modules/onlinebot/online/bot_controller.py
+++ b/modules/onlinebot/online/bot_controller.py
@@ -1,6 +1,3 @@
-from threading import Thread
-
-from core import command_request, sender_obj
from core.aochat.BaseModule import BaseModule
from core.buddy_service import BuddyService
from core.chat_blob import ChatBlob
@@ -31,6 +28,7 @@ class BotController(BaseModule):
self.org_pork: PorkService = registry.get_instance("org_pork_service")
self.command_alias_service: CommandAliasService = registry.get_instance("command_alias_service")
self.buddy_service: BuddyService = registry.get_instance("buddy_service")
+ self.online = registry.get_instance("buddy_service")
def pre_start(self):
self.db.exec("CREATE TABLE IF NOT EXISTS org_bots(char_id int primary key not null, org_id int not null)")
@@ -38,24 +36,31 @@ class BotController(BaseModule):
def start(self):
pass
- @command(command="bots", params=[Const("add"), Character("botname")], access_level="admin",
- description="Add an bot to the bot list")
- def bots_add_any(self, sender, _, bot):
- Thread(target=self.bots_add, args=(bot, sender)).start()
-
- def bots_add(self, bot: sender_obj, request: command_request):
+ @command(command="orgs", params=[Const("bots"), Const("add"), Character("botname")], access_level="admin",
+ description="Add an bot to the bot list", sub_command="bots_mng")
+ def cmd_orgs_bots_add(self, request, _, _1, bot):
player = self.pork.request_char_info(bot.name, 5)
if self.db.exec("INSERT IGNORE INTO org_bots(char_id, org_id) VALUES(?, ?)", [bot.char_id, player.org_id]) == 0:
- request.reply("The bot %s is already marked as an chatbot." % bot.name)
+ request.reply(f"The bot {bot.name} is already marked as a chatbot.")
else:
- request.reply("Successfully marked %s as an chatbot." % bot.name)
+ self.db.exec("DELETE FROM online where char_id=?", [bot.char_id])
+ request.reply(f"Successfully marked {bot.name} as a chatbot.")
- @command(command="bots",
- params=[],
+ @command(command="orgs", params=[Const("bots"), Const("rem"), Character("botname")], access_level="admin",
+ description="Add an bot to the bot list", sub_command="bots_mng")
+ def cmd_orgs_bots_rem(self, request, _, _1, bot):
+
+ if self.db.exec("DELETE FROM org_bots WHERE char_id=?", [bot.char_id]) == 0:
+ request.reply(f"{bot.name} is not a chatbot.")
+ else:
+ request.reply(f"Successfully removed {bot.name} as a chatbot. The character will show up in !online again after its next login.")
+
+ @command(command="orgs",
+ params=[Const("bots")],
access_level="moderator",
description="show all orgbots",
- sub_command="show")
- def bots_show_all(self, _):
+ sub_command="bots_show")
+ def cmd_orgs_bots_show(self, _, _1):
def format_row(query):
bud = self.buddy_service.is_online(query["char_id"])
buddy = "O" if bud == 1 else "O" if bud == 0 else "U"
diff --git a/modules/onlinebot/online/org_alias_controller.py b/modules/onlinebot/online/org_alias_controller.py
index 3ea9de7..0347944 100644
--- a/modules/onlinebot/online/org_alias_controller.py
+++ b/modules/onlinebot/online/org_alias_controller.py
@@ -1,23 +1,21 @@
from core.buddy_service import BuddyService
+from core.chat_blob import ChatBlob
from core.command_alias_service import CommandAliasService
from core.db import DB
-from core.decorators import instance
+from core.decorators import instance, command
from core.logger import Logger
from core.lookup.pork_service import PorkService
from core.text import Text
from core.igncore import IgnCore
from core.util import Util
from modules.core.accounting.services.access_service import AccessService
+from core.command_param_types import Const, Any
# noinspection DuplicatedCode
@instance()
class OrgAliasController:
- org_prefix = {4736: "AP", 9632: "AP", 9622: "AC", 9831: "AC", 581633: "BST", 9707: "CoH", 9990: "CoH", 4637: "HAV",
- 10197: "IMP", 6093: "LCG", 4789: "MJ", 4687: "NA", 4800: "PR", 4993: "REGS", 813067: "SOTL",
- 4851: "SP", 9611: "SP", 4614: "TA", 4831: "TGNF", 6183: "TRE", 9822: "TS", 4611: "UHS",
- 5571: "UOTR", 520210: "WO", 1349649: "VR", 4826: "42", 1349647: "VR", 6332: "DRA", 848: "DRA",
- 4675: "VA", 1558530: "EoS"}
+ prefix_cache = {}
def inject(self, registry):
self.logger = Logger(__name__)
@@ -33,6 +31,75 @@ class OrgAliasController:
def start(self):
self.db.exec("CREATE TABLE IF NOT EXISTS org_alias(org_id int primary key not null, org_alias VARCHAR(255))")
+ for entry in self.db.query("SELECT * from org_alias"):
+ self.prefix_cache[entry.org_id] = entry.org_alias
def get_alias(self, org_id):
- return self.org_prefix.get(org_id, "-UKN-")
+ return self.prefix_cache.get(org_id, "-UKN-")
+
+ @command(command="orgs", params=[Const("alias"), Const("list", is_optional=True)], access_level="member",
+ description="View the currently registered org aliases", sub_command="alias_info")
+ def cmd_alias_list(self, request, _, _1):
+ alias_list = self.db.query("SELECT ao.org_id, ao.org_name, a.org_alias, (!ISNULL(o.org_id)) as member FROM org_alias a LEFT JOIN all_orgs ao on a.org_id = ao.org_id "
+ "LEFT JOIN orgs o on a.org_id = o.org_id "
+ "ORDER BY ISNULL(o.org_id), LOWER(ao.org_name)")
+ blob = []
+ last = -1
+ for alias in alias_list:
+ if last != alias.member:
+ if alias.member == 1:
+ blob.append("")
+ elif alias.member == 0:
+ blob.append("\n")
+ last = alias.member
+ blob.append(f" [{alias.org_alias}] - {alias.org_name} ({alias.org_id})")
+ return ChatBlob("Aliases currently in use", "\n".join(blob))
+
+ @command(command="orgs", params=[Const("alias"), Const("set"), Any("alias"), Any("org")], access_level="moderator",
+ description="View the currently registered org aliases",
+ sub_command="alias_moderate")
+ def cmd_alias_add(self, sender, _, _1, alias, search):
+ orgs = self.org_pork.find_org(search)
+ if len(orgs) == 1:
+ orgs = orgs[0]
+ if self.db.exec("REPLACE INTO org_alias(org_id, org_alias) VALUES (?, ?)", [orgs.org_id, alias]):
+ self.prefix_cache[orgs.org_id] = alias
+ return f"Alias for {orgs.org_name} has been set to [{alias}]."
+
+ else:
+ return f"There's already an alias for {orgs.org_name}.. its [{self.get_alias(orgs.org_id)}]"
+ elif len(orgs) == 0:
+ return f"No org with the name {search} was found on PoRK."
+ else:
+ blob = "Your search had multiple results; please pick an org:
"
+ for org in orgs:
+ blob += f'[{self.text.make_chatcmd("Set Alias", f"/tell orgs alias set {alias} {org.org_id}")}]' \
+ f'[{self.text.make_chatcmd("More", f"/tell orgs info {org.org_id}")}]' \
+ f' {org.org_name} ({org.org_id}) ' \
+ f'<{org.faction.lower()}>{org.faction} [{org.member_count} members]' \
+ f'
'
+ return ChatBlob("Pick an Org", blob)
+
+ @command(command="orgs", params=[Const("alias"), Const("rem"), Any("org")], access_level="moderator",
+ description="View the currently registered org aliases",
+ sub_command="alias_moderate")
+ def cmd_alias_del(self, sender, _, _1, search):
+ orgs = self.org_pork.find_org(search)
+ if len(orgs) == 1:
+ orgs = orgs[0]
+ if self.db.exec("DELETE FROM org_alias where org_id=?", [orgs.org_id]) == 1:
+ self.prefix_cache.pop(orgs.org_id)
+ return f"Alias for {orgs.org_name} has been removed."
+ else:
+ return f"There's no alias for {orgs.org_name}.."
+ elif len(orgs) == 0:
+ return f"No org with the name {search} was found on PoRK."
+ else:
+ blob = "Your search had multiple results; please pick an org:
"
+ for org in orgs:
+ blob += f'[{self.text.make_chatcmd("Set Alias", f"/tell orgs alias rem {org.org_id}")}]' \
+ f'[{self.text.make_chatcmd("More", f"/tell orgs info {org.org_id}")}]' \
+ f' {org.org_name} ({org.org_id}) ' \
+ f'<{org.faction.lower()}>{org.faction} [{org.member_count} members]' \
+ f'
'
+ return ChatBlob("Pick an Org", blob)
diff --git a/modules/onlinebot/online/org_controller.py b/modules/onlinebot/online/org_controller.py
index 446eb16..cae60ba 100644
--- a/modules/onlinebot/online/org_controller.py
+++ b/modules/onlinebot/online/org_controller.py
@@ -51,6 +51,7 @@ class OrgController:
self.alias_controller: OrgAliasController = registry.get_instance("org_alias_controller")
self.account_service: AccountService = registry.get_instance("account_service")
self.cache: CacheService = registry.get_instance("cache_service")
+ self.org_alias: OrgAliasController = registry.get_instance("org_alias_controller")
def start(self):
self.db.exec("CREATE TABLE IF NOT EXISTS orgs(org_id int primary key not null)")
@@ -65,12 +66,12 @@ class OrgController:
self.buddy_service.add_buddy(player.char_id, "member")
@command(command="orgs", params=[Const("add"), Any("Organisation")], access_level="admin",
- description="Add an org to the online list")
+ description="Add an org to the online list", sub_command="moderate")
def orgs_add_any(self, sender, _, org):
return self.orgs_add(org, sender)
def orgs_add(self, search, sender):
- orgs = self.find_org(search)
+ orgs = self.org_pork.find_org(search)
if len(orgs) == 1:
orgs = orgs[0]
if self.db.exec("REPLACE INTO orgs(org_id) VALUES(?)",
@@ -95,12 +96,12 @@ class OrgController:
return ChatBlob("Pick an Org", blob)
@command(command="orgs", params=[Const("rem"), Any("Organisation")], access_level="admin",
- description="Remove an org from the online list")
+ description="Remove an org from the online list", sub_command="moderate")
def orgs_rem_any(self, _, _1, org):
return self.orgs_rem(org)
def orgs_rem(self, search):
- orgs = self.find_org(search)
+ orgs = self.org_pork.find_org(search)
if len(orgs) == 1:
orgs = orgs[0]
@@ -124,19 +125,21 @@ class OrgController:
return ChatBlob("Pick an Org", blob)
@command(command="orgs", params=[Const("list", is_optional=True)], access_level="member",
- description="View all orgs on the online list", sub_command="list")
+ description="View all orgs on the online list", sub_command="online_info")
def orgs_list(self, sender: CommandRequest, _):
- head = "Organisations in our Alliance"
+ head = "Organisations registered as Members"
blob = ""
- for org in self.db.query("SELECT * from orgs o "
- "left join all_orgs a on o.org_id = a.org_id order by a.org_name"):
+ for org in self.db.query("SELECT o.*, a.*, oa.org_alias from orgs o "
+ "left join all_orgs a on o.org_id = a.org_id "
+ "left join org_alias oa on o.org_id = oa.org_id order by a.org_name"):
org = DictObject(org)
- blob += "- %s%s%s (%d) with %d members\n" % (
+ blob += "- %s%s%s%s (%d) with %d members\n" % (
"[" + self.text.make_chatcmd("Info", "/tell orgs info %d" % org.org_id,
style="style='text-decoration:none'") + "] ",
"[" + self.text.make_chatcmd("Remove", "/tell orgs rem %d" % org.org_id,
style="style='text-decoration:none'") + "] " if
sender.sender.access_level["level"] <= 10 else "",
+ f"[{self.org_alias.get_alias(org.org_id)}] ",
org.org_name, org.org_id,
self.db.query_single("SELECT member_count from all_orgs where org_id=?", [org.org_id]).member_count)
@@ -291,12 +294,6 @@ class OrgController:
self.threads["roster"] = thread
thread.start()
- def find_org(self, search, table="all_orgs"):
- if search.isdigit():
- return self.db.query("SELECT * FROM " + table + " where org_id = ?", [search])
- elif isinstance(search, str):
- return self.db.query("SELECT * FROM " + table + " where org_name LIKE ?", ["%" + search + "%"])
-
def fetch_single(self, org_id, org_name, sender: object):
start = time.time()
data = []
diff --git a/modules/orgbot/org/org_roster_controller.py b/modules/orgbot/org/org_roster_controller.py
index c710acc..4c7459a 100644
--- a/modules/orgbot/org/org_roster_controller.py
+++ b/modules/orgbot/org/org_roster_controller.py
@@ -36,7 +36,7 @@ class OrgRosterController:
ORG_MEMBER_LOGOFF_EVENT = "org_member_logoff"
ORG_MEMBER_REMOVED_EVENT = "org_member_removed"
- LEFT_ORG = [508, 45978487508, 45978487]
+ LEFT_ORG = [508, 45978487]
KICKED_FROM_ORG = [508, 37093479]
INVITED_TO_ORG = [508, 173558247]
KICKED_INACTIVE_FROM_ORG = [508, 20908201]
diff --git a/modules/standard/tower/land_controller.py b/modules/standard/tower/land_controller.py
index 8a76927..041d0b5 100644
--- a/modules/standard/tower/land_controller.py
+++ b/modules/standard/tower/land_controller.py
@@ -16,10 +16,10 @@ from core.setting_types import BooleanSettingType
from core.text import Text
from core.util import Util
from modules.standard.helpbot.playfield_controller import PlayfieldController
-# legacy(0), EU - friendly(1) or US - friendly(2) => timing
from modules.standard.tower.tower_attack_controller import TowerAttackController
from modules.standard.tower.tower_events import TowerEventController
+# legacy(0), EU - friendly(1) or US - friendly(2) => timing
FIXED_TIMES = {1: 4,
2: 20}
@@ -219,7 +219,7 @@ class LandController:
blob += towers
else:
if not row.enabled:
- blob += "Disabled\n"
+ blob += "Disabled\n"
else:
blob += "This site is potentially unplanted\n"
@@ -251,7 +251,10 @@ class LandController:
def get_towers(self, pf, site=None):
if site:
- return self.db.query("""SELECT d.playfield_id AS pf_id,d.site_number, d.site_name, d.min_ql, d.max_ql, d.x_coord, d.y_coord, d.timing, d.enabled, a.tower_id, a.ql, a.close_time, a.penalty_until, a.planted, b.*, c.*, e.* FROM tower_sites d
+ return self.db.query("""SELECT d.playfield_id AS pf_id,d.site_number, d.site_name, d.min_ql, d.max_ql,
+ d.x_coord, d.y_coord, d.timing, d.enabled, a.tower_id, a.ql,
+ a.close_time, a.penalty_until, a.planted, b.*, c.*, e.*
+ FROM tower_sites d
LEFT JOIN towers a on a.pf_id = d.playfield_id and a.site_number = d.site_number
LEFT JOIN aodb b ON a.high_id = b.highid
LEFT JOIN playfields c on d.playfield_id = c.id
@@ -259,8 +262,11 @@ class LandController:
WHERE playfield_id=? AND d.site_number=? ORDER BY close_time IS NULL, ql desc""",
[pf, site])
else:
- return self.db.query("""SELECT d.playfield_id AS pf_id, d.site_number, d.site_name, d.min_ql, d.max_ql, d.x_coord, d.y_coord, d.timing, d.enabled, a.tower_id, a.ql, a.close_time, a.penalty_until, a.planted, b.*, c.*, e.* FROM tower_sites d
- LEFT JOIN towers a on a.pf_id = d.playfield_id and a.site_number = d.site_number
+ return self.db.query("""SELECT d.playfield_id AS pf_id, d.site_number, d.site_name, d.min_ql, d.max_ql,
+ d.x_coord, d.y_coord, d.timing, d.enabled, a.tower_id, a.ql,
+ a.close_time, a.penalty_until, a.planted, b.*, c.*, e.*
+ FROM tower_sites d
+ LEFT JOIN towers a on a.pf_id = d.playfield_id and a.site_number = d.site_number AND a.close_time IS NOT NULL
LEFT JOIN aodb b ON a.high_id = b.highid
LEFT JOIN playfields c on d.playfield_id = c.id
LEFT JOIN all_orgs e on a.org_id = e.org_id
@@ -270,27 +276,17 @@ class LandController:
""", [pf])
def get_towers_by_org(self, org_id):
- return self.db.query(
- "SELECT COUNT(CASE WHEN name LIKE '%Turret%' THEN 1 WHEN name LIKE '%SAM Battery%' THEN 1 END) turrets, "
- "COUNT(CASE WHEN name LIKE '%Guard%' THEN 1 END) guard, "
- "a.*, b.*, c.*, d.site_name, d.min_ql, d.max_ql, d.timing, d.enabled, e.* FROM towers a "
- "LEFT JOIN aodb b ON a.high_id = b.highid "
- "LEFT JOIN playfields c on a.pf_id = c.id "
- "LEFT JOIN tower_sites d on a.pf_id = d.playfield_id and a.site_number = d.site_number "
- "LEFT JOIN all_orgs e on a.org_id = e.org_id "
- "WHERE a.org_id=? GROUP BY a.pf_id, a.site_number ORDER BY ql, close_time IS NOT NULL", [org_id])
-
- # For some reason the Code above broke... and apparently works again now? leaving this one here, just in case.
- # return self.db.query("""SELECT * FROM (SELECT COUNT(CASE WHEN name LIKE '%Turret%' THEN 1 WHEN name LIKE '%SAM Battery%' THEN 1 END) turrets,
- # COUNT(CASE WHEN name LIKE '%Guard%' THEN 1 END) guard, a.pf_id, a.org_id, e.org_name, c.*, d.* FROM towers a
- # LEFT JOIN aodb b ON a.high_id = b.highid
- # LEFT JOIN playfields c on a.pf_id = c.id
- # LEFT JOIN tower_sites d on a.pf_id = d.playfield_id and a.site_number = d.site_number
- # LEFT JOIN all_orgs e on a.org_id = e.org_id
- # WHERE a.org_id=? GROUP BY a.pf_id, a.site_number ORDER BY ql, close_time IS NOT NULL) t1
- # LEFT JOIN
- # (SELECT * from towers a WHERE a.org_id=? AND close_time IS NOT NULL) t2
- # ON t1.pf_id=t2.pf_id AND t1.site_number = t2.site_number""", [org_id, org_id])
+ return self.db.query("""SELECT * FROM (SELECT COUNT(CASE WHEN name LIKE '%Turret%' THEN 1 WHEN name LIKE '%SAM Battery%' THEN 1 END) turrets,
+ COUNT(CASE WHEN name LIKE '%Guard%' THEN 1 END) guard, a.pf_id, a.org_id, e.org_name, c.*, d.* FROM towers a
+ LEFT JOIN aodb b ON a.high_id = b.highid
+ LEFT JOIN playfields c on a.pf_id = c.id
+ LEFT JOIN tower_sites d on a.pf_id = d.playfield_id and a.site_number = d.site_number
+ LEFT JOIN all_orgs e on a.org_id = e.org_id
+ WHERE a.org_id=? GROUP BY a.pf_id, a.site_number ORDER BY ql, close_time IS NOT NULL) t1
+ LEFT JOIN
+ (SELECT * from towers a WHERE a.org_id=? AND close_time IS NOT NULL) t2
+ ON t1.pf_id=t2.pf_id AND t1.site_number = t2.site_number
+ ORDER BY ql""", [org_id, org_id])
def find_orgs(self, search):
return self.db.query("SELECT DISTINCT a.org_name, a.org_id FROM all_orgs a "