Skip to content

Instantly share code, notes, and snippets.

@arav97531
Last active May 31, 2022 20:31
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save arav97531/48b166e1941057bcf35d0b3c129e4195 to your computer and use it in GitHub Desktop.
Save arav97531/48b166e1941057bcf35d0b3c129e4195 to your computer and use it in GitHub Desktop.
Elite: Dangerous Journal Extractor
#!/usr/bin/env python3
"""Elite: Dangerous Journal Extractor
Is a tool for extracting information from journal files.
Features:
- extract your discoveries into a CSV table;
- make an ImportStars.txt file.
Changelog:
Version 1.0.1 - 19th October 2018
- sorting discoveries by scan timestamp;
- edit comments, minor fixes.
Version 1.0.0 - 15th October 2018
- extracting discoveries made by commander into a CSV table;
- extracting visited stars into an ImportStars.txt file;
- automatically place an ImportStars.txt file in a proper directory.
Optional.
"""
__version__ = "1.0.1"
__author__ = "Arav"
__email__ = "me@arav.icu"
__copyright__ = f"Copyright © 2018 {__author__} <{__email__}>"
__license__ = """
This program is free software. It comes without any warranty, to
the extent permitted by applicable law. You can redistribute it
and/or modify it under the terms of the Do What The Fuck You Want
To Public License, Version 2, as published by Sam Hocevar. See
http://www.wtfpl.net/ for more details.
"""
from argparse import ArgumentParser
from json import loads
from os import getenv, listdir, path
ED_JOURNAL_PATH = path.join(getenv("HOMEPATH"), \
"Saved Games\\Frontier Developments\\Elite Dangerous")
ED_LOCAL_PATH = path.join(getenv("LOCALAPPDATA"), \
"Frontier Developments\\Elite Dangerous")
TABLE_HEADER = [
"Scan timestamp (GMT)",
"Sell timestamp (GMT)",
"Body name",
"Body type",
"Body class",
"Distance from arrival (Ls)"]
def sort_a_list_by_tuple_element(list_of_tuples, element_idx=0):
"""Sort a list of tuples by tuple's element of given index.
``list_of_tuples`` is a list of tuples to be sorted.
``element_idx`` is an index of tuple's element.
"""
return sorted(list_of_tuples, key=lambda x: x[element_idx])
def get_visited_stars_cache_dir():
"""Return a path to the directory contains a VistedStarsCache.dat file.
Returns None if this directory wasn't finded.
"""
for entry in listdir(ED_LOCAL_PATH):
entry = path.join(ED_LOCAL_PATH, entry)
if path.isdir(entry):
for file_ in listdir(path.join(ED_LOCAL_PATH, entry)):
if file_ == "VisitedStarsCache.dat":
return path.join(ED_LOCAL_PATH, entry)
return None
def csv_write(data, file_name, header=None, delimeter=";"):
"""Write a list of tuples into a file in CSV format.
``data`` is a list of tuples to be written.
``file_name`` is a name of an output file.
``header`` is an optional header for a table.
``delimeter`` is a symbol or a string used as a separator for table elements.
"""
if header and len(header) != len(data[0]):
raise ValueError("Lenght of a header (%i) and data row (%i) mismatch." \
% (len(header), len(data[0])))
with open(file_name, "w") as csv_f:
if header:
csv_f.write(delimeter.join(header) + "\n")
for entry in data:
csv_f.write(delimeter.join(map(str, entry)) + "\n")
def list_file_write(data, file_name):
"""Write a list into a file.
``data`` is a list to be written.
``file_name`` is a name of an output file.
"""
with open(file_name, "w") as f:
for entry in data:
f.write(entry + "\n")
def journal_entries(journal_path):
"""Return entries from journal files. The entries are deserialised from JSON
to Python object.
``path`` is the path to the directory that contains the journal files. Must
be absolute.
"""
for journal in listdir(journal_path):
if not journal.endswith('log'):
continue
journal = path.join(journal_path, journal)
for entry in open(journal):
yield loads(entry)
def extract_discoveries(journal_path):
"""Extract only bodies that were discovered by you. In form of list of
tuples.
Each tuple contains following data in this order:
scan timestamp
sell timestamp
body's name
body's type
body's class
Its distance from arrive in Ls (light seconds)
``journal_path`` is a path to the place where your E:D journal logs are.
"""
scans = dict()
discoveries = list()
for entry in journal_entries(journal_path):
if entry["event"] == "Scan":
scans[entry["BodyName"]] = (
# scan timestamp #0
entry["timestamp"].replace("T", " ").replace("Z", ""),
# body type #1
"Planet" if "PlanetClass" in entry else "Star" \
if "StarType" in entry else "Asteroid Belt",
# body class #2
entry["PlanetClass"] if "PlanetClass" in entry \
else entry["StarType"] if "StarType" in entry else "",
# body's distance from center of star system #3
entry["DistanceFromArrivalLS"]
)
elif entry["event"] == "SellExplorationData":
for discovery in entry["Discovered"]:
discoveries.append((
scans[discovery][0],
entry["timestamp"].replace("T", " ").replace("Z", ""),
discovery,
scans[discovery][1],
scans[discovery][2],
scans[discovery][3]
))
del scans
return sort_a_list_by_tuple_element(discoveries)
def extract_visited_stars(journal_path):
"""Return a list of your visited stars.
``journal_path`` is a path to the place where your E:D journal logs are.
"""
visited_stars = list()
for entry in journal_entries(journal_path):
if entry["event"] == "FSDJump":
if not entry["StarSystem"] in visited_stars:
visited_stars.append(entry["StarSystem"])
return visited_stars
def main():
args_parser = ArgumentParser(
prog="ed-journal-extractor",
description="Tool for extraction information from E:D journal files. " \
"This tool can extract your discoveries into a CSV table and " \
"make an ImportStars.txt file with your visited stars. ",
epilog="A file ImportStars.txt goes into a numbered " \
"directory in '%%localappdata%%\\Frontier Developments\\Elite " \
"Dangerous\\' where a file VisitedStarsCache.dat is. The game will " \
"import this list on the next restart.")
args_parser.add_argument("-v", "--version", action="store_true",
help="show version of the program")
args_parser.add_argument("-J", "--journal-dir",
help="specifies different directory to look for journals")
args_parser.add_argument("-D", "--discoveries-file",
help="specifies different name for discoveries output file")
args_parser.add_argument("-d", "--discoveries", action="store_true",
help="extract your discoveries into a CSV table. Default action.")
args_parser.add_argument("-s", "--visited-stars", action="store_true",
help="make an ImportStars.txt file")
args_parser.add_argument("-S", "--visited-stars-auto", action="store_true",
help="make an ImportStars.txt and place it into a proper directory")
args = args_parser.parse_args()
if args.version:
print("Elite: Dangerous Journal Extractor\n" \
f"Version: {__version__}\n{__copyright__}\n{__license__}")
exit()
journal_path = args.journal_dir or ED_JOURNAL_PATH
discoveries_file = args.discoveries_file or "discoveries.csv"
if args.visited_stars:
try:
visited_stars = extract_visited_stars(journal_path)
list_file_write(visited_stars, "ImportStars.txt")
except Exception as ex:
print(f"Cannot make a list of visited stars. Error: {ex}")
elif args.visited_stars_auto:
path = get_visited_stars_cache_dir()
if not path:
print("VisitedStarsCache.dat's location not found. Saving in " \
"working directory.")
path = "ImportStars.txt"
try:
visited_stars = extract_visited_stars(journal_path)
list_file_write(visited_stars, path)
except Exception as ex:
print(f"Cannot make a list of visited stars. Error: {ex}")
if args.discoveries or not args.visited_stars \
or not args.visited_stars_auto:
try:
discoveries = extract_discoveries(journal_path)
csv_write(discoveries, discoveries_file, TABLE_HEADER)
except Exception as ex:
print(f"Cannot retrieve a list of your discoveries. Error: {ex}")
print(ex.with_traceback())
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment