tradedangerous 12.0.3__tar.gz → 12.0.5__tar.gz

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.

Files changed (100) hide show
  1. {tradedangerous-12.0.3/tradedangerous.egg-info → tradedangerous-12.0.5}/PKG-INFO +1 -1
  2. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/cache.py +236 -98
  3. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/version.py +1 -1
  4. {tradedangerous-12.0.3 → tradedangerous-12.0.5/tradedangerous.egg-info}/PKG-INFO +1 -1
  5. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/LICENSE +0 -0
  6. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/README.md +0 -0
  7. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/pyproject.toml +0 -0
  8. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/setup.cfg +0 -0
  9. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/setup.py +0 -0
  10. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tests/test_bootstrap_commands.py +0 -0
  11. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tests/test_bootstrap_plugins.py +0 -0
  12. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tests/test_cache.py +0 -0
  13. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tests/test_commands.py +0 -0
  14. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tests/test_fs.py +0 -0
  15. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tests/test_peek.py +0 -0
  16. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tests/test_tools.py +0 -0
  17. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tests/test_trade.py +0 -0
  18. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tests/test_trade_run.py +0 -0
  19. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tests/test_utils.py +0 -0
  20. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/trade.py +0 -0
  21. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/__init__.py +0 -0
  22. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/cli.py +0 -0
  23. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/commands/TEMPLATE.py +0 -0
  24. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/commands/__init__.py +0 -0
  25. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/commands/buildcache_cmd.py +0 -0
  26. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/commands/buy_cmd.py +0 -0
  27. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/commands/commandenv.py +0 -0
  28. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/commands/exceptions.py +0 -0
  29. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/commands/export_cmd.py +0 -0
  30. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/commands/import_cmd.py +0 -0
  31. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/commands/local_cmd.py +0 -0
  32. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/commands/market_cmd.py +0 -0
  33. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/commands/nav_cmd.py +0 -0
  34. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/commands/olddata_cmd.py +0 -0
  35. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/commands/parsing.py +0 -0
  36. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/commands/rares_cmd.py +0 -0
  37. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/commands/run_cmd.py +0 -0
  38. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/commands/sell_cmd.py +0 -0
  39. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/commands/shipvendor_cmd.py +0 -0
  40. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/commands/station_cmd.py +0 -0
  41. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/commands/trade_cmd.py +0 -0
  42. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/commands/update_cmd.py +0 -0
  43. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/corrections.py +0 -0
  44. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/csvexport.py +0 -0
  45. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/db/__init__.py +0 -0
  46. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/db/adapter.py +0 -0
  47. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/db/config.py +0 -0
  48. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/db/engine.py +0 -0
  49. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/db/lifecycle.py +0 -0
  50. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/db/locks.py +0 -0
  51. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/db/orm_models.py +0 -0
  52. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/db/paths.py +0 -0
  53. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/db/utils.py +0 -0
  54. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/edscupdate.py +0 -0
  55. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/edsmupdate.py +0 -0
  56. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/formatting.py +0 -0
  57. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/fs.py +0 -0
  58. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/gui.py +0 -0
  59. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/mapping.py +0 -0
  60. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/mfd/__init__.py +0 -0
  61. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/mfd/saitek/__init__.py +0 -0
  62. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/mfd/saitek/directoutput.py +0 -0
  63. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/mfd/saitek/x52pro.py +0 -0
  64. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/misc/checkpricebounds.py +0 -0
  65. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/misc/clipboard.py +0 -0
  66. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/misc/coord64.py +0 -0
  67. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/misc/derp-sentinel.py +0 -0
  68. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/misc/diff-system-csvs.py +0 -0
  69. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/misc/eddb.py +0 -0
  70. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/misc/eddn.py +0 -0
  71. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/misc/edsc.py +0 -0
  72. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/misc/edsm.py +0 -0
  73. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/misc/importeddbstats.py +0 -0
  74. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/misc/prices-json-exp.py +0 -0
  75. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/misc/progress.py +0 -0
  76. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/plugins/__init__.py +0 -0
  77. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/plugins/edcd_plug.py +0 -0
  78. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/plugins/eddblink_plug.py +0 -0
  79. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/plugins/edmc_batch_plug.py +0 -0
  80. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/plugins/spansh_plug.py +0 -0
  81. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/prices.py +0 -0
  82. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/submit-distances.py +0 -0
  83. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/templates/Added.csv +0 -0
  84. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/templates/Category.csv +0 -0
  85. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/templates/RareItem.csv +0 -0
  86. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/templates/TradeDangerous.sql +0 -0
  87. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/tools.py +0 -0
  88. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/tradecalc.py +0 -0
  89. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/tradedb.py +0 -0
  90. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/tradeenv.py +0 -0
  91. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/tradeexcept.py +0 -0
  92. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/transfers.py +0 -0
  93. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous/utils.py +0 -0
  94. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous.egg-info/SOURCES.txt +0 -0
  95. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous.egg-info/dependency_links.txt +0 -0
  96. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous.egg-info/entry_points.txt +0 -0
  97. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous.egg-info/not-zip-safe +0 -0
  98. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous.egg-info/requires.txt +0 -0
  99. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradedangerous.egg-info/top_level.txt +0 -0
  100. {tradedangerous-12.0.3 → tradedangerous-12.0.5}/tradegui.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tradedangerous
3
- Version: 12.0.3
3
+ Version: 12.0.5
4
4
  Summary: Trade-Dangerous is a set of powerful trading tools for Elite Dangerous, organized around one of the most powerful trade run optimizers available.
5
5
  Home-page: https://github.com/eyeonus/Trade-Dangerous
6
6
  Author: eyeonus
@@ -33,7 +33,7 @@ import typing
33
33
 
34
34
 
35
35
  from functools import partial as partial_fn
36
- from sqlalchemy import func, Integer, Float, DateTime
36
+ from sqlalchemy import func, Integer, Float, DateTime, tuple_
37
37
  from sqlalchemy import inspect as sa_inspect
38
38
  from sqlalchemy.orm import Session
39
39
  from sqlalchemy.types import DateTime as SA_DateTime
@@ -586,23 +586,30 @@ def processPrices(
586
586
  nonlocal newItems, updtItems, ignItems
587
587
  itemName, modified = matches.group('item', 'time')
588
588
  itemName = itemName.upper()
589
-
589
+
590
590
  # Look up the item ID.
591
591
  itemID = getItemID(itemName, -1)
592
592
  if itemID < 0:
593
593
  oldName = itemName
594
594
  itemName = corrections.correctItem(itemName)
595
- if itemName == DELETED:
595
+
596
+ # Silently skip DELETED items
597
+ if itemName == corrections.DELETED:
596
598
  DEBUG1("DELETED {}", oldName)
597
599
  return
600
+
601
+ # Retry with corrected name
598
602
  itemName = itemName.upper()
599
603
  itemID = getItemID(itemName, -1)
604
+
600
605
  if itemID < 0:
601
606
  ignoreOrWarn(
602
607
  UnknownItemError(priceFile, lineNo, itemName)
603
608
  )
604
609
  return
610
+
605
611
  DEBUG1("Renamed {} -> {}", oldName, itemName)
612
+
606
613
 
607
614
  lastModified = stationItemDates.get(itemID, None)
608
615
  if lastModified and merging:
@@ -749,25 +756,27 @@ def processPricesFile(
749
756
  SA.StationItem.station_id.in_([sid for (sid,) in stations])
750
757
  ).delete(synchronize_session=False)
751
758
 
759
+ # Remove zeroed pairs
760
+ removedItems = 0
752
761
  if zeros:
753
762
  session.query(SA.StationItem).filter(
754
763
  tuple_(SA.StationItem.station_id, SA.StationItem.item_id).in_(zeros)
755
764
  ).delete(synchronize_session=False)
756
- removedItems = len(zeros)
765
+ removedItems = len(zeros)
757
766
 
767
+ # Upsert items
758
768
  if items:
759
- for item in items:
760
- (
761
- station_id,
762
- item_id,
763
- modified,
764
- demand_price,
765
- demand_units,
766
- demand_level,
767
- supply_price,
768
- supply_units,
769
- supply_level,
770
- ) = item
769
+ for (
770
+ station_id,
771
+ item_id,
772
+ modified,
773
+ demand_price,
774
+ demand_units,
775
+ demand_level,
776
+ supply_price,
777
+ supply_units,
778
+ supply_level,
779
+ ) in items:
771
780
  obj = SA.StationItem(
772
781
  station_id=station_id,
773
782
  item_id=item_id,
@@ -811,6 +820,7 @@ def processPricesFile(
811
820
 
812
821
 
813
822
 
823
+
814
824
  ######################################################################
815
825
 
816
826
 
@@ -871,22 +881,9 @@ def processImportFile(
871
881
  """
872
882
  Import a CSV file into the given table.
873
883
 
874
- - RareItem.csv:
875
- Skips FK marker columns:
876
- * !name@System.system_id
877
- * name@Station.station_id
878
- * name@Category.category_id
879
- Looks up system_id (transient), station_id, category_id via cached helpers.
880
- NOTE: system_id is NOT a RareItem column and is not passed to the model.
881
-
882
- - System.csv:
883
- Skips 'name@Added.added_id' from active columns; resolves Added by name,
884
- defaulting to "EDSM" when blank.
885
-
886
- - All tables:
887
- Uses parse_ts() for datetimes.
888
- Enforces unq: unique headers.
889
- Commits per tradedangerous.db.utils.get_import_batch_size(session).
884
+ Applies header parsing, uniqueness checks, foreign key lookups,
885
+ in-row deprecation correction (warnings only at -vv via DEBUG1), and upserts via SQLAlchemy ORM.
886
+ Commits in batches for large datasets.
890
887
  """
891
888
 
892
889
  tdenv.DEBUG0("Processing import file '{}' for table '{}'", str(importPath), tableName)
@@ -895,114 +892,258 @@ def processImportFile(
895
892
  if line_callback:
896
893
  line_callback = partial_fn(line_callback, **call_args)
897
894
 
898
- uniquePfx = "unq:"
899
- uniqueLen = len(uniquePfx)
895
+ # --- batch size config from environment or fallback ---
896
+ env_batch = os.environ.get("TD_LISTINGS_BATCH")
897
+ if env_batch:
898
+ try:
899
+ max_transaction_items = int(env_batch)
900
+ except ValueError:
901
+ tdenv.WARN("Invalid TD_LISTINGS_BATCH value %r, falling back to defaults.", env_batch)
902
+ max_transaction_items = None
903
+ else:
904
+ max_transaction_items = None
905
+
906
+ if max_transaction_items is None:
907
+ if session.bind.dialect.name in ("mysql", "mariadb"):
908
+ max_transaction_items = 50 * 1024
909
+ else:
910
+ max_transaction_items = 250 * 1024
900
911
 
901
- # Backend-aware batch policy (SQLite=None→single commit; MariaDB defaults to 50k; env override respected)
902
- max_transaction_items = get_import_batch_size(session, profile="csv") # from tradedangerous.db.utils
903
- transaction_items = 0
912
+ transaction_items = 0 # track how many rows inserted before committing
904
913
 
905
914
  with importPath.open("r", encoding="utf-8") as importFile:
906
915
  csvin = csv.reader(importFile, delimiter=",", quotechar="'", doublequote=True)
907
916
 
908
- # header
917
+ # Read header row
909
918
  columnDefs = next(csvin)
910
919
  columnCount = len(columnDefs)
911
920
 
912
- activeColumns: list[str] = [] # headers that map directly to ORM fields
913
- kept_indices: list[int] = [] # original header indices that we KEEP (aligns values)
914
- uniqueIndexes: list[int] = [] # indexes into activeColumns (post-skip)
915
- fk_col_indices: dict[str, int] = {} # special FK headers their original indices
921
+ # --- Process headers: extract column names, track indices ---
922
+ activeColumns: list[str] = [] # Final columns we'll use (after "unq:" stripping)
923
+ kept_indices: list[int] = [] # Indices into CSV rows we keep (aligned to activeColumns)
924
+ uniqueIndexes: list[int] = [] # Indexes (into activeColumns) of unique keys
925
+ fk_col_indices: dict[str, int] = {} # Special handling for FK resolution
926
+
927
+ uniquePfx = "unq:"
928
+ uniqueLen = len(uniquePfx)
929
+
930
+ # map of header (without "unq:") -> original CSV index, for correction by name
931
+ header_index: dict[str, int] = {}
916
932
 
917
- # --- preprocess header ---
918
933
  for cIndex, cName in enumerate(columnDefs):
919
934
  colName, _, srcKey = cName.partition("@")
935
+ baseName = colName[uniqueLen:] if colName.startswith(uniquePfx) else colName
936
+ header_index[baseName] = cIndex
920
937
 
921
- # --- System.csv ---
938
+ # Special-case: System-added
922
939
  if tableName == "System":
923
940
  if cName == "name":
924
- srcKey = "" # plain field
941
+ srcKey = ""
925
942
  elif cName == "name@Added.added_id":
926
- # We'll resolve Added by name separately; skip from active columns.
927
943
  fk_col_indices["added"] = cIndex
928
- continue # do NOT keep this header/value
944
+ continue
929
945
 
930
- # --- RareItem.csv (skip FK headers; remember positions) ---
946
+ # Foreign key columns for RareItem
931
947
  if tableName == "RareItem":
932
948
  if cName == "!name@System.system_id":
933
949
  fk_col_indices["system"] = cIndex
934
- continue # do NOT keep this header/value
950
+ continue
935
951
  if cName == "name@Station.station_id":
936
952
  fk_col_indices["station"] = cIndex
937
- continue # do NOT keep this header/value
953
+ continue
938
954
  if cName == "name@Category.category_id":
939
955
  fk_col_indices["category"] = cIndex
940
- continue # do NOT keep this header/value
956
+ continue
941
957
 
942
- # unique index marker (e.g., "unq:name")
958
+ # Handle unique constraint tracking
943
959
  if colName.startswith(uniquePfx):
944
960
  uniqueIndexes.append(len(activeColumns))
945
- colName = colName[uniqueLen:]
961
+ colName = baseName
946
962
 
947
- # keep normal columns and remember their source index
948
963
  activeColumns.append(colName)
949
964
  kept_indices.append(cIndex)
950
965
 
951
- # optional deprecation checker
952
- deprecationFn = getattr(sys.modules[__name__], "deprecationCheck" + tableName, None)
953
-
954
966
  importCount = 0
955
- uniqueIndex = {}
967
+ uniqueIndex: dict[str, int] = {}
956
968
 
969
+ # helpers for correction + visibility-gated warning
970
+ DELETED = corrections.DELETED
971
+
972
+ def _warn(line_no: int, msg: str) -> None:
973
+ # Gate deprecation chatter to -vv (DEBUG1)
974
+ tdenv.DEBUG1("{}:{} WARNING {}", importPath, line_no, msg)
975
+
976
+ def _apply_row_corrections(table_name: str, row: list[str], line_no: int) -> bool:
977
+ """
978
+ Returns True if the row should be skipped (deleted in tolerant mode), False otherwise.
979
+ Mutates 'row' in place with corrected values.
980
+ """
981
+ try:
982
+ if table_name == "System":
983
+ idx = header_index.get("name")
984
+ if idx is not None:
985
+ orig = row[idx]
986
+ corr = corrections.correctSystem(orig)
987
+ if corr is DELETED:
988
+ if tdenv.ignoreUnknown:
989
+ _warn(line_no, f'System "{orig}" is marked as DELETED and should not be used.')
990
+ return True
991
+ raise DeletedKeyError(importPath, line_no, "System", orig)
992
+ if corr != orig:
993
+ _warn(line_no, f'System "{orig}" is deprecated and should be replaced with "{corr}".')
994
+ row[idx] = corr
995
+
996
+ elif table_name == "Station":
997
+ s_idx = header_index.get("system")
998
+ n_idx = header_index.get("name")
999
+ if s_idx is not None and n_idx is not None:
1000
+ s_orig = row[s_idx]
1001
+ s_corr = corrections.correctSystem(s_orig)
1002
+ if s_corr is DELETED:
1003
+ if tdenv.ignoreUnknown:
1004
+ _warn(line_no, f'System "{s_orig}" is marked as DELETED and should not be used.')
1005
+ return True
1006
+ raise DeletedKeyError(importPath, line_no, "System", s_orig)
1007
+ if s_corr != s_orig:
1008
+ _warn(line_no, f'System "{s_orig}" is deprecated and should be replaced with "{s_corr}".')
1009
+ row[s_idx] = s_corr
1010
+ n_orig = row[n_idx]
1011
+ n_corr = corrections.correctStation(s_corr, n_orig)
1012
+ if n_corr is DELETED:
1013
+ if tdenv.ignoreUnknown:
1014
+ _warn(line_no, f'Station "{n_orig}" is marked as DELETED and should not be used.')
1015
+ return True
1016
+ raise DeletedKeyError(importPath, line_no, "Station", n_orig)
1017
+ if n_corr != n_orig:
1018
+ _warn(line_no, f'Station "{n_orig}" is deprecated and should be replaced with "{n_corr}".')
1019
+ row[n_idx] = n_corr
1020
+
1021
+ elif table_name == "Category":
1022
+ idx = header_index.get("name")
1023
+ if idx is not None:
1024
+ orig = row[idx]
1025
+ corr = corrections.correctCategory(orig)
1026
+ if corr is DELETED:
1027
+ if tdenv.ignoreUnknown:
1028
+ _warn(line_no, f'Category "{orig}" is marked as DELETED and should not be used.')
1029
+ return True
1030
+ raise DeletedKeyError(importPath, line_no, "Category", orig)
1031
+ if corr != orig:
1032
+ _warn(line_no, f'Category "{orig}" is deprecated and should be replaced with "{corr}".')
1033
+ row[idx] = corr
1034
+
1035
+ elif table_name == "Item":
1036
+ cat_idx = header_index.get("category")
1037
+ name_idx = header_index.get("name")
1038
+ if cat_idx is not None:
1039
+ c_orig = row[cat_idx]
1040
+ c_corr = corrections.correctCategory(c_orig)
1041
+ if c_corr is DELETED:
1042
+ if tdenv.ignoreUnknown:
1043
+ _warn(line_no, f'Category "{c_orig}" is marked as DELETED and should not be used.')
1044
+ return True
1045
+ raise DeletedKeyError(importPath, line_no, "Category", c_orig)
1046
+ if c_corr != c_orig:
1047
+ _warn(line_no, f'Category "{c_orig}" is deprecated and should be replaced with "{c_corr}".')
1048
+ row[cat_idx] = c_corr
1049
+ if name_idx is not None:
1050
+ i_orig = row[name_idx]
1051
+ i_corr = corrections.correctItem(i_orig)
1052
+ if i_corr is DELETED:
1053
+ if tdenv.ignoreUnknown:
1054
+ _warn(line_no, f'Item "{i_orig}" is marked as DELETED and should not be used.')
1055
+ return True
1056
+ raise DeletedKeyError(importPath, line_no, "Item", i_orig)
1057
+ if i_corr != i_orig:
1058
+ _warn(line_no, f'Item "{i_orig}" is deprecated and should be replaced with "{i_corr}".')
1059
+ row[name_idx] = i_corr
1060
+
1061
+ # RareItem: we only correct category (FK lookup uses names) to improve hit rate.
1062
+ elif table_name == "RareItem":
1063
+ cat_idx = header_index.get("category")
1064
+ if cat_idx is not None:
1065
+ c_orig = row[cat_idx]
1066
+ c_corr = corrections.correctCategory(c_orig)
1067
+ if c_corr is DELETED:
1068
+ if tdenv.ignoreUnknown:
1069
+ _warn(line_no, f'Category "{c_orig}" is marked as DELETED and should not be used.')
1070
+ return True
1071
+ raise DeletedKeyError(importPath, line_no, "Category", c_orig)
1072
+ if c_corr != c_orig:
1073
+ _warn(line_no, f'Category "{c_orig}" is deprecated and should be replaced with "{c_corr}".')
1074
+ row[cat_idx] = c_corr
1075
+
1076
+ except BuildCacheBaseException:
1077
+ # strict mode path bubbles up; caller will handle
1078
+ raise
1079
+ return False # do not skip
1080
+
1081
+ # --- Read data lines ---
957
1082
  for linein in csvin:
958
1083
  if line_callback:
959
1084
  line_callback()
960
1085
  if not linein:
961
1086
  continue
1087
+
962
1088
  lineNo = csvin.line_num
963
1089
 
964
1090
  if len(linein) != columnCount:
965
- tdenv.NOTE(
966
- "Wrong number of columns ({}:{}): {}",
967
- importPath,
968
- lineNo,
969
- ", ".join(linein),
970
- )
1091
+ tdenv.NOTE("Wrong number of columns ({}:{}): {}", importPath, lineNo, ", ".join(linein))
971
1092
  continue
972
1093
 
973
1094
  tdenv.DEBUG1(" Values: {}", ", ".join(linein))
974
1095
 
975
- # deprecation checks
976
- if deprecationFn:
977
- try:
978
- deprecationFn(importPath, lineNo, linein)
979
- except (DeprecatedKeyError, DeletedKeyError) as e:
980
- if not tdenv.ignoreUnknown:
981
- raise e
982
- e.category = "WARNING"
983
- tdenv.NOTE("{}", e)
1096
+ # --- Apply corrections BEFORE uniqueness; may skip if deleted in tolerant mode
1097
+ try:
1098
+ if _apply_row_corrections(tableName, linein, lineNo):
984
1099
  continue
1100
+ except DeletedKeyError:
1101
+ if not tdenv.ignoreUnknown:
1102
+ # strict: fail hard
1103
+ raise
1104
+ # tolerant: already warned in _apply_row_corrections; skip row
1105
+ continue
985
1106
 
986
- # Build values aligned to activeColumns (skip the FK columns we excluded)
1107
+ # Extract and clean values to use (from corrected line)
987
1108
  activeValues = [linein[i] for i in kept_indices]
988
1109
 
989
- # unique index enforcement over activeColumns
990
- if uniqueIndexes:
991
- keyValues = [str(activeValues[i]).upper() for i in uniqueIndexes]
992
- key = ":!:".join(keyValues)
993
- prevLineNo = uniqueIndex.get(key, 0)
994
- if prevLineNo:
995
- key_disp = "/".join(keyValues)
996
- raise DuplicateKeyError(importPath, lineNo, "entry", key_disp, prevLineNo)
997
- uniqueIndex[key] = lineNo
1110
+ # --- Uniqueness check (after correction) ---
1111
+ try:
1112
+ if uniqueIndexes:
1113
+ keyValues = [str(activeValues[i]).upper() for i in uniqueIndexes]
1114
+ key = ":!:".join(keyValues)
1115
+ prevLineNo = uniqueIndex.get(key, 0)
1116
+ if prevLineNo:
1117
+ key_disp = "/".join(keyValues)
1118
+ if tdenv.ignoreUnknown:
1119
+ e = DuplicateKeyError(importPath, lineNo, "entry", key_disp, prevLineNo)
1120
+ e.category = "WARNING"
1121
+ tdenv.NOTE("{}", e)
1122
+ continue
1123
+ raise DuplicateKeyError(importPath, lineNo, "entry", key_disp, prevLineNo)
1124
+ uniqueIndex[key] = lineNo
1125
+ except Exception as e:
1126
+ # Keep processing the file, don’t tear down the loop
1127
+ tdenv.WARN(
1128
+ "*** INTERNAL ERROR: {err}\n"
1129
+ "CSV File: {file}:{line}\n"
1130
+ "Table: {table}\n"
1131
+ "Params: {params}\n".format(
1132
+ err=str(e),
1133
+ file=str(importPath),
1134
+ line=lineNo,
1135
+ table=tableName,
1136
+ params=linein,
1137
+ )
1138
+ )
1139
+ session.rollback()
1140
+ continue
998
1141
 
999
1142
  try:
1000
- # Base rowdict from non-FK columns only
1001
1143
  rowdict = dict(zip(activeColumns, activeValues))
1002
1144
 
1003
- # --- RareItem foreign key lookups ---
1145
+ # Foreign key lookups — RareItem
1004
1146
  if tableName == "RareItem":
1005
- # Resolve system (transient; only for station lookup)
1006
1147
  sys_id = None
1007
1148
  if "system" in fk_col_indices:
1008
1149
  sys_name = linein[fk_col_indices["system"]]
@@ -1011,7 +1152,6 @@ def processImportFile(
1011
1152
  except ValueError:
1012
1153
  tdenv.WARN("Unknown System '{}' in {}", sys_name, importPath)
1013
1154
 
1014
- # Station (requires system context)
1015
1155
  if "station" in fk_col_indices:
1016
1156
  stn_name = linein[fk_col_indices["station"]]
1017
1157
  if sys_id is not None:
@@ -1022,7 +1162,6 @@ def processImportFile(
1022
1162
  else:
1023
1163
  tdenv.WARN("Station lookup skipped (no system_id) for '{}'", stn_name)
1024
1164
 
1025
- # Category
1026
1165
  if "category" in fk_col_indices:
1027
1166
  cat_name = linein[fk_col_indices["category"]]
1028
1167
  try:
@@ -1030,7 +1169,7 @@ def processImportFile(
1030
1169
  except ValueError:
1031
1170
  tdenv.WARN("Unknown Category '{}' in {}", cat_name, importPath)
1032
1171
 
1033
- # --- System foreign key lookup (Added), default "EDSM" if blank ---
1172
+ # Foreign key lookups System.added
1034
1173
  if tableName == "System" and "added" in fk_col_indices:
1035
1174
  added_val = linein[fk_col_indices["added"]] or "EDSM"
1036
1175
  try:
@@ -1039,24 +1178,21 @@ def processImportFile(
1039
1178
  rowdict["added_id"] = None
1040
1179
  tdenv.WARN("Unknown Added value '{}' in {}", added_val, importPath)
1041
1180
 
1042
- # --- type coercion ---
1181
+ # --- Type coercion for common types ---
1043
1182
  for key, val in list(rowdict.items()):
1044
1183
  if val in ("", None):
1045
1184
  rowdict[key] = None
1046
1185
  continue
1047
- # ints
1048
1186
  if key.endswith("_id") or key.endswith("ID") or key in ("cost", "max_allocation"):
1049
1187
  try:
1050
1188
  rowdict[key] = int(val)
1051
1189
  except ValueError:
1052
1190
  rowdict[key] = None
1053
- # floats
1054
1191
  elif key in ("pos_x", "pos_y", "pos_z", "ls_from_star"):
1055
1192
  try:
1056
1193
  rowdict[key] = float(val)
1057
1194
  except ValueError:
1058
1195
  rowdict[key] = None
1059
- # datetimes
1060
1196
  elif "time" in key or key == "modified":
1061
1197
  parsed = parse_ts(val)
1062
1198
  if parsed:
@@ -1070,24 +1206,22 @@ def processImportFile(
1070
1206
  val,
1071
1207
  )
1072
1208
  rowdict[key] = None
1073
- # strings (incl. TriState flags) left as-is
1074
1209
 
1075
- # reserved word remaps
1210
+ # Special handling for SQL reserved word `class`
1076
1211
  if tableName == "Upgrade" and "class" in rowdict:
1077
1212
  rowdict["class_"] = rowdict.pop("class")
1078
1213
  if tableName == "FDevOutfitting" and "class" in rowdict:
1079
1214
  rowdict["class_"] = rowdict.pop("class")
1080
-
1081
- # ensure we never pass system_id to RareItem (not a column)
1082
1215
  if tableName == "RareItem" and "system_id" in rowdict:
1083
1216
  rowdict.pop("system_id", None)
1084
1217
 
1218
+ # ORM insert/merge
1085
1219
  Model = getattr(SA, tableName)
1086
1220
  obj = Model(**rowdict)
1087
1221
  session.merge(obj)
1088
1222
  importCount += 1
1089
1223
 
1090
- # batched commit (only if enabled for this backend)
1224
+ # Batch commit
1091
1225
  if max_transaction_items:
1092
1226
  transaction_items += 1
1093
1227
  if transaction_items >= max_transaction_items:
@@ -1096,6 +1230,7 @@ def processImportFile(
1096
1230
  transaction_items = 0
1097
1231
 
1098
1232
  except Exception as e:
1233
+ # Log all import errors — but keep going
1099
1234
  tdenv.WARN(
1100
1235
  "*** INTERNAL ERROR: {err}\n"
1101
1236
  "CSV File: {file}:{line}\n"
@@ -1110,10 +1245,13 @@ def processImportFile(
1110
1245
  )
1111
1246
  session.rollback()
1112
1247
 
1248
+ # Final commit after file done
1113
1249
  session.commit()
1114
1250
  tdenv.DEBUG0("{count} {table}s imported", count=importCount, table=tableName)
1115
1251
 
1116
1252
 
1253
+
1254
+
1117
1255
  def buildCache(tdb, tdenv):
1118
1256
  """
1119
1257
  Rebuilds the database from source files.
@@ -12,5 +12,5 @@
12
12
  """just keeper of current version"""
13
13
 
14
14
  # TODO: remember to update tests when version changes
15
- __version__ = '12.0.3'
15
+ __version__ = '12.0.5'
16
16
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tradedangerous
3
- Version: 12.0.3
3
+ Version: 12.0.5
4
4
  Summary: Trade-Dangerous is a set of powerful trading tools for Elite Dangerous, organized around one of the most powerful trade run optimizers available.
5
5
  Home-page: https://github.com/eyeonus/Trade-Dangerous
6
6
  Author: eyeonus
File without changes