Files
pyfa/scripts/findNonMarket.py
2017-06-12 16:12:45 -04:00

433 lines
17 KiB
Python
Executable File

#!/usr/bin/env python3
import copy
import os.path
import re
import sqlite3
script_dir = os.path.dirname(str(__file__, sys.getfilesystemencoding()))
# Connect to database and set up cursor
db = sqlite3.connect(os.path.join(script_dir, "..", "eve.db"))
cursor = db.cursor()
# Queries to get raw data
QUERY_ALLEFFECTS = 'SELECT effectID, effectName FROM dgmeffects'
# Limit categories to
# \Modules (7), Charges (8), Drones (18),
# Implants (20), Subsystems (32)
QUERY_PUBLISHEDTYPEIDS = 'SELECT it.typeID FROM invtypes AS it INNER JOIN \
invgroups AS ig ON it.groupID = ig.groupID INNER JOIN invcategories AS ic ON \
ig.categoryID = ic.categoryID WHERE it.published = 1 AND ic.categoryID IN \
(7, 8, 18, 20, 32)'
QUERY_TYPEID_GROUPID = 'SELECT groupID FROM invtypes WHERE typeID = ? LIMIT 1'
QUERY_GROUPID_CATEGORYID = 'SELECT categoryID FROM invgroups WHERE \
groupID = ? LIMIT 1'
QUERY_TYPEID_PARENTTYPEID = 'SELECT parentTypeID FROM invmetatypes WHERE \
typeID = ? LIMIT 1'
QUERY_TYPEID_MARKETGROUPID = 'SELECT marketGroupID FROM invtypes WHERE \
typeID = ? LIMIT 1'
QUERY_TYPEID_TYPENAME = 'SELECT typeName FROM invtypes WHERE typeID = ? \
LIMIT 1'
QUERY_MARKETGROUPID_PARENTGROUPID = 'SELECT parentGroupID FROM \
invmarketgroups WHERE marketGroupID = ? LIMIT 1'
QUERY_EFFECTID_TYPEID = 'SELECT typeID FROM dgmtypeeffects WHERE effectID = ?'
# Queries for printing
QUERY_GROUPID_GROUPNAME = 'SELECT groupName FROM invgroups WHERE groupID = ? \
LIMIT 1'
QUERY_CATEGORYID_CATEGORYNAME = 'SELECT categoryName FROM invcategories \
WHERE categoryID = ? LIMIT 1'
QUERY_MARKETGROUPID_MARKETGROUPNAME = 'SELECT marketGroupName FROM \
invmarketgroups WHERE marketGroupID = ? LIMIT 1'
QUERY_TYPEID_ATTRIBS = 'SELECT da.attributeName, dta.value FROM dgmattribs AS \
da INNER JOIN dgmtypeattribs AS dta ON dta.attributeID = da.attributeID WHERE \
dta.typeID = ?'
QUERY_TYPEID_BASEATTRIBS = 'SELECT volume, mass, capacity FROM invtypes WHERE \
typeID = ?'
QUERY_TYPEID_METAGROUPID = 'SELECT metaGroupID FROM invmetatypes WHERE typeID = ?'
QUERY_METAGROUPNAME_METAGROUPID = 'SELECT metaGroupName FROM invmetagroups WHERE metaGroupID = ?'
# Compose list of effects w/o symbols which eos doesn't take into
# consideration, we'll use it to find proper effect IDs from file
# names
globalmap_effectnameeos_effectid = {}
STRIPSPEC = "[^A-Za-z0-9]"
cursor.execute(QUERY_ALLEFFECTS)
for row in cursor:
effectid = row[0]
effectnamedb = row[1]
effectnameeos = re.sub(STRIPSPEC, "", effectnamedb)
# There may be different effects with the same name, so form
# sets of IDs
if not effectnameeos in globalmap_effectnameeos_effectid:
globalmap_effectnameeos_effectid[effectnameeos] = set()
globalmap_effectnameeos_effectid[effectnameeos].add(effectid)
# Published types set
publishedtypes = set()
cursor.execute(QUERY_PUBLISHEDTYPEIDS)
for row in cursor:
publishedtypes.add(row[0])
# Compose group maps
# { groupid : set(typeid) }
globalmap_groupid_typeid = {}
# { typeid : groupid }
globalmap_typeid_groupid = {}
for typeid in publishedtypes:
groupid = 0
cursor.execute(QUERY_TYPEID_GROUPID, (typeid,))
for row in cursor:
groupid = row[0]
if not groupid in globalmap_groupid_typeid:
globalmap_groupid_typeid[groupid] = set()
globalmap_groupid_typeid[groupid].add(typeid)
globalmap_typeid_groupid[typeid] = groupid
# Category maps
# { categoryid : set(typeid) }
globalmap_categoryid_typeid = {}
# { typeid : categoryid }
globalmap_typeid_categoryid = {}
for typeid in publishedtypes:
categoryid = 0
cursor.execute(QUERY_GROUPID_CATEGORYID,
(globalmap_typeid_groupid[typeid],))
for row in cursor:
categoryid = row[0]
if not categoryid in globalmap_categoryid_typeid:
globalmap_categoryid_typeid[categoryid] = set()
globalmap_categoryid_typeid[categoryid].add(typeid)
globalmap_typeid_categoryid[typeid] = categoryid
# Base type maps
# { basetypeid : set(typeid) }
globalmap_basetypeid_typeid = {}
# { typeid : basetypeid }
globalmap_typeid_basetypeid = {}
for typeid in publishedtypes:
# Not all typeIDs in the database have baseTypeID, so assign some
# default value to it
basetypeid = 0
cursor.execute(QUERY_TYPEID_PARENTTYPEID, (typeid,))
for row in cursor:
basetypeid = row[0]
# If base type is not published or is not set in database, consider
# item as variation of self
if basetypeid not in publishedtypes:
basetypeid = typeid
if not basetypeid in globalmap_basetypeid_typeid:
globalmap_basetypeid_typeid[basetypeid] = set()
globalmap_basetypeid_typeid[basetypeid].add(typeid)
globalmap_typeid_basetypeid[typeid] = basetypeid
# Market group maps - we won't use these for further processing, but
# just as helper for composing other maps
# { marketgroupid : set(typeid) }
globalmap_marketgroupid_typeid = {}
# { typeid : set(marketgroupid) }
globalmap_typeid_marketgroupid = {}
for typeid in publishedtypes:
marketgroupid = 0
cursor.execute(QUERY_TYPEID_MARKETGROUPID, (typeid,))
for row in cursor:
marketgroupid = row[0]
if not marketgroupid:
continue
if not marketgroupid in globalmap_marketgroupid_typeid:
globalmap_marketgroupid_typeid[marketgroupid] = set()
globalmap_marketgroupid_typeid[marketgroupid].add(typeid)
# Copy items to all parent market groups
INITIALMARKETGROUPIDS = tuple(globalmap_marketgroupid_typeid)
for marketgroupid in INITIALMARKETGROUPIDS:
# Limit depths for case if database will refer to groups making
# the loop
cyclingmarketgroupid = marketgroupid
for depth in range(20):
cursor_parentmarket = db.cursor()
cursor_parentmarket.execute(QUERY_MARKETGROUPID_PARENTGROUPID,
(cyclingmarketgroupid,))
for row in cursor_parentmarket:
cyclingmarketgroupid = row[0]
if cyclingmarketgroupid:
if not cyclingmarketgroupid in globalmap_marketgroupid_typeid:
globalmap_marketgroupid_typeid[cyclingmarketgroupid] = set()
globalmap_marketgroupid_typeid[cyclingmarketgroupid].update\
(globalmap_marketgroupid_typeid[marketgroupid])
else: break
# Now, make a reverse map
for marketgroupid, typeidset in list(globalmap_marketgroupid_typeid.items()):
for typeid in typeidset:
if not typeid in globalmap_typeid_marketgroupid:
globalmap_typeid_marketgroupid[typeid] = set()
globalmap_typeid_marketgroupid[typeid].add(marketgroupid)
# Combine market groups and variations
# { marketgroupid : set(typeidwithvariations) }
globalmap_marketgroupid_typeidwithvariations = \
copy.deepcopy(globalmap_marketgroupid_typeid)
# { typeidwithvariations : set(marketgroupid) }
globalmap_typeidwithvariations_marketgroupid = {}
for marketgroupid in globalmap_marketgroupid_typeidwithvariations:
typestoadd = set()
for typeid in globalmap_marketgroupid_typeidwithvariations[marketgroupid]:
if typeid in globalmap_basetypeid_typeid:
for variationid in globalmap_basetypeid_typeid[typeid]:
# Do not include items which have market group, even if
# they're variation
if not variationid in globalmap_typeid_marketgroupid:
typestoadd.add(variationid)
globalmap_marketgroupid_typeidwithvariations[marketgroupid].update\
(typestoadd)
# Make reverse map using simple way too
for marketgroupid, typeidwithvariationsset in \
list(globalmap_marketgroupid_typeidwithvariations.items()):
for typeid in typeidwithvariationsset:
if not typeid in globalmap_typeidwithvariations_marketgroupid:
globalmap_typeidwithvariations_marketgroupid[typeid] = set()
globalmap_typeidwithvariations_marketgroupid[typeid].add(marketgroupid)
nonmarket = set()
for typeid in publishedtypes:
if not typeid in globalmap_typeidwithvariations_marketgroupid:
nonmarket.add(typeid)
def getItemAttrs(typeid):
attrs = {}
cursor.execute(QUERY_TYPEID_ATTRIBS, (typeid,))
for row in cursor:
attrs[row[0]] = row[1]
cursor.execute(QUERY_TYPEID_BASEATTRIBS, (typeid,))
for row in cursor:
if row[0] is not None:
attrs["volume"] = row[0]
if row[1] is not None:
attrs["mass"] = row[1]
if row[2] is not None:
attrs["capacity"] = row[2]
return attrs
def suggestMktGrp(typeid, mode="grp"):
typecat = globalmap_typeid_categoryid[typeid]
catname = ""
cursor.execute(QUERY_CATEGORYID_CATEGORYNAME, (typecat,))
for row in cursor:
catname = row[0]
typename = ""
cursor.execute(QUERY_TYPEID_TYPENAME, (typeid,))
for row in cursor:
typename = row[0]
if catname.lower() == "module" and "civilian" in typename.lower():
return 760
attrs = getItemAttrs(typeid)
implantness = None
boosterness = None
cpu = None
power = None
droneBandwidthUsed = None
volume = None
if "implantness" in attrs:
implantness = attrs["implantness"]
if "boosterness" in attrs:
boosterness = attrs["boosterness"]
if "cpu" in attrs:
cpu = attrs["cpu"]
if "power" in attrs:
power = attrs["power"]
if "droneBandwidthUsed" in attrs:
droneBandwidthUsed = attrs["droneBandwidthUsed"]
if "volume" in attrs:
volume = attrs["volume"]
if mode == "grp":
grp = globalmap_typeid_groupid[typeid]
comrades = globalmap_groupid_typeid[grp]
elif mode == "cat":
cat = globalmap_typeid_categoryid[typeid]
comrades = globalmap_categoryid_typeid[cat]
mktgrps_w_cos = {}
for co in comrades:
marketgroupid = 0
cursor.execute(QUERY_TYPEID_MARKETGROUPID, (co,))
for row in cursor:
marketgroupid = row[0]
if not marketgroupid:
continue
if not marketgroupid in mktgrps_w_cos:
mktgrps_w_cos[marketgroupid] = 0.0
similarity_factor = 1.0
metagrp = 0
cursor.execute(QUERY_TYPEID_METAGROUPID, (co,))
for row in cursor:
metagrp = row[0]
if not metagrp in (0,1,2,14):
similarity_factor *= 0.01
if implantness or boosterness or cpu or power or droneBandwidthUsed or volume:
cgrpattrs = getItemAttrs(co)
if implantness:
if "implantness" in cgrpattrs:
if cgrpattrs["implantness"] != implantness:
similarity_factor *= 0.1
else:
similarity_factor *= 0.01
if boosterness:
if "boosterness" in cgrpattrs:
if cgrpattrs["boosterness"] != boosterness:
similarity_factor *= 0.1
else:
similarity_factor *= 0.01
if cpu:
if "cpu" in cgrpattrs and cgrpattrs["cpu"]:
fct = cpu / cgrpattrs["cpu"]
if fct > 1:
fct = 1 / fct
similarity_factor *= fct
else:
similarity_factor *= 0.01
if power:
if "power" in cgrpattrs and cgrpattrs["power"]:
fct = power / cgrpattrs["power"]
if fct > 1:
fct = 1 / fct
similarity_factor *= fct
else:
similarity_factor *= 0.01
if droneBandwidthUsed:
if "droneBandwidthUsed" in cgrpattrs:
fct = droneBandwidthUsed / cgrpattrs["droneBandwidthUsed"]
if fct > 1:
fct = 1 / fct
similarity_factor *= fct
else:
similarity_factor *= 0.01
if volume:
if "volume" in cgrpattrs:
fct = volume / cgrpattrs["volume"]
if fct > 1:
fct = 1 / fct
similarity_factor *= fct
else:
similarity_factor *= 0.01
mktgrps_w_cos[marketgroupid] += similarity_factor
if mktgrps_w_cos:
winner = max(list(mktgrps_w_cos.keys()), key=lambda k: mktgrps_w_cos[k])
else:
winner = None
return winner
def suggestMetaGrp(typeid):
typename = ""
cursor.execute(QUERY_TYPEID_TYPENAME, (typeid,))
for row in cursor:
typename = row[0]
faction_affixes = ("Arch Angel", "Domination", "Blood", "Guristas", "Sansha", "Sanshas", "Shadow", "Guardian", "Serpentis",
"Caldari", "Imperial", "Gallente", "Federation", "Republic",
"Ammatar", "Khanid", "Thukker", "Syndicate", "Sisters", "Legion", "ORE",
"Nugoehuvi")
deadspace_affixes = ("Gistii", "Gistum", "Gist",
"Corpii", "Corpum", "Corpus",
"Pithi", "Pithum", "Pith",
"Centii", "Centum", "Centus",
"Coreli", "Corelum", "Core")
storyline_names = {"Akemon", "Michi", "Ogdin", "Pashan", "Shaqil", "Whelan Machorin", "Numon"}
officer_names = ("Ahremen", "Brokara", "Brynn", "Chelm", "Cormack", "Draclira", "Estamel", "Gotan", "Hakim",
"Kaikka", "Mizuro", "Raysere", "Selynne", "Setele", "Tairei", "Thon", "Tuvan", "Vizan")
storyline_pattern_general = "'[A-Za-z ]+'"
storyline_pattern_names = "|".join("{0}".format(name) for name in storyline_names)
faction_pattern = "({0}) ".format("|".join(faction_affixes))
deadspace_pattern = "({0}) ".format("|".join(deadspace_affixes))
officer_pattern = "({0}) ".format("|".join("{0}'s".format(name) for name in officer_names))
attrs = getItemAttrs(typeid)
if attrs.get("metaLevel") is not None:
mlvl = attrs["metaLevel"]
if mlvl in (0, 1, 2, 3, 4):
meta = 1
elif mlvl == 5:
meta = 2
elif mlvl in (6, 7):
meta = 3
elif mlvl in (8, 9):
meta = 4
elif mlvl in (11, 12, 13, 14):
if re.search(deadspace_pattern, typename):
meta = 6
else:
meta = 5
else:
meta = 1
elif re.search(officer_pattern, typename):
meta = 5
elif re.search(deadspace_pattern, typename):
meta = 6
elif re.search(faction_pattern, typename):
meta = 4
elif re.search(storyline_pattern_names, typename):
meta = 3
elif re.search(storyline_pattern_general, typename) and not "Hardwiring" in typename:
meta = 3
else:
meta = 1
return meta
map_typeid_stuff = {}
map_typeid_stuff2 = {}
for typeid in nonmarket:
typename = ""
cursor.execute(QUERY_TYPEID_TYPENAME, (typeid,))
for row in cursor:
typename = row[0]
grpname = ""
cursor.execute(QUERY_GROUPID_GROUPNAME, (globalmap_typeid_groupid[typeid],))
for row in cursor:
grpname = row[0]
mkt = suggestMktGrp(typeid)
if mkt is None:
mkt = suggestMktGrp(typeid, mode="cat")
meta = suggestMetaGrp(typeid)
attrs = getItemAttrs(typeid)
if mkt:
map_typeid_stuff[typeid] = (mkt, meta)
marketgroupname = ""
cursor.execute(QUERY_MARKETGROUPID_MARKETGROUPNAME,
(mkt,))
for row in cursor:
marketgroupname = row[0]
# Prepend market group name with its parents names
prependparentid = mkt
# Limit depth to avoid looping, as usual
for depth in range(20):
cursor_parentmarket = db.cursor()
cursor_parentmarket.execute(QUERY_MARKETGROUPID_PARENTGROUPID,
(prependparentid,))
for row in cursor_parentmarket:
prependparentid = row[0]
if prependparentid:
cursor_parentmarket2 = db.cursor()
cursor_parentmarket2.execute(QUERY_MARKETGROUPID_MARKETGROUPNAME,
(prependparentid,))
for row in cursor_parentmarket2:
marketgroupname = "{0} > {1}".format(row[0],
marketgroupname)
else:
break
else:
marketgroupname = "None"
map_typeid_stuff2[typename] = (mkt, marketgroupname)
metagroupname = ""
cursor.execute(QUERY_METAGROUPNAME_METAGROUPID,
(meta,))
for row in cursor:
metagroupname = row[0]
#print("---\nItem: {0}\nGroup: {1}\nSuggested market group: {2} ({3})\nMeta group: {4}".format(typename, grpname, marketgroupname, mkt, metagroupname))
#print("\n\nmap = {{ {0} }}".format(", ".join("{0}: ({1}, {2})".format(key, map_typeid_stuff[key][0], map_typeid_stuff[key][1]) for key in sorted(map_typeid_stuff))))
print(("---\n{0}".format("\n".join("\"{0}\": {1}, # {2}".format(key, map_typeid_stuff2[key][0], map_typeid_stuff2[key][1]) for key in sorted(map_typeid_stuff2)))))