tradedangerous 11.5.3__py3-none-any.whl → 12.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of tradedangerous might be problematic. Click here for more details.
- tradedangerous/cache.py +567 -395
- tradedangerous/cli.py +2 -2
- tradedangerous/commands/TEMPLATE.py +25 -26
- tradedangerous/commands/__init__.py +8 -16
- tradedangerous/commands/buildcache_cmd.py +40 -10
- tradedangerous/commands/buy_cmd.py +57 -46
- tradedangerous/commands/commandenv.py +0 -2
- tradedangerous/commands/export_cmd.py +78 -50
- tradedangerous/commands/import_cmd.py +67 -31
- tradedangerous/commands/market_cmd.py +52 -19
- tradedangerous/commands/olddata_cmd.py +120 -107
- tradedangerous/commands/rares_cmd.py +122 -110
- tradedangerous/commands/run_cmd.py +118 -66
- tradedangerous/commands/sell_cmd.py +52 -45
- tradedangerous/commands/shipvendor_cmd.py +49 -234
- tradedangerous/commands/station_cmd.py +55 -485
- tradedangerous/commands/update_cmd.py +56 -420
- tradedangerous/csvexport.py +173 -162
- tradedangerous/db/__init__.py +27 -0
- tradedangerous/db/adapter.py +191 -0
- tradedangerous/db/config.py +95 -0
- tradedangerous/db/engine.py +246 -0
- tradedangerous/db/lifecycle.py +332 -0
- tradedangerous/db/locks.py +208 -0
- tradedangerous/db/orm_models.py +455 -0
- tradedangerous/db/paths.py +112 -0
- tradedangerous/db/utils.py +661 -0
- tradedangerous/gui.py +2 -2
- tradedangerous/plugins/eddblink_plug.py +387 -251
- tradedangerous/plugins/spansh_plug.py +2488 -821
- tradedangerous/prices.py +124 -142
- tradedangerous/templates/TradeDangerous.sql +6 -6
- tradedangerous/tradecalc.py +1227 -1109
- tradedangerous/tradedb.py +533 -384
- tradedangerous/tradeenv.py +12 -1
- tradedangerous/version.py +1 -1
- {tradedangerous-11.5.3.dist-info → tradedangerous-12.0.1.dist-info}/METADATA +11 -7
- {tradedangerous-11.5.3.dist-info → tradedangerous-12.0.1.dist-info}/RECORD +42 -38
- {tradedangerous-11.5.3.dist-info → tradedangerous-12.0.1.dist-info}/WHEEL +1 -1
- tradedangerous/commands/update_gui.py +0 -721
- tradedangerous/jsonprices.py +0 -254
- tradedangerous/plugins/edapi_plug.py +0 -1071
- tradedangerous/plugins/journal_plug.py +0 -537
- tradedangerous/plugins/netlog_plug.py +0 -316
- {tradedangerous-11.5.3.dist-info → tradedangerous-12.0.1.dist-info}/entry_points.txt +0 -0
- {tradedangerous-11.5.3.dist-info → tradedangerous-12.0.1.dist-info/licenses}/LICENSE +0 -0
- {tradedangerous-11.5.3.dist-info → tradedangerous-12.0.1.dist-info}/top_level.txt +0 -0
tradedangerous/tradedb.py
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# --------------------------------------------------------------------
|
|
2
2
|
# Copyright (C) Oliver 'kfsone' Smith 2014 <oliver@kfs.org>:
|
|
3
3
|
# Copyright (C) Bernd 'Gazelle' Gollesch 2016, 2017
|
|
4
|
-
# Copyright (C)
|
|
4
|
+
# Copyright (C) Stefan 'Tromador' Morrell 2025
|
|
5
|
+
# Copyright (C) Jonathan 'eyeonus' Jones 2018 - 2025
|
|
5
6
|
#
|
|
6
7
|
# You are free to use, redistribute, or even print and eat a copy of
|
|
7
8
|
# this software so long as you include this copyright notice.
|
|
@@ -59,7 +60,6 @@ import heapq
|
|
|
59
60
|
import itertools
|
|
60
61
|
import locale
|
|
61
62
|
import re
|
|
62
|
-
import sqlite3
|
|
63
63
|
import sys
|
|
64
64
|
import typing
|
|
65
65
|
|
|
@@ -74,6 +74,44 @@ if typing.TYPE_CHECKING:
|
|
|
74
74
|
|
|
75
75
|
locale.setlocale(locale.LC_ALL, '')
|
|
76
76
|
|
|
77
|
+
from sqlalchemy import func, select
|
|
78
|
+
from sqlalchemy.orm import Session
|
|
79
|
+
from .db import make_engine_from_config, get_session_factory, healthcheck
|
|
80
|
+
from .db.orm_models import (
|
|
81
|
+
System, Station, Item, Category, Ship, Upgrade, RareItem,
|
|
82
|
+
StationItem, ShipVendor, UpgradeVendor, Added, ExportControl, StationItemStaging
|
|
83
|
+
)
|
|
84
|
+
from .db.utils import age_in_days
|
|
85
|
+
|
|
86
|
+
# --------------------------------------------------------------------
|
|
87
|
+
# SQLAlchemy ORM imports (aliased to avoid clashing with legacy wrappers).
|
|
88
|
+
# These map to the actual database tables via SQLAlchemy and are used
|
|
89
|
+
# internally in loaders/writers to replace raw sqlite3 queries.
|
|
90
|
+
#
|
|
91
|
+
# NOTE: We still instantiate and use legacy wrapper classes defined in
|
|
92
|
+
# this file (System, Station, Item, etc.) to maintain API compatibility
|
|
93
|
+
# across the rest of the codebase (Pass 1 migration).
|
|
94
|
+
#
|
|
95
|
+
# In a possible future cleanup (Pass 2), the wrappers may be removed
|
|
96
|
+
# entirely, and code updated to use ORM models directly.
|
|
97
|
+
# --------------------------------------------------------------------
|
|
98
|
+
|
|
99
|
+
from .db.orm_models import (
|
|
100
|
+
Added as SA_Added,
|
|
101
|
+
System as SA_System,
|
|
102
|
+
Station as SA_Station,
|
|
103
|
+
Item as SA_Item,
|
|
104
|
+
Category as SA_Category,
|
|
105
|
+
StationItem as SA_StationItem,
|
|
106
|
+
RareItem as SA_RareItem,
|
|
107
|
+
Ship as SA_Ship,
|
|
108
|
+
ShipVendor as SA_ShipVendor,
|
|
109
|
+
Upgrade as SA_Upgrade,
|
|
110
|
+
UpgradeVendor as SA_UpgradeVendor,
|
|
111
|
+
ExportControl as SA_ExportControl,
|
|
112
|
+
StationItemStaging as SA_StationItemStaging,
|
|
113
|
+
)
|
|
114
|
+
|
|
77
115
|
|
|
78
116
|
######################################################################
|
|
79
117
|
# Classes
|
|
@@ -570,53 +608,91 @@ class TradeDB:
|
|
|
570
608
|
load=True,
|
|
571
609
|
debug=None,
|
|
572
610
|
):
|
|
573
|
-
|
|
611
|
+
# --- SQLAlchemy engine/session (replaces sqlite3.Connection) ---
|
|
612
|
+
self.engine = None
|
|
613
|
+
self.Session = None
|
|
574
614
|
self.tradingCount = None
|
|
575
|
-
|
|
615
|
+
|
|
616
|
+
# Environment
|
|
576
617
|
tdenv = tdenv or TradeEnv(debug=(debug or 0))
|
|
577
618
|
self.tdenv = tdenv
|
|
578
|
-
|
|
619
|
+
|
|
620
|
+
# --- Path setup (unchanged) ---
|
|
579
621
|
self.templatePath = Path(tdenv.templateDir).resolve()
|
|
580
622
|
self.dataPath = dataPath = fs.ensurefolder(tdenv.dataDir)
|
|
581
623
|
self.csvPath = fs.ensurefolder(tdenv.csvDir)
|
|
582
|
-
|
|
583
|
-
fs.copy_if_newer(
|
|
584
|
-
fs.copy_if_newer(
|
|
585
|
-
fs.copy_if_newer(
|
|
586
|
-
fs.copy_if_newer(
|
|
587
|
-
|
|
624
|
+
|
|
625
|
+
fs.copy_if_newer(self.templatePath / "Added.csv", self.csvPath / "Added.csv")
|
|
626
|
+
fs.copy_if_newer(self.templatePath / "RareItem.csv", self.csvPath / "RareItem.csv")
|
|
627
|
+
fs.copy_if_newer(self.templatePath / "Category.csv", self.csvPath / "Category.csv")
|
|
628
|
+
fs.copy_if_newer(self.templatePath / "TradeDangerous.sql", self.dataPath / "TradeDangerous.sql")
|
|
629
|
+
|
|
588
630
|
self.dbPath = Path(tdenv.dbFilename or dataPath / TradeDB.defaultDB)
|
|
589
631
|
self.sqlPath = dataPath / Path(tdenv.sqlFilename or TradeDB.defaultSQL)
|
|
590
|
-
pricePath
|
|
632
|
+
pricePath = Path(tdenv.pricesFilename or TradeDB.defaultPrices)
|
|
591
633
|
self.pricesPath = dataPath / pricePath
|
|
634
|
+
|
|
592
635
|
self.importTables = [
|
|
593
636
|
(str(self.csvPath / Path(fn)), tn)
|
|
594
637
|
for fn, tn in TradeDB.defaultTables
|
|
595
638
|
]
|
|
596
639
|
self.importPaths = {tn: tp for tp, tn in self.importTables}
|
|
597
|
-
|
|
598
|
-
self.dbFilename
|
|
599
|
-
self.sqlFilename
|
|
640
|
+
|
|
641
|
+
self.dbFilename = str(self.dbPath)
|
|
642
|
+
self.sqlFilename = str(self.sqlPath)
|
|
600
643
|
self.pricesFilename = str(self.pricesPath)
|
|
601
|
-
|
|
644
|
+
|
|
645
|
+
# --- Cache attributes (unchanged) ---
|
|
602
646
|
self.avgSelling, self.avgBuying = None, None
|
|
603
647
|
self.tradingStationCount = 0
|
|
604
|
-
self.addedByID
|
|
605
|
-
self.systemByID
|
|
606
|
-
self.systemByName
|
|
607
|
-
self.stellarGrid
|
|
608
|
-
self.stationByID
|
|
609
|
-
self.shipByID
|
|
610
|
-
self.categoryByID
|
|
611
|
-
self.itemByID
|
|
612
|
-
self.itemByName
|
|
613
|
-
self.itemByFDevID
|
|
614
|
-
self.rareItemByID
|
|
648
|
+
self.addedByID = None
|
|
649
|
+
self.systemByID = None
|
|
650
|
+
self.systemByName = None
|
|
651
|
+
self.stellarGrid = None
|
|
652
|
+
self.stationByID = None
|
|
653
|
+
self.shipByID = None
|
|
654
|
+
self.categoryByID = None
|
|
655
|
+
self.itemByID = None
|
|
656
|
+
self.itemByName = None
|
|
657
|
+
self.itemByFDevID = None
|
|
658
|
+
self.rareItemByID = None
|
|
615
659
|
self.rareItemByName = None
|
|
616
|
-
|
|
660
|
+
|
|
661
|
+
# --- Engine bootstrap ---
|
|
662
|
+
from .db import make_engine_from_config, get_session_factory
|
|
663
|
+
import os
|
|
664
|
+
|
|
665
|
+
cfg = getattr(tdenv, "dbConfig", None)
|
|
666
|
+
if not cfg:
|
|
667
|
+
cfg = os.environ.get("TD_DB_CONFIG", "db_config.ini")
|
|
668
|
+
|
|
669
|
+
self.engine = make_engine_from_config(cfg)
|
|
670
|
+
self.Session = get_session_factory(self.engine)
|
|
671
|
+
|
|
672
|
+
|
|
673
|
+
# --- Initial load ---
|
|
617
674
|
if load:
|
|
618
675
|
self.reloadCache()
|
|
619
676
|
self.load(maxSystemLinkLy=tdenv.maxSystemLinkLy)
|
|
677
|
+
|
|
678
|
+
# ------------------------------------------------------------------
|
|
679
|
+
# Legacy compatibility dataPath shim
|
|
680
|
+
# ------------------------------------------------------------------
|
|
681
|
+
@property
|
|
682
|
+
def dataDir(self):
|
|
683
|
+
"""
|
|
684
|
+
Legacy alias for self.dataPath (removed in SQLAlchemy refactor).
|
|
685
|
+
Falls back to './data' if configuration not yet loaded.
|
|
686
|
+
"""
|
|
687
|
+
# Try the modern attribute first
|
|
688
|
+
if hasattr(self, "dataPath") and self.dataPath:
|
|
689
|
+
return self.dataPath
|
|
690
|
+
# If we have an environment object, use its dataDir
|
|
691
|
+
if hasattr(self, "tdenv") and getattr(self.tdenv, "dataDir", None):
|
|
692
|
+
return self.tdenv.dataDir
|
|
693
|
+
# Final fallback (first run, pre-bootstrap)
|
|
694
|
+
return Path("./data")
|
|
695
|
+
|
|
620
696
|
|
|
621
697
|
@staticmethod
|
|
622
698
|
def calculateDistance2(lx, ly, lz, rx, ry, rz):
|
|
@@ -637,85 +713,85 @@ class TradeDB:
|
|
|
637
713
|
############################################################
|
|
638
714
|
# Access to the underlying database.
|
|
639
715
|
|
|
640
|
-
def getDB(self)
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
"""
|
|
660
|
-
|
|
716
|
+
def getDB(self):
|
|
717
|
+
"""
|
|
718
|
+
Return a new SQLAlchemy Session bound to this TradeDB engine.
|
|
719
|
+
"""
|
|
720
|
+
if not self.engine:
|
|
721
|
+
raise TradeException("Database engine not initialised")
|
|
722
|
+
return self.Session()
|
|
723
|
+
|
|
724
|
+
def query(self, sql: str, *params):
|
|
725
|
+
"""
|
|
726
|
+
Execute a SQL statement via the SQLAlchemy engine and return the result cursor.
|
|
727
|
+
"""
|
|
728
|
+
from sqlalchemy import text
|
|
729
|
+
with self.engine.connect() as conn:
|
|
730
|
+
return conn.execute(text(sql), params)
|
|
731
|
+
|
|
732
|
+
def queryColumn(self, sql: str, *params):
|
|
733
|
+
"""
|
|
734
|
+
Execute a SQL statement and return the first column of the first row.
|
|
735
|
+
"""
|
|
736
|
+
result = self.query(sql, *params).first()
|
|
737
|
+
return result[0] if result else None
|
|
738
|
+
|
|
661
739
|
|
|
662
740
|
def reloadCache(self):
|
|
663
741
|
"""
|
|
664
|
-
|
|
742
|
+
Ensure DB is present and minimally populated using the central policy.
|
|
743
|
+
|
|
744
|
+
Delegates sanity checks to lifecycle.ensure_fresh_db (seconds-only checks):
|
|
745
|
+
- core tables exist (System, Station, Category, Item, StationItem)
|
|
746
|
+
- each has a primary key
|
|
747
|
+
- seed rows exist (Category > 0, System > 0)
|
|
748
|
+
- cheap connectivity probe
|
|
749
|
+
|
|
750
|
+
If checks fail (or lifecycle decides to force), it will call buildCache(self, self.tdenv)
|
|
751
|
+
to reset/populate via the authoritative path. Otherwise it is a no-op.
|
|
665
752
|
"""
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
)
|
|
694
|
-
return
|
|
695
|
-
|
|
696
|
-
self.tdenv.DEBUG0("Rebuilding DB Cache [{}]", str(changedPaths))
|
|
697
|
-
else:
|
|
698
|
-
self.tdenv.DEBUG0("Building DB Cache")
|
|
699
|
-
|
|
700
|
-
cache.buildCache(self, self.tdenv)
|
|
753
|
+
from tradedangerous.db.lifecycle import ensure_fresh_db
|
|
754
|
+
|
|
755
|
+
self.tdenv.DEBUG0("reloadCache: engine URL = {}", str(self.engine.url))
|
|
756
|
+
|
|
757
|
+
try:
|
|
758
|
+
summary = ensure_fresh_db(
|
|
759
|
+
backend=self.engine.dialect.name,
|
|
760
|
+
engine=self.engine,
|
|
761
|
+
data_dir=self.dataPath,
|
|
762
|
+
metadata=None,
|
|
763
|
+
mode="auto",
|
|
764
|
+
tdb=self,
|
|
765
|
+
tdenv=self.tdenv,
|
|
766
|
+
)
|
|
767
|
+
action = summary.get("action", "kept")
|
|
768
|
+
reason = summary.get("reason")
|
|
769
|
+
if reason:
|
|
770
|
+
self.tdenv.DEBUG0("reloadCache: ensure_fresh_db → {} (reason: {})", action, reason)
|
|
771
|
+
else:
|
|
772
|
+
self.tdenv.DEBUG0("reloadCache: ensure_fresh_db → {}", action)
|
|
773
|
+
except Exception as e:
|
|
774
|
+
self.tdenv.WARN("reloadCache: ensure_fresh_db failed: {}", e)
|
|
775
|
+
self.tdenv.DEBUG0("reloadCache: Falling back to buildCache()")
|
|
776
|
+
from tradedangerous import cache
|
|
777
|
+
cache.buildCache(self, self.tdenv)
|
|
778
|
+
|
|
779
|
+
|
|
701
780
|
|
|
702
781
|
############################################################
|
|
703
782
|
# Load "added" data.
|
|
704
783
|
|
|
705
784
|
def _loadAdded(self):
|
|
706
785
|
"""
|
|
707
|
-
Loads the Added table as a simple dictionary
|
|
708
|
-
"""
|
|
709
|
-
stmt = """
|
|
710
|
-
SELECT added_id, name
|
|
711
|
-
FROM Added
|
|
786
|
+
Loads the Added table as a simple dictionary.
|
|
712
787
|
"""
|
|
713
788
|
addedByID = {}
|
|
714
|
-
with
|
|
715
|
-
for
|
|
716
|
-
addedByID[
|
|
789
|
+
with self.Session() as session:
|
|
790
|
+
for row in session.query(Added.added_id, Added.name):
|
|
791
|
+
addedByID[row.added_id] = row.name
|
|
717
792
|
self.addedByID = addedByID
|
|
718
793
|
self.tdenv.DEBUG1("Loaded {:n} Addeds", len(addedByID))
|
|
794
|
+
|
|
719
795
|
|
|
720
796
|
def lookupAdded(self, name):
|
|
721
797
|
name = name.lower()
|
|
@@ -733,24 +809,33 @@ class TradeDB:
|
|
|
733
809
|
|
|
734
810
|
def _loadSystems(self):
|
|
735
811
|
"""
|
|
736
|
-
Initial load the
|
|
812
|
+
Initial load of the list of systems via SQLAlchemy.
|
|
737
813
|
CAUTION: Will orphan previously loaded objects.
|
|
738
814
|
"""
|
|
739
|
-
stmt = """
|
|
740
|
-
SELECT system_id,
|
|
741
|
-
name, pos_x, pos_y, pos_z,
|
|
742
|
-
added_id
|
|
743
|
-
FROM System
|
|
744
|
-
"""
|
|
745
|
-
|
|
746
815
|
systemByID, systemByName = {}, {}
|
|
747
|
-
with
|
|
748
|
-
for
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
816
|
+
with self.Session() as session:
|
|
817
|
+
for row in session.query(
|
|
818
|
+
SA_System.system_id,
|
|
819
|
+
SA_System.name,
|
|
820
|
+
SA_System.pos_x,
|
|
821
|
+
SA_System.pos_y,
|
|
822
|
+
SA_System.pos_z,
|
|
823
|
+
SA_System.added_id,
|
|
824
|
+
):
|
|
825
|
+
system = System(
|
|
826
|
+
row.system_id,
|
|
827
|
+
row.name,
|
|
828
|
+
row.pos_x,
|
|
829
|
+
row.pos_y,
|
|
830
|
+
row.pos_z,
|
|
831
|
+
row.added_id,
|
|
832
|
+
)
|
|
833
|
+
systemByID[row.system_id] = system
|
|
834
|
+
systemByName[row.name.upper()] = system
|
|
835
|
+
|
|
752
836
|
self.systemByID, self.systemByName = systemByID, systemByName
|
|
753
837
|
self.tdenv.DEBUG1("Loaded {:n} Systems", len(systemByID))
|
|
838
|
+
|
|
754
839
|
|
|
755
840
|
def lookupSystem(self, key):
|
|
756
841
|
"""
|
|
@@ -769,40 +854,43 @@ class TradeDB:
|
|
|
769
854
|
self,
|
|
770
855
|
name,
|
|
771
856
|
x, y, z,
|
|
772
|
-
added="Local",
|
|
773
857
|
modified='now',
|
|
774
858
|
commit=True,
|
|
775
859
|
):
|
|
776
860
|
"""
|
|
777
|
-
Add a system to the local cache and memory copy.
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
861
|
+
Add a system to the local cache and memory copy using SQLAlchemy.
|
|
862
|
+
Note: 'added' field has been deprecated and is no longer populated.
|
|
863
|
+
"""
|
|
864
|
+
with self.Session() as session:
|
|
865
|
+
# Create ORM System row (added_id is deprecated → NULL)
|
|
866
|
+
orm_system = SA_System(
|
|
867
|
+
name=name,
|
|
868
|
+
pos_x=x,
|
|
869
|
+
pos_y=y,
|
|
870
|
+
pos_z=z,
|
|
871
|
+
added_id=None,
|
|
872
|
+
modified=None if modified == 'now' else modified,
|
|
873
|
+
)
|
|
874
|
+
session.add(orm_system)
|
|
875
|
+
if commit:
|
|
876
|
+
session.commit()
|
|
877
|
+
else:
|
|
878
|
+
session.flush()
|
|
879
|
+
|
|
880
|
+
ID = orm_system.system_id
|
|
881
|
+
|
|
882
|
+
# Maintain legacy wrapper + caches (added_id always None now)
|
|
883
|
+
system = System(ID, name.upper(), x, y, z, None)
|
|
795
884
|
self.systemByID[ID] = system
|
|
796
885
|
self.systemByName[system.dbname] = system
|
|
797
|
-
|
|
798
|
-
db.commit()
|
|
886
|
+
|
|
799
887
|
self.tdenv.NOTE(
|
|
800
888
|
"Added new system #{}: {} [{},{},{}]",
|
|
801
889
|
ID, name, x, y, z
|
|
802
890
|
)
|
|
803
|
-
# Invalidate the grid
|
|
804
891
|
self.stellarGrid = None
|
|
805
892
|
return system
|
|
893
|
+
|
|
806
894
|
|
|
807
895
|
def updateLocalSystem(
|
|
808
896
|
self, system,
|
|
@@ -811,69 +899,92 @@ class TradeDB:
|
|
|
811
899
|
commit=True,
|
|
812
900
|
):
|
|
813
901
|
"""
|
|
814
|
-
|
|
902
|
+
Update an entry for a local system using SQLAlchemy.
|
|
815
903
|
"""
|
|
816
904
|
oldname = system.dbname
|
|
817
905
|
dbname = name.upper()
|
|
906
|
+
|
|
818
907
|
if not force:
|
|
819
|
-
if oldname == dbname and
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
908
|
+
if (oldname == dbname and
|
|
909
|
+
system.posX == x and
|
|
910
|
+
system.posY == y and
|
|
911
|
+
system.posZ == z):
|
|
823
912
|
return False
|
|
913
|
+
|
|
824
914
|
del self.systemByName[oldname]
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
915
|
+
|
|
916
|
+
with self.Session() as session:
|
|
917
|
+
# Find Added row for added_id
|
|
918
|
+
added_row = session.query(Added).filter(Added.name == added).first()
|
|
919
|
+
if not added_row:
|
|
920
|
+
raise TradeException(f"Added entry not found: {added}")
|
|
921
|
+
|
|
922
|
+
# Load ORM System row
|
|
923
|
+
orm_system = session.get(SA_System, system.ID)
|
|
924
|
+
if not orm_system:
|
|
925
|
+
raise TradeException(f"System ID not found: {system.ID}")
|
|
926
|
+
|
|
927
|
+
# Apply updates
|
|
928
|
+
orm_system.name = dbname
|
|
929
|
+
orm_system.pos_x = x
|
|
930
|
+
orm_system.pos_y = y
|
|
931
|
+
orm_system.pos_z = z
|
|
932
|
+
orm_system.added_id = added_row.added_id
|
|
933
|
+
orm_system.modified = None if modified == 'now' else modified
|
|
934
|
+
|
|
935
|
+
if commit:
|
|
936
|
+
session.commit()
|
|
937
|
+
else:
|
|
938
|
+
session.flush()
|
|
939
|
+
|
|
839
940
|
self.tdenv.NOTE(
|
|
840
941
|
"{} (#{}) updated in {}: {}, {}, {}, {}, {}, {}",
|
|
841
942
|
oldname, system.ID,
|
|
842
943
|
self.dbPath if self.tdenv.detail > 1 else "local db",
|
|
843
|
-
dbname,
|
|
844
|
-
x, y, z,
|
|
845
|
-
added, modified,
|
|
944
|
+
dbname, x, y, z, added, modified,
|
|
846
945
|
)
|
|
946
|
+
|
|
947
|
+
# Update wrapper caches
|
|
948
|
+
system.name = dbname
|
|
949
|
+
system.posX, system.posY, system.posZ = x, y, z
|
|
950
|
+
system.addedID = added_row.added_id
|
|
847
951
|
self.systemByName[dbname] = system
|
|
848
|
-
|
|
952
|
+
|
|
849
953
|
return True
|
|
954
|
+
|
|
850
955
|
|
|
851
956
|
def removeLocalSystem(
|
|
852
957
|
self, system,
|
|
853
958
|
commit=True,
|
|
854
959
|
):
|
|
855
|
-
"""
|
|
960
|
+
"""Remove a system and its stations from the local DB using SQLAlchemy."""
|
|
961
|
+
# First remove stations attached to this system
|
|
856
962
|
for stn in self.stations():
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
963
|
+
if stn.system == system:
|
|
964
|
+
self.removeLocalStation(stn, commit=False)
|
|
965
|
+
|
|
966
|
+
with self.Session() as session:
|
|
967
|
+
orm_system = session.get(SA_System, system.ID)
|
|
968
|
+
if orm_system:
|
|
969
|
+
session.delete(orm_system)
|
|
970
|
+
if commit:
|
|
971
|
+
session.commit()
|
|
972
|
+
else:
|
|
973
|
+
session.flush()
|
|
974
|
+
|
|
975
|
+
# Update caches
|
|
866
976
|
del self.systemByName[system.dbname]
|
|
867
977
|
del self.systemByID[system.ID]
|
|
868
|
-
|
|
978
|
+
|
|
869
979
|
self.tdenv.NOTE(
|
|
870
980
|
"{} (#{}) deleted from {}",
|
|
871
|
-
system.name
|
|
981
|
+
system.name, system.ID,
|
|
872
982
|
self.dbPath if self.tdenv.detail > 1 else "local db",
|
|
873
983
|
)
|
|
874
|
-
|
|
984
|
+
|
|
875
985
|
system.dbname = "DELETED " + system.dbname
|
|
876
986
|
del system
|
|
987
|
+
|
|
877
988
|
|
|
878
989
|
def __buildStellarGrid(self):
|
|
879
990
|
"""
|
|
@@ -1134,61 +1245,80 @@ class TradeDB:
|
|
|
1134
1245
|
|
|
1135
1246
|
def _loadStations(self):
|
|
1136
1247
|
"""
|
|
1137
|
-
Populate the Station list.
|
|
1248
|
+
Populate the Station list using SQLAlchemy.
|
|
1138
1249
|
Station constructor automatically adds itself to the System object.
|
|
1139
1250
|
CAUTION: Will orphan previously loaded objects.
|
|
1140
1251
|
"""
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
ls_from_star, market, blackmarket, shipyard,
|
|
1144
|
-
max_pad_size, outfitting, rearm, refuel, repair, planetary, type_id
|
|
1145
|
-
FROM Station
|
|
1146
|
-
"""
|
|
1147
|
-
|
|
1252
|
+
# NOTE: Requires module-level import:
|
|
1253
|
+
# from tradedangerous.db.utils import age_in_days
|
|
1148
1254
|
stationByID = {}
|
|
1149
1255
|
systemByID = self.systemByID
|
|
1150
1256
|
self.tradingStationCount = 0
|
|
1257
|
+
|
|
1151
1258
|
# Fleet Carriers are station type 24.
|
|
1152
1259
|
# Odyssey settlements are station type 25.
|
|
1153
1260
|
# Assume type 0 (Unknown) are also Fleet Carriers.
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
with
|
|
1261
|
+
types = {'fleet-carrier': [24, 0], 'odyssey': [25]}
|
|
1262
|
+
|
|
1263
|
+
with self.Session() as session:
|
|
1264
|
+
# Query all stations
|
|
1265
|
+
rows = session.query(
|
|
1266
|
+
SA_Station.station_id,
|
|
1267
|
+
SA_Station.system_id,
|
|
1268
|
+
SA_Station.name,
|
|
1269
|
+
SA_Station.ls_from_star,
|
|
1270
|
+
SA_Station.market,
|
|
1271
|
+
SA_Station.blackmarket,
|
|
1272
|
+
SA_Station.shipyard,
|
|
1273
|
+
SA_Station.max_pad_size,
|
|
1274
|
+
SA_Station.outfitting,
|
|
1275
|
+
SA_Station.rearm,
|
|
1276
|
+
SA_Station.refuel,
|
|
1277
|
+
SA_Station.repair,
|
|
1278
|
+
SA_Station.planetary,
|
|
1279
|
+
SA_Station.type_id,
|
|
1280
|
+
)
|
|
1157
1281
|
for (
|
|
1158
1282
|
ID, systemID, name,
|
|
1159
1283
|
lsFromStar, market, blackMarket, shipyard,
|
|
1160
1284
|
maxPadSize, outfitting, rearm, refuel, repair, planetary, type_id
|
|
1161
|
-
) in
|
|
1162
|
-
isFleet
|
|
1285
|
+
) in rows:
|
|
1286
|
+
isFleet = 'Y' if int(type_id) in types['fleet-carrier'] else 'N'
|
|
1163
1287
|
isOdyssey = 'Y' if int(type_id) in types['odyssey'] else 'N'
|
|
1164
1288
|
station = Station(
|
|
1165
1289
|
ID, systemByID[systemID], name,
|
|
1166
1290
|
lsFromStar, market, blackMarket, shipyard,
|
|
1167
|
-
maxPadSize, outfitting, rearm, refuel, repair,
|
|
1291
|
+
maxPadSize, outfitting, rearm, refuel, repair,
|
|
1292
|
+
planetary, isFleet, isOdyssey,
|
|
1168
1293
|
0, None,
|
|
1169
1294
|
)
|
|
1170
1295
|
stationByID[ID] = station
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1296
|
+
|
|
1297
|
+
# Trading station info
|
|
1298
|
+
tradingCount = 0
|
|
1299
|
+
rows = (
|
|
1300
|
+
session.query(
|
|
1301
|
+
SA_StationItem.station_id,
|
|
1302
|
+
func.count().label("item_count"),
|
|
1303
|
+
# Dialect-safe average age in **days**
|
|
1304
|
+
func.avg(age_in_days(session, SA_StationItem.modified)).label("data_age_days"),
|
|
1305
|
+
)
|
|
1306
|
+
.group_by(SA_StationItem.station_id)
|
|
1307
|
+
.having(func.count() > 0)
|
|
1308
|
+
)
|
|
1309
|
+
|
|
1310
|
+
for ID, itemCount, dataAge in rows:
|
|
1183
1311
|
station = stationByID[ID]
|
|
1184
1312
|
station.itemCount = itemCount
|
|
1185
1313
|
station.dataAge = dataAge
|
|
1186
1314
|
tradingCount += 1
|
|
1187
|
-
|
|
1315
|
+
|
|
1188
1316
|
self.stationByID = stationByID
|
|
1189
1317
|
self.tradingStationCount = tradingCount
|
|
1190
1318
|
self.tdenv.DEBUG1("Loaded {:n} Stations", len(stationByID))
|
|
1191
1319
|
self.stellarGrid = None
|
|
1320
|
+
|
|
1321
|
+
|
|
1192
1322
|
|
|
1193
1323
|
def addLocalStation(
|
|
1194
1324
|
self,
|
|
@@ -1210,18 +1340,18 @@ class TradeDB:
|
|
|
1210
1340
|
commit=True,
|
|
1211
1341
|
):
|
|
1212
1342
|
"""
|
|
1213
|
-
Add a station to the local cache and memory copy.
|
|
1343
|
+
Add a station to the local cache and memory copy using SQLAlchemy.
|
|
1214
1344
|
"""
|
|
1215
|
-
|
|
1216
|
-
market
|
|
1345
|
+
# Normalise/validate inputs
|
|
1346
|
+
market = market.upper()
|
|
1217
1347
|
blackMarket = blackMarket.upper()
|
|
1218
|
-
shipyard
|
|
1219
|
-
maxPadSize
|
|
1220
|
-
outfitting
|
|
1221
|
-
rearm
|
|
1222
|
-
refuel
|
|
1223
|
-
repair
|
|
1224
|
-
planetary
|
|
1348
|
+
shipyard = shipyard.upper()
|
|
1349
|
+
maxPadSize = maxPadSize.upper()
|
|
1350
|
+
outfitting = outfitting.upper()
|
|
1351
|
+
rearm = rearm.upper()
|
|
1352
|
+
refuel = refuel.upper()
|
|
1353
|
+
repair = repair.upper()
|
|
1354
|
+
planetary = planetary.upper()
|
|
1225
1355
|
assert market in "?YN"
|
|
1226
1356
|
assert blackMarket in "?YN"
|
|
1227
1357
|
assert shipyard in "?YN"
|
|
@@ -1230,37 +1360,42 @@ class TradeDB:
|
|
|
1230
1360
|
assert rearm in "?YN"
|
|
1231
1361
|
assert refuel in "?YN"
|
|
1232
1362
|
assert repair in "?YN"
|
|
1233
|
-
assert planetary in
|
|
1234
|
-
assert fleet in
|
|
1235
|
-
assert odyssey in
|
|
1236
|
-
|
|
1363
|
+
assert planetary in "?YN"
|
|
1364
|
+
assert fleet in "?YN"
|
|
1365
|
+
assert odyssey in "?YN"
|
|
1366
|
+
|
|
1367
|
+
# Type mapping
|
|
1237
1368
|
type_id = 0
|
|
1238
1369
|
if fleet == 'Y':
|
|
1239
1370
|
type_id = 24
|
|
1240
1371
|
if odyssey == 'Y':
|
|
1241
1372
|
type_id = 25
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1373
|
+
|
|
1374
|
+
with self.Session() as session:
|
|
1375
|
+
orm_station = SA_Station(
|
|
1376
|
+
name=name,
|
|
1377
|
+
system_id=system.ID,
|
|
1378
|
+
ls_from_star=lsFromStar,
|
|
1379
|
+
market=market,
|
|
1380
|
+
blackmarket=blackMarket,
|
|
1381
|
+
shipyard=shipyard,
|
|
1382
|
+
max_pad_size=maxPadSize,
|
|
1383
|
+
outfitting=outfitting,
|
|
1384
|
+
rearm=rearm,
|
|
1385
|
+
refuel=refuel,
|
|
1386
|
+
repair=repair,
|
|
1387
|
+
planetary=planetary,
|
|
1388
|
+
type_id=type_id,
|
|
1389
|
+
modified=None if modified == 'now' else modified,
|
|
1256
1390
|
)
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1391
|
+
session.add(orm_station)
|
|
1392
|
+
if commit:
|
|
1393
|
+
session.commit()
|
|
1394
|
+
else:
|
|
1395
|
+
session.flush()
|
|
1396
|
+
ID = orm_station.station_id
|
|
1397
|
+
|
|
1398
|
+
# Legacy wrapper object
|
|
1264
1399
|
station = Station(
|
|
1265
1400
|
ID, system, name,
|
|
1266
1401
|
lsFromStar=lsFromStar,
|
|
@@ -1273,13 +1408,13 @@ class TradeDB:
|
|
|
1273
1408
|
refuel=refuel,
|
|
1274
1409
|
repair=repair,
|
|
1275
1410
|
planetary=planetary,
|
|
1276
|
-
fleet=
|
|
1277
|
-
odyssey=
|
|
1278
|
-
itemCount=0,
|
|
1411
|
+
fleet=fleet,
|
|
1412
|
+
odyssey=odyssey,
|
|
1413
|
+
itemCount=0,
|
|
1414
|
+
dataAge=0,
|
|
1279
1415
|
)
|
|
1280
1416
|
self.stationByID[ID] = station
|
|
1281
|
-
|
|
1282
|
-
db.commit()
|
|
1417
|
+
|
|
1283
1418
|
self.tdenv.NOTE(
|
|
1284
1419
|
"{} (#{}) added to {}: "
|
|
1285
1420
|
"ls={}, mkt={}, bm={}, yard={}, pad={}, "
|
|
@@ -1313,36 +1448,35 @@ class TradeDB:
|
|
|
1313
1448
|
commit=True,
|
|
1314
1449
|
):
|
|
1315
1450
|
"""
|
|
1316
|
-
Alter the properties of a station in-memory and in the DB.
|
|
1451
|
+
Alter the properties of a station in-memory and in the DB using SQLAlchemy.
|
|
1317
1452
|
"""
|
|
1318
1453
|
changes = []
|
|
1319
|
-
|
|
1454
|
+
|
|
1320
1455
|
def _changed(label, old, new):
|
|
1321
|
-
changes.append(
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1456
|
+
changes.append(f"{label}('{old}'=>'{new}')")
|
|
1457
|
+
|
|
1458
|
+
# Mutate wrapper + record changes
|
|
1325
1459
|
if name is not None:
|
|
1326
1460
|
if force or name.upper() != station.dbname.upper():
|
|
1327
1461
|
_changed("name", station.dbname, name)
|
|
1328
1462
|
station.dbname = name
|
|
1329
|
-
|
|
1463
|
+
|
|
1330
1464
|
if lsFromStar is not None:
|
|
1331
1465
|
assert lsFromStar >= 0
|
|
1332
1466
|
if lsFromStar != station.lsFromStar:
|
|
1333
1467
|
if lsFromStar > 0 or force:
|
|
1334
1468
|
_changed("ls", station.lsFromStar, lsFromStar)
|
|
1335
1469
|
station.lsFromStar = lsFromStar
|
|
1336
|
-
|
|
1337
|
-
def _check_setting(label,
|
|
1470
|
+
|
|
1471
|
+
def _check_setting(label, attr_name, newValue, allowed):
|
|
1338
1472
|
if newValue is not None:
|
|
1339
1473
|
newValue = newValue.upper()
|
|
1340
1474
|
assert newValue in allowed
|
|
1341
|
-
oldValue = getattr(station,
|
|
1475
|
+
oldValue = getattr(station, attr_name, '?')
|
|
1342
1476
|
if newValue != oldValue and (force or newValue != '?'):
|
|
1343
1477
|
_changed(label, oldValue, newValue)
|
|
1344
|
-
setattr(station,
|
|
1345
|
-
|
|
1478
|
+
setattr(station, attr_name, newValue)
|
|
1479
|
+
|
|
1346
1480
|
_check_setting("pad", "maxPadSize", maxPadSize, TradeDB.padSizes)
|
|
1347
1481
|
_check_setting("mkt", "market", market, TradeDB.marketStates)
|
|
1348
1482
|
_check_setting("blk", "blackMarket", blackMarket, TradeDB.marketStates)
|
|
@@ -1354,85 +1488,78 @@ class TradeDB:
|
|
|
1354
1488
|
_check_setting("plt", "planetary", planetary, TradeDB.planetStates)
|
|
1355
1489
|
_check_setting("flc", "fleet", fleet, TradeDB.fleetStates)
|
|
1356
1490
|
_check_setting("ody", "odyssey", odyssey, TradeDB.odysseyStates)
|
|
1357
|
-
|
|
1491
|
+
|
|
1358
1492
|
if not changes:
|
|
1359
1493
|
return False
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
modified,
|
|
1390
|
-
station.ID
|
|
1391
|
-
])
|
|
1392
|
-
if commit:
|
|
1393
|
-
db.commit()
|
|
1394
|
-
|
|
1494
|
+
|
|
1495
|
+
with self.Session() as session:
|
|
1496
|
+
orm_station = session.get(SA_Station, station.ID)
|
|
1497
|
+
if not orm_station:
|
|
1498
|
+
raise TradeException(f"Station ID not found: {station.ID}")
|
|
1499
|
+
|
|
1500
|
+
orm_station.name = station.dbname
|
|
1501
|
+
orm_station.system_id = station.system.ID
|
|
1502
|
+
orm_station.ls_from_star = station.lsFromStar
|
|
1503
|
+
orm_station.market = station.market
|
|
1504
|
+
orm_station.blackmarket = station.blackMarket
|
|
1505
|
+
orm_station.shipyard = station.shipyard
|
|
1506
|
+
orm_station.max_pad_size = station.maxPadSize
|
|
1507
|
+
orm_station.outfitting = station.outfitting
|
|
1508
|
+
orm_station.rearm = station.rearm
|
|
1509
|
+
orm_station.refuel = station.refuel
|
|
1510
|
+
orm_station.repair = station.repair
|
|
1511
|
+
orm_station.planetary = station.planetary
|
|
1512
|
+
orm_station.type_id = (
|
|
1513
|
+
24 if station.fleet == 'Y' else
|
|
1514
|
+
25 if station.odyssey == 'Y' else 0
|
|
1515
|
+
)
|
|
1516
|
+
orm_station.modified = None if modified == 'now' else modified
|
|
1517
|
+
|
|
1518
|
+
if commit:
|
|
1519
|
+
session.commit()
|
|
1520
|
+
else:
|
|
1521
|
+
session.flush()
|
|
1522
|
+
|
|
1395
1523
|
self.tdenv.NOTE(
|
|
1396
1524
|
"{} (#{}) updated in {}: {}",
|
|
1397
1525
|
station.name(), station.ID,
|
|
1398
1526
|
self.dbPath if self.tdenv.detail > 1 else "local db",
|
|
1399
1527
|
", ".join(changes)
|
|
1400
1528
|
)
|
|
1401
|
-
|
|
1529
|
+
|
|
1402
1530
|
return True
|
|
1403
1531
|
|
|
1404
1532
|
def removeLocalStation(self, station, commit=True):
|
|
1405
1533
|
"""
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
Becareful of any references to the station you may still have
|
|
1409
|
-
after this.
|
|
1534
|
+
Remove a station from the local database and memory image using SQLAlchemy.
|
|
1535
|
+
Be careful of any references to the station you may still have after this.
|
|
1410
1536
|
"""
|
|
1411
|
-
|
|
1412
|
-
# Remove reference from my system
|
|
1537
|
+
# Remove reference from parent system (wrapper-level)
|
|
1413
1538
|
system = station.system
|
|
1414
|
-
system.stations
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1539
|
+
if station in system.stations:
|
|
1540
|
+
system.stations.remove(station)
|
|
1541
|
+
|
|
1542
|
+
# Remove from ID lookup cache
|
|
1543
|
+
if station.ID in self.stationByID:
|
|
1544
|
+
del self.stationByID[station.ID]
|
|
1545
|
+
|
|
1546
|
+
# Delete from DB
|
|
1547
|
+
with self.Session() as session:
|
|
1548
|
+
orm_station = session.get(SA_Station, station.ID)
|
|
1549
|
+
if orm_station:
|
|
1550
|
+
session.delete(orm_station)
|
|
1551
|
+
if commit:
|
|
1552
|
+
session.commit()
|
|
1553
|
+
else:
|
|
1554
|
+
session.flush()
|
|
1555
|
+
|
|
1429
1556
|
self.tdenv.NOTE(
|
|
1430
1557
|
"{} (#{}) deleted from {}",
|
|
1431
1558
|
station.name(), station.ID,
|
|
1432
1559
|
self.dbPath if self.tdenv.detail > 1 else "local db",
|
|
1433
1560
|
)
|
|
1434
|
-
|
|
1435
|
-
station.dbname = "DELETED "+station.dbname
|
|
1561
|
+
|
|
1562
|
+
station.dbname = "DELETED " + station.dbname
|
|
1436
1563
|
del station
|
|
1437
1564
|
|
|
1438
1565
|
def lookupPlace(self, name):
|
|
@@ -1771,19 +1898,22 @@ class TradeDB:
|
|
|
1771
1898
|
|
|
1772
1899
|
def _loadShips(self):
|
|
1773
1900
|
"""
|
|
1774
|
-
Populate the Ship list.
|
|
1901
|
+
Populate the Ship list using SQLAlchemy.
|
|
1775
1902
|
CAUTION: Will orphan previously loaded objects.
|
|
1776
1903
|
"""
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1904
|
+
with self.Session() as session:
|
|
1905
|
+
rows = session.query(
|
|
1906
|
+
SA_Ship.ship_id,
|
|
1907
|
+
SA_Ship.name,
|
|
1908
|
+
SA_Ship.cost,
|
|
1909
|
+
)
|
|
1910
|
+
self.shipByID = {
|
|
1911
|
+
row.ship_id: Ship(row.ship_id, row.name, row.cost, stations=[])
|
|
1912
|
+
for row in rows
|
|
1913
|
+
}
|
|
1914
|
+
|
|
1786
1915
|
self.tdenv.DEBUG1("Loaded {} Ships", len(self.shipByID))
|
|
1916
|
+
|
|
1787
1917
|
|
|
1788
1918
|
def lookupShip(self, name):
|
|
1789
1919
|
"""
|
|
@@ -1806,17 +1936,17 @@ class TradeDB:
|
|
|
1806
1936
|
|
|
1807
1937
|
def _loadCategories(self):
|
|
1808
1938
|
"""
|
|
1809
|
-
Populate the list of item categories.
|
|
1939
|
+
Populate the list of item categories using SQLAlchemy.
|
|
1810
1940
|
CAUTION: Will orphan previously loaded objects.
|
|
1811
1941
|
"""
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1942
|
+
with self.Session() as session:
|
|
1943
|
+
rows = session.query(
|
|
1944
|
+
SA_Category.category_id,
|
|
1945
|
+
SA_Category.name,
|
|
1946
|
+
)
|
|
1817
1947
|
self.categoryByID = {
|
|
1818
|
-
|
|
1819
|
-
for
|
|
1948
|
+
row.category_id: Category(row.category_id, row.name, [])
|
|
1949
|
+
for row in rows
|
|
1820
1950
|
}
|
|
1821
1951
|
|
|
1822
1952
|
self.tdenv.DEBUG1("Loaded {} Categories", len(self.categoryByID))
|
|
@@ -1837,16 +1967,19 @@ class TradeDB:
|
|
|
1837
1967
|
|
|
1838
1968
|
def _loadItems(self):
|
|
1839
1969
|
"""
|
|
1840
|
-
Populate the Item list.
|
|
1970
|
+
Populate the Item list using SQLAlchemy.
|
|
1841
1971
|
CAUTION: Will orphan previously loaded objects.
|
|
1842
1972
|
"""
|
|
1843
|
-
stmt = """
|
|
1844
|
-
SELECT item_id, name, category_id, avg_price, fdev_id
|
|
1845
|
-
FROM Item
|
|
1846
|
-
"""
|
|
1847
1973
|
itemByID, itemByName, itemByFDevID = {}, {}, {}
|
|
1848
|
-
with
|
|
1849
|
-
|
|
1974
|
+
with self.Session() as session:
|
|
1975
|
+
rows = session.query(
|
|
1976
|
+
SA_Item.item_id,
|
|
1977
|
+
SA_Item.name,
|
|
1978
|
+
SA_Item.category_id,
|
|
1979
|
+
SA_Item.avg_price,
|
|
1980
|
+
SA_Item.fdev_id,
|
|
1981
|
+
)
|
|
1982
|
+
for ID, name, categoryID, avgPrice, fdevID in rows:
|
|
1850
1983
|
category = self.categoryByID[categoryID]
|
|
1851
1984
|
item = Item(
|
|
1852
1985
|
ID, name, category,
|
|
@@ -1857,17 +1990,13 @@ class TradeDB:
|
|
|
1857
1990
|
itemByName[name] = item
|
|
1858
1991
|
if fdevID:
|
|
1859
1992
|
itemByFDevID[fdevID] = item
|
|
1860
|
-
|
|
1861
1993
|
category.items.append(item)
|
|
1862
|
-
|
|
1994
|
+
|
|
1863
1995
|
self.itemByID = itemByID
|
|
1864
1996
|
self.itemByName = itemByName
|
|
1865
1997
|
self.itemByFDevID = itemByFDevID
|
|
1866
|
-
|
|
1867
|
-
self.tdenv.DEBUG1(
|
|
1868
|
-
"Loaded {:n} Items",
|
|
1869
|
-
len(self.itemByID)
|
|
1870
|
-
)
|
|
1998
|
+
|
|
1999
|
+
self.tdenv.DEBUG1("Loaded {:n} Items", len(self.itemByID))
|
|
1871
2000
|
|
|
1872
2001
|
def lookupItem(self, name):
|
|
1873
2002
|
"""
|
|
@@ -1881,87 +2010,107 @@ class TradeDB:
|
|
|
1881
2010
|
|
|
1882
2011
|
def getAverageSelling(self):
|
|
1883
2012
|
"""
|
|
1884
|
-
Query the database for average selling prices of all items.
|
|
2013
|
+
Query the database for average selling prices of all items using SQLAlchemy.
|
|
1885
2014
|
"""
|
|
1886
2015
|
if not self.avgSelling:
|
|
1887
2016
|
self.avgSelling = {itemID: 0 for itemID in self.itemByID}
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
2017
|
+
|
|
2018
|
+
with self.Session() as session:
|
|
2019
|
+
rows = (
|
|
2020
|
+
session.query(
|
|
2021
|
+
SA_Item.item_id,
|
|
2022
|
+
func.ifnull(func.avg(SA_StationItem.supply_price), 0),
|
|
2023
|
+
)
|
|
2024
|
+
.outerjoin(
|
|
2025
|
+
SA_StationItem,
|
|
2026
|
+
(SA_Item.item_id == SA_StationItem.item_id) &
|
|
2027
|
+
(SA_StationItem.supply_price > 0),
|
|
2028
|
+
)
|
|
2029
|
+
.filter(SA_StationItem.supply_price > 0)
|
|
2030
|
+
.group_by(SA_Item.item_id)
|
|
2031
|
+
)
|
|
2032
|
+
for ID, cr in rows:
|
|
2033
|
+
self.avgSelling[ID] = int(cr)
|
|
2034
|
+
|
|
1900
2035
|
return self.avgSelling
|
|
1901
|
-
|
|
2036
|
+
|
|
1902
2037
|
def getAverageBuying(self):
|
|
1903
2038
|
"""
|
|
1904
|
-
Query the database for average buying prices of all items.
|
|
2039
|
+
Query the database for average buying prices of all items using SQLAlchemy.
|
|
1905
2040
|
"""
|
|
1906
2041
|
if not self.avgBuying:
|
|
1907
2042
|
self.avgBuying = {itemID: 0 for itemID in self.itemByID}
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
2043
|
+
|
|
2044
|
+
with self.Session() as session:
|
|
2045
|
+
rows = (
|
|
2046
|
+
session.query(
|
|
2047
|
+
SA_Item.item_id,
|
|
2048
|
+
func.ifnull(func.avg(SA_StationItem.demand_price), 0),
|
|
2049
|
+
)
|
|
2050
|
+
.outerjoin(
|
|
2051
|
+
SA_StationItem,
|
|
2052
|
+
(SA_Item.item_id == SA_StationItem.item_id) &
|
|
2053
|
+
(SA_StationItem.demand_price > 0),
|
|
2054
|
+
)
|
|
2055
|
+
.filter(SA_StationItem.demand_price > 0)
|
|
2056
|
+
.group_by(SA_Item.item_id)
|
|
2057
|
+
)
|
|
2058
|
+
for ID, cr in rows:
|
|
2059
|
+
self.avgBuying[ID] = int(cr)
|
|
2060
|
+
|
|
1920
2061
|
return self.avgBuying
|
|
2062
|
+
|
|
1921
2063
|
|
|
1922
2064
|
############################################################
|
|
1923
2065
|
# Rare Items
|
|
1924
2066
|
|
|
1925
2067
|
def _loadRareItems(self):
|
|
1926
2068
|
"""
|
|
1927
|
-
Populate the RareItem list.
|
|
2069
|
+
Populate the RareItem list using SQLAlchemy.
|
|
1928
2070
|
"""
|
|
1929
|
-
stmt = """
|
|
1930
|
-
SELECT rare_id, station_id, category_id, name,
|
|
1931
|
-
cost, max_allocation, illegal, suppressed
|
|
1932
|
-
FROM RareItem
|
|
1933
|
-
"""
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
2071
|
rareItemByID, rareItemByName = {}, {}
|
|
1937
2072
|
stationByID = self.stationByID
|
|
1938
|
-
|
|
2073
|
+
|
|
2074
|
+
with self.Session() as session:
|
|
2075
|
+
rows = session.query(
|
|
2076
|
+
SA_RareItem.rare_id,
|
|
2077
|
+
SA_RareItem.station_id,
|
|
2078
|
+
SA_RareItem.category_id,
|
|
2079
|
+
SA_RareItem.name,
|
|
2080
|
+
SA_RareItem.cost,
|
|
2081
|
+
SA_RareItem.max_allocation,
|
|
2082
|
+
SA_RareItem.illegal,
|
|
2083
|
+
SA_RareItem.suppressed,
|
|
2084
|
+
)
|
|
1939
2085
|
for (
|
|
1940
2086
|
ID, stnID, catID, name,
|
|
1941
2087
|
cost, maxAlloc, illegal, suppressed
|
|
1942
|
-
) in
|
|
1943
|
-
station
|
|
2088
|
+
) in rows:
|
|
2089
|
+
station = stationByID[stnID]
|
|
1944
2090
|
category = self.categoryByID[catID]
|
|
1945
2091
|
rare = RareItem(
|
|
1946
|
-
ID, station, name,
|
|
2092
|
+
ID, station, name,
|
|
2093
|
+
cost, maxAlloc, illegal, suppressed,
|
|
1947
2094
|
category, f"{category.dbname}/{name}"
|
|
1948
2095
|
)
|
|
1949
|
-
rareItemByID[ID] =
|
|
1950
|
-
|
|
2096
|
+
rareItemByID[ID] = rare
|
|
2097
|
+
rareItemByName[name] = rare
|
|
2098
|
+
|
|
2099
|
+
self.rareItemByID = rareItemByID
|
|
1951
2100
|
self.rareItemByName = rareItemByName
|
|
1952
|
-
|
|
1953
|
-
self.tdenv.DEBUG1(
|
|
1954
|
-
|
|
1955
|
-
len(rareItemByID)
|
|
1956
|
-
)
|
|
2101
|
+
|
|
2102
|
+
self.tdenv.DEBUG1("Loaded {:n} RareItems", len(rareItemByID))
|
|
2103
|
+
|
|
1957
2104
|
|
|
1958
2105
|
############################################################
|
|
1959
2106
|
# Price data.
|
|
1960
2107
|
|
|
1961
2108
|
def close(self):
|
|
1962
|
-
if self.
|
|
1963
|
-
self.
|
|
1964
|
-
|
|
2109
|
+
if self.engine:
|
|
2110
|
+
self.engine.dispose()
|
|
2111
|
+
# Keep engine + Session references so reloadCache/buildCache can reuse them
|
|
2112
|
+
|
|
2113
|
+
|
|
1965
2114
|
|
|
1966
2115
|
def load(self, maxSystemLinkLy=None):
|
|
1967
2116
|
"""
|