tradedangerous 12.7.6__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.
Files changed (87) hide show
  1. py.typed +1 -0
  2. trade.py +49 -0
  3. tradedangerous/__init__.py +43 -0
  4. tradedangerous/cache.py +1381 -0
  5. tradedangerous/cli.py +136 -0
  6. tradedangerous/commands/TEMPLATE.py +74 -0
  7. tradedangerous/commands/__init__.py +244 -0
  8. tradedangerous/commands/buildcache_cmd.py +102 -0
  9. tradedangerous/commands/buy_cmd.py +427 -0
  10. tradedangerous/commands/commandenv.py +372 -0
  11. tradedangerous/commands/exceptions.py +94 -0
  12. tradedangerous/commands/export_cmd.py +150 -0
  13. tradedangerous/commands/import_cmd.py +222 -0
  14. tradedangerous/commands/local_cmd.py +243 -0
  15. tradedangerous/commands/market_cmd.py +207 -0
  16. tradedangerous/commands/nav_cmd.py +252 -0
  17. tradedangerous/commands/olddata_cmd.py +270 -0
  18. tradedangerous/commands/parsing.py +221 -0
  19. tradedangerous/commands/rares_cmd.py +298 -0
  20. tradedangerous/commands/run_cmd.py +1521 -0
  21. tradedangerous/commands/sell_cmd.py +262 -0
  22. tradedangerous/commands/shipvendor_cmd.py +60 -0
  23. tradedangerous/commands/station_cmd.py +68 -0
  24. tradedangerous/commands/trade_cmd.py +181 -0
  25. tradedangerous/commands/update_cmd.py +67 -0
  26. tradedangerous/corrections.py +55 -0
  27. tradedangerous/csvexport.py +234 -0
  28. tradedangerous/db/__init__.py +27 -0
  29. tradedangerous/db/adapter.py +192 -0
  30. tradedangerous/db/config.py +107 -0
  31. tradedangerous/db/engine.py +259 -0
  32. tradedangerous/db/lifecycle.py +332 -0
  33. tradedangerous/db/locks.py +208 -0
  34. tradedangerous/db/orm_models.py +500 -0
  35. tradedangerous/db/paths.py +113 -0
  36. tradedangerous/db/utils.py +661 -0
  37. tradedangerous/edscupdate.py +565 -0
  38. tradedangerous/edsmupdate.py +474 -0
  39. tradedangerous/formatting.py +210 -0
  40. tradedangerous/fs.py +156 -0
  41. tradedangerous/gui.py +1146 -0
  42. tradedangerous/mapping.py +133 -0
  43. tradedangerous/mfd/__init__.py +103 -0
  44. tradedangerous/mfd/saitek/__init__.py +3 -0
  45. tradedangerous/mfd/saitek/directoutput.py +678 -0
  46. tradedangerous/mfd/saitek/x52pro.py +195 -0
  47. tradedangerous/misc/checkpricebounds.py +287 -0
  48. tradedangerous/misc/clipboard.py +49 -0
  49. tradedangerous/misc/coord64.py +83 -0
  50. tradedangerous/misc/csvdialect.py +57 -0
  51. tradedangerous/misc/derp-sentinel.py +35 -0
  52. tradedangerous/misc/diff-system-csvs.py +159 -0
  53. tradedangerous/misc/eddb.py +81 -0
  54. tradedangerous/misc/eddn.py +349 -0
  55. tradedangerous/misc/edsc.py +437 -0
  56. tradedangerous/misc/edsm.py +121 -0
  57. tradedangerous/misc/importeddbstats.py +54 -0
  58. tradedangerous/misc/prices-json-exp.py +179 -0
  59. tradedangerous/misc/progress.py +194 -0
  60. tradedangerous/plugins/__init__.py +249 -0
  61. tradedangerous/plugins/edcd_plug.py +371 -0
  62. tradedangerous/plugins/eddblink_plug.py +861 -0
  63. tradedangerous/plugins/edmc_batch_plug.py +133 -0
  64. tradedangerous/plugins/spansh_plug.py +2647 -0
  65. tradedangerous/prices.py +211 -0
  66. tradedangerous/submit-distances.py +422 -0
  67. tradedangerous/templates/Added.csv +37 -0
  68. tradedangerous/templates/Category.csv +17 -0
  69. tradedangerous/templates/RareItem.csv +143 -0
  70. tradedangerous/templates/TradeDangerous.sql +338 -0
  71. tradedangerous/tools.py +40 -0
  72. tradedangerous/tradecalc.py +1302 -0
  73. tradedangerous/tradedb.py +2320 -0
  74. tradedangerous/tradeenv.py +313 -0
  75. tradedangerous/tradeenv.pyi +109 -0
  76. tradedangerous/tradeexcept.py +131 -0
  77. tradedangerous/tradeorm.py +183 -0
  78. tradedangerous/transfers.py +192 -0
  79. tradedangerous/utils.py +243 -0
  80. tradedangerous/version.py +16 -0
  81. tradedangerous-12.7.6.dist-info/METADATA +106 -0
  82. tradedangerous-12.7.6.dist-info/RECORD +87 -0
  83. tradedangerous-12.7.6.dist-info/WHEEL +5 -0
  84. tradedangerous-12.7.6.dist-info/entry_points.txt +3 -0
  85. tradedangerous-12.7.6.dist-info/licenses/LICENSE +373 -0
  86. tradedangerous-12.7.6.dist-info/top_level.txt +2 -0
  87. tradegui.py +24 -0
tradedangerous/cli.py ADDED
@@ -0,0 +1,136 @@
1
+ # --------------------------------------------------------------------
2
+ # Copyright (C) Oliver 'kfsone' Smith 2014 <oliver@kfs.org>:
3
+ # Copyright (C) Bernd 'Gazelle' Gollesch 2016, 2017
4
+ # Copyright (C) Jonathan 'eyeonus' Jones 2018, 2019
5
+ #
6
+ # You are free to use, redistribute, or even print and eat a copy of
7
+ # this software so long as you include this copyright notice.
8
+ # I guarantee there is at least one bug neither of us knew about.
9
+ # --------------------------------------------------------------------
10
+ # TradeDangerous :: Command Line App :: Main Module
11
+ #
12
+ # TradeDangerous is a powerful set of tools for traders in Frontier
13
+ # Development's game "Elite: Dangerous". It's main function is
14
+ # calculating the most profitable trades between either individual
15
+ # stations or working out "profit runs".
16
+ #
17
+ # I wrote TD because I realized that the best trade run - in terms
18
+ # of the "average profit per stop" was rarely as simple as going
19
+ # Chango -> Dahan -> Chango.
20
+ #
21
+ # E:D's economy is complex; sometimes you can make the most profit
22
+ # by trading one item A->B and flying a second item B->A.
23
+ # But more often you need to fly multiple stations, especially since
24
+ # as you are making money different trade options are coming into
25
+ # your affordable range.
26
+ #
27
+ # END USERS: If you are a user looking to find out how to use TD,
28
+ # please consult the file "README.md".
29
+ #
30
+ # DEVELOPERS: If you are a programmer who wants TD to do something
31
+ # cool, please see the TradeDB and TradeCalc modules. TD is designed
32
+ # to empower other programmers to do cool stuff.
33
+
34
+ import os
35
+ import sys
36
+ import traceback
37
+
38
+ from . import commands
39
+ from . import tradeexcept
40
+ from .commands import exceptions
41
+ from .plugins import PluginException
42
+
43
+ from . import tradedb
44
+
45
+ if "CPROF" in os.environ:
46
+ import cProfile
47
+
48
+
49
+ def main(argv = None):
50
+ if not argv:
51
+ argv = sys.argv
52
+ if sys.hexversion < 0x30813F0:
53
+ raise SystemExit(
54
+ "Sorry: TradeDangerous requires Python 3.8.19 or higher.\n"
55
+ "For assistance, see:\n"
56
+ "\tBug Tracker: https://github.com/eyeonus/Trade-Dangerous/issues\n"
57
+ "\tDocumentation: https://github.com/eyeonus/Trade-Dangerous/wiki\n"
58
+ "\tEDForum Thread: https://forums.frontier.co.uk/showthread.php/441509\n"
59
+ )
60
+
61
+ try:
62
+ try:
63
+ if "CPROF" in os.environ:
64
+ cProfile.run("trade(argv)")
65
+ else:
66
+ trade(argv)
67
+ except PluginException as e:
68
+ print("PLUGIN ERROR: {}".format(e))
69
+ if 'EXCEPTIONS' in os.environ:
70
+ raise e
71
+ sys.exit(1)
72
+ except tradeexcept.TradeException as e:
73
+ print("%s: %s" % (argv[0], str(e)))
74
+ if 'EXCEPTIONS' in os.environ:
75
+ raise e
76
+ sys.exit(1)
77
+ except (UnicodeEncodeError, UnicodeDecodeError):
78
+ print("-----------------------------------------------------------")
79
+ print("ERROR: Unexpected unicode error in the wild!")
80
+ print()
81
+ print(traceback.format_exc())
82
+ print(
83
+ "Please report this bug (http://github.com/eyeonus/Trade-Dangerous/issues). You may be "
84
+ "able to work around it by using the '-q' parameter. Windows "
85
+ "users may be able to use 'chcp.com 65001' to tell the console "
86
+ "you want to support UTF-8 characters."
87
+ )
88
+
89
+ def trade(argv):
90
+ """
91
+ This method represents the trade command.
92
+ """
93
+ cmdIndex = commands.CommandIndex()
94
+ cmdenv = cmdIndex.parse(argv)
95
+
96
+ # Phase A: preflight/fast validation (must run before any heavy TradeDB load)
97
+ if (preflight := getattr(cmdenv, "preflight", None)) and callable(preflight):
98
+ preflight()
99
+
100
+ # Phase B: heavy init + execution
101
+ tdb = tradedb.TradeDB(cmdenv, load=cmdenv.wantsTradeDB)
102
+ if cmdenv.usesTradeData:
103
+ tsc = tdb.tradingStationCount
104
+ if tsc == 0:
105
+ raise exceptions.NoDataError(
106
+ "There is no trading data for ANY station in "
107
+ "the local database. Please enter or import "
108
+ "price data."
109
+ )
110
+ if tsc == 1:
111
+ raise exceptions.NoDataError(
112
+ "The local database only contains trading data "
113
+ "for one station. Please enter or import data "
114
+ "for additional stations."
115
+ )
116
+ if tsc < 8:
117
+ cmdenv.NOTE(
118
+ "The local database only contains trading data "
119
+ "for {} stations. Please enter or import data "
120
+ "for additional stations.".format(
121
+ tsc
122
+ )
123
+ )
124
+
125
+ try:
126
+ results = cmdenv.run(tdb)
127
+ except tradeexcept.SimpleAbort as e:
128
+ cmdenv.console.print(f"\n{e}\n", style="red")
129
+ sys.exit(1)
130
+ finally:
131
+ # always close tdb
132
+ tdb.close(final=True)
133
+
134
+ if results:
135
+ results.render()
136
+
@@ -0,0 +1,74 @@
1
+ from __future__ import annotations
2
+ import typing
3
+
4
+ from .commandenv import ResultRow
5
+ from .parsing import ParseArgument # import specific helpers as needed
6
+
7
+ from tradedangerous.formatting import RowFormat
8
+
9
+ if typing.TYPE_CHECKING:
10
+ from tradedangerous import TradeDB, TradeORM, CommandEnv, CommandResults
11
+
12
+
13
+ ######################################################################
14
+ # Parser config
15
+
16
+ help = 'Describe your command briefly here for the top-level --help.'
17
+ name = 'TEMPLATE' # name of your .py file excluding the _cmd
18
+ epilog = None # text to print at the bottom of --help
19
+
20
+ # Whether this command needs a TradeDB instance
21
+ wantsTradeDB = True
22
+ usesTradeData = False
23
+
24
+ # Parser wiring (keep tuples for consistency with loader)
25
+ arguments = (
26
+ ParseArgument("name", help="Example positional(s).", type=str, nargs="*"),
27
+ )
28
+ switches = (
29
+ ParseArgument("--flag", help="Example flag.", action="store_true", default=False),
30
+ )
31
+
32
+
33
+ # Runtime API
34
+
35
+
36
+ def run(
37
+ results: CommandResults,
38
+ cmdenv: CommandEnv,
39
+ tdb: TradeDB | TradeORM | None, # choose one
40
+ ) -> CommandResults | bool | None: # choose one
41
+ """
42
+ Implement code that validates arguments, collects and prepares
43
+ any data you will need to generate your results for the user.
44
+
45
+ If your command has finished and has no output to generate,
46
+ return None, otherwise return "results" to be forwarded to
47
+ the 'render' function.
48
+
49
+ DO NOT print() during 'run', this allows run() functions to
50
+ be re-used between modules and allows them to be used beyond
51
+ the trade.py command line - e.g. someone writing a TD GUI
52
+ will call run() and then render the results themselves.
53
+ """
54
+
55
+ ### TODO: Implement
56
+ row = ResultRow(example="ok")
57
+ results.rows.append(row)
58
+ return results
59
+
60
+
61
+ def render(results: CommandResults, cmdenv: CommandEnv, tdb: TradeDB | TradeORM | None):
62
+ """
63
+ If run() returns a truthy value, the trade.py code will then
64
+ call the corresponding render() function.
65
+
66
+ This is where you should generate any output from your command.
67
+ """
68
+ fmt = RowFormat()
69
+ fmt.addColumn("Example", "<", 10, key=lambda r: getattr(r, "example", ""))
70
+ if not cmdenv.quiet:
71
+ hdr, ul = fmt.heading()
72
+ print(hdr, ul, sep="\n")
73
+ for row in results.rows:
74
+ print(fmt.format(row))
@@ -0,0 +1,244 @@
1
+ from .commandenv import CommandEnv
2
+ from textwrap import TextWrapper
3
+
4
+ import argparse # For parsing command line args.
5
+ import os
6
+ import pathlib
7
+ import sys
8
+
9
+ from . import exceptions
10
+ from . import parsing
11
+
12
+ from . import buildcache_cmd
13
+ from . import buy_cmd
14
+ from . import export_cmd
15
+ from . import import_cmd
16
+ from . import local_cmd
17
+ from . import market_cmd
18
+ from . import nav_cmd
19
+ from . import olddata_cmd
20
+ from . import rares_cmd
21
+ from . import run_cmd
22
+ from . import sell_cmd
23
+ from . import shipvendor_cmd
24
+ from . import station_cmd
25
+ from . import trade_cmd
26
+ from . import update_cmd
27
+
28
+ from tradedangerous import version
29
+ from tradedangerous.tradeenv import ENV_DEFAULTS
30
+
31
+
32
+ thismodule = sys.modules[__name__]
33
+
34
+ commandIndex = {
35
+ cmd[0:cmd.find('_cmd')]: getattr(thismodule, cmd)
36
+ for cmd in thismodule.__dir__() if cmd.endswith("_cmd")
37
+ }
38
+
39
+ ######################################################################
40
+ # Helpers
41
+
42
+
43
+ class HelpAction(argparse.Action):
44
+ """
45
+ argparse action helper for printing the argument usage,
46
+ because Python 3.4's argparse is ever-so subtly very broken.
47
+ """
48
+
49
+ def __call__(self, parser, namespace, values, option_string = None):
50
+ raise exceptions.UsageError(
51
+ f"TradeDangerous v{version.__version__} help",
52
+ parser.format_help()
53
+ )
54
+
55
+
56
+ def addArguments(group, options, required, topGroup = None):
57
+ """
58
+ Registers a list of options to the specified group. Nodes
59
+ are either an instance of ParseArgument or a list of
60
+ ParseArguments. The list form is considered to be a
61
+ mutually exclusive group of arguments.
62
+ """
63
+ for option in options:
64
+ if isinstance(option, parsing.MutuallyExclusiveGroup):
65
+ exGrp = (topGroup or group).add_mutually_exclusive_group()
66
+ parsing.registerParserHelpers(exGrp)
67
+ addArguments(exGrp, option.arguments, required, topGroup = group)
68
+ else:
69
+ assert required not in option.kwargs
70
+ if option.args[0][0] == '-':
71
+ group.add_argument(*(option.args), required = required, **(option.kwargs))
72
+ elif required:
73
+ group.add_argument(*(option.args), **(option.kwargs))
74
+ else:
75
+ group.add_argument(*(option.args), nargs = '?', **(option.kwargs))
76
+
77
+
78
+ def _findFromFile(cmd, prefix = '.tdrc'):
79
+ if cmd:
80
+ # check the current directory, fall back to home
81
+ filename = f'{prefix}_{cmd}'
82
+ for dirname in '.', os.path.expanduser('~'):
83
+ cmdPath = pathlib.Path(dirname) / filename
84
+ if cmdPath.exists():
85
+ return cmdPath.resolve()
86
+ return None
87
+
88
+
89
+ class CommandIndex:
90
+
91
+ def usage(self, argv):
92
+ """
93
+ Generate the outlying usage text for TD.
94
+ This tells the user the list of current
95
+ commands, generated programatically,
96
+ and the outlying command functionality.
97
+ """
98
+
99
+ text = (
100
+ "Usage: {prog} <command>\n\n"
101
+ "Where <command> is one of:\n\n"
102
+ .format(prog = argv[0])
103
+ )
104
+
105
+ # Figure out the pre-indentation
106
+ cmdFmt = ' {:<12s} '
107
+ cmdFmtLen = len(cmdFmt.format(''))
108
+ tw = TextWrapper(
109
+ subsequent_indent = ' ' * (cmdFmtLen + 1),
110
+ width = 78,
111
+ drop_whitespace = True,
112
+ expand_tabs = True,
113
+ fix_sentence_endings = True,
114
+ break_long_words = False,
115
+ break_on_hyphens = True,
116
+ )
117
+
118
+ lastCmdName = None
119
+ for cmdName, cmd in sorted(commandIndex.items()):
120
+ tw.initial_indent = cmdFmt.format(cmdName)
121
+ text += tw.fill(cmd.help) + "\n"
122
+ lastCmdName = cmdName
123
+
124
+ text += (
125
+ "\n"
126
+ f"Version {version.__version__}\n"
127
+ f"For additional help on a specific command, such as '{lastCmdName}' use\n"
128
+ f" {argv[0]} {lastCmdName} -h"
129
+ )
130
+ return text
131
+
132
+ def parse(self, argv, fromfile_prefix = '+'):
133
+ if len(argv) <= 1 or argv[1] == '--help' or argv[1] == '-h':
134
+ raise exceptions.UsageError(
135
+ "TradeDangerous provides a set of trade database "
136
+ "facilities for Elite:Dangerous.", self.usage(argv))
137
+
138
+ cmdName, cmdModule = argv[1].casefold(), None
139
+ try:
140
+ cmdModule = commandIndex[cmdName]
141
+ except KeyError:
142
+ pass
143
+
144
+ if not cmdModule:
145
+ candidates = []
146
+ for name, module in commandIndex.items():
147
+ if name.startswith(cmdName):
148
+ candidates.append([name, module])
149
+ if not candidates:
150
+ raise exceptions.CommandLineError(
151
+ f"Unrecognized command, '{cmdName}'",
152
+ self.usage(argv)
153
+ )
154
+ if len(candidates) > 1:
155
+ candidate_names = ', '.join(c[0] for c in candidates)
156
+ raise exceptions.CommandLineError(
157
+ f"Ambiguous command, '{cmdName}', could match: {candidate_names}",
158
+ self.usage(argv)
159
+ )
160
+ argv[1] = cmdName = candidates[0][0]
161
+ cmdModule = candidates[0][1]
162
+
163
+ class ArgParser(argparse.ArgumentParser):
164
+ def error(self, message):
165
+ raise exceptions.CommandLineError(message, self.format_usage())
166
+
167
+ parser = ArgParser(
168
+ description = f"TradeDangerous v{version.__version__}: {cmdName}",
169
+ add_help = False,
170
+ allow_abbrev = True,
171
+ epilog = f"Version {version.__version__}.\nUse {argv[0]} {argv[1]} -h for more help",
172
+ fromfile_prefix_chars = fromfile_prefix,
173
+ )
174
+ parser.set_defaults(_editing = False)
175
+ parsing.registerParserHelpers(parser)
176
+
177
+ subParsers = parser.add_subparsers(title = 'Command Options')
178
+ subParser = subParsers.add_parser(cmdModule.name,
179
+ help = cmdModule.help,
180
+ add_help = False,
181
+ epilog = cmdModule.epilog,
182
+ )
183
+ parsing.registerParserHelpers(subParser)
184
+
185
+ arguments = cmdModule.arguments
186
+ if arguments:
187
+ argParser = subParser.add_argument_group('Required Arguments')
188
+ addArguments(argParser, arguments, True)
189
+
190
+ switches = cmdModule.switches
191
+ if switches:
192
+ switchParser = subParser.add_argument_group('Optional Switches')
193
+ addArguments(switchParser, switches, False)
194
+
195
+ # Arguments common to all subparsers.
196
+ stdArgs = subParser.add_argument_group('Common Switches')
197
+ stdArgs.add_argument('--help', '-h',
198
+ help = 'Show this help message and exit.',
199
+ action = HelpAction, nargs = 0,
200
+ )
201
+ stdArgs.add_argument('--debug', '-w',
202
+ help = 'Enable/raise level of diagnostic output.',
203
+ default = 0, required = False, action = 'count',
204
+ )
205
+ stdArgs.add_argument('--detail', '-v',
206
+ help = 'Increase level of detail in output.',
207
+ default = 0, required = False, action = 'count',
208
+ )
209
+ stdArgs.add_argument('--color', '-c',
210
+ help = 'Add color to output for enhanced readability.',
211
+ default = False, action = 'store_true',
212
+ )
213
+ stdArgs.add_argument('--quiet', '-q',
214
+ help = 'Reduce level of detail in output.',
215
+ default = 0, required = False, action = 'count',
216
+ )
217
+ stdArgs.add_argument('--db',
218
+ help = 'Specify location of the SQLite database.',
219
+ default = None, dest = 'dbFilename', type = str,
220
+ )
221
+ stdArgs.add_argument('--cwd', '-C',
222
+ help = 'Change the working directory file accesses are made from.',
223
+ type = str, required = False,
224
+ )
225
+ stdArgs.add_argument('--link-ly', '-L',
226
+ help = 'Maximum lightyears between systems to be considered linked.',
227
+ type = float,
228
+ default = ENV_DEFAULTS['maxSystemLinkLy'], dest = 'maxSystemLinkLy',
229
+ )
230
+
231
+ fromfilePath = _findFromFile(cmdModule.name)
232
+ if fromfilePath:
233
+ argv.insert(2, f'{fromfile_prefix}{fromfilePath}')
234
+
235
+ # Parse argv; optionally swallow unknown args/switches if the module allows it.
236
+ accept_unknown = getattr(cmdModule, 'acceptUnknown', False)
237
+ if accept_unknown:
238
+ properties, _unknown = parser.parse_known_args(argv[1:])
239
+ else:
240
+ properties = parser.parse_args(argv[1:])
241
+
242
+ parsed = CommandEnv(vars(properties), argv, cmdModule)
243
+ parsed.DEBUG0("Command line was: {}", argv)
244
+ return parsed
@@ -0,0 +1,102 @@
1
+ from __future__ import annotations
2
+ import typing
3
+
4
+ from .exceptions import CommandLineError
5
+ from .parsing import ParseArgument
6
+
7
+ from tradedangerous.db.lifecycle import ensure_fresh_db
8
+
9
+ if typing.TYPE_CHECKING:
10
+ from tradedangerous import CommandEnv, CommandResults, TradeDB
11
+
12
+
13
+ ######################################################################
14
+ # Parser config
15
+
16
+ help = 'Build TradeDangerous cache file from sources'
17
+ name = 'buildcache'
18
+ epilog = (
19
+ 'TD will normally do this for you automatically whenever '
20
+ 'it detects changes to one or more source file; most end-'
21
+ 'users will never need to use this command.\n'
22
+ 'N.B. This process is destructive: '
23
+ 'any data in the .db that is not reflected in the '
24
+ 'source files will be lost.'
25
+ )
26
+ wantsTradeDB = False # Cause we're about to frak with it.
27
+ arguments = [
28
+ ]
29
+ switches = [
30
+ ParseArgument(
31
+ '--sql', default = None, dest = 'sqlFilename',
32
+ help = 'Specify SQL script to execute.',
33
+ ),
34
+ ParseArgument(
35
+ '--prices', default = None, dest = 'pricesFilename',
36
+ help = 'Specify the prices file to load.',
37
+ ),
38
+ ParseArgument(
39
+ '--force', '-f', default = False, action = 'store_true',
40
+ dest = 'force',
41
+ help = 'Overwrite existing file',
42
+ ),
43
+ ParseArgument(
44
+ '--ignore-unknown', '-i',
45
+ default = False, action = 'store_true',
46
+ dest = 'ignoreUnknown',
47
+ help = (
48
+ "Data for systems, stations and items that are not "
49
+ "recognized is reported as warning but skipped."
50
+ ),
51
+ ),
52
+ ]
53
+
54
+ ######################################################################
55
+ # Helpers
56
+
57
+ ######################################################################
58
+ # Perform query and populate result set
59
+
60
+
61
+ def run(results: CommandResults, cmdenv: CommandEnv, tdb: TradeDB) -> bool:
62
+ """
63
+ BRUTE-FORCE rebuild of the cache/database.
64
+
65
+ Semantics preserved:
66
+ - If DB exists and --force not given => error
67
+ - SQL file must exist
68
+ - Performs a full destructive rebuild
69
+
70
+ Implementation change:
71
+ - Delegates to tradedangerous.db.lifecycle.ensure_fresh_db with mode='force'
72
+ so all backend-specific checks and rebuild steps run via the central path.
73
+ """
74
+ # Deprecation note: keep short and visible but non-fatal.
75
+ print("NOTE: 'buildcache' is deprecated. Prefer 'update' or importer plugins. "
76
+ "Proceeding with a forced rebuild via db.lifecycle.ensure_fresh_db().")
77
+
78
+ # Honor legacy safety: require --force to overwrite an existing DB file.
79
+ if not cmdenv.force and tdb.dbPath.exists():
80
+ raise CommandLineError(
81
+ f"SQLite3 database '{tdb.dbFilename}' already exists.\n"
82
+ "Either remove the file first or use the '-f/--force' option."
83
+ )
84
+
85
+ # Ensure the SQL source exists (buildCache ultimately relies on this path).
86
+ if not tdb.sqlPath.exists():
87
+ raise CommandLineError(f"SQL File does not exist: {tdb.sqlFilename}")
88
+
89
+ # Force a rebuild through the lifecycle helper (works for both backends).
90
+ ensure_fresh_db(
91
+ backend=tdb.engine.dialect.name if getattr(tdb, "engine", None) else "sqlite",
92
+ engine=getattr(tdb, "engine", None),
93
+ data_dir=tdb.dataPath,
94
+ metadata=None,
95
+ mode="force",
96
+ tdb=tdb,
97
+ tdenv=cmdenv,
98
+ rebuild=True,
99
+ )
100
+
101
+ # We've done everything, there is no work for the caller to do.
102
+ return False