Skip to content

Instantly share code, notes, and snippets.

@suchmememanyskill
Last active March 14, 2024 16:49
Show Gist options
  • Select an option

  • Save suchmememanyskill/943447b6b35ac5aa2d921e8071c6cad0 to your computer and use it in GitHub Desktop.

Select an option

Save suchmememanyskill/943447b6b35ac5aa2d921e8071c6cad0 to your computer and use it in GitHub Desktop.
# Based on https://gist.github.com/PartyWumpus/b1bc83b5b29b155e40742d0aa290f0db
# https://github.com/steamdatabase/steamtracking
# git diff HEAD~1 HEAD --unified=0 --no-color "*.css" > diff
# then
from collections import Counter
import cssselect
import json
import uuid
import os
import re
f = open("diff", encoding="utf-8")
before = ""
name_map = {}
thevariable = None
def find_names(selector):
global thevariable
if type(selector) == list:
arr = []
for new in selector:
res = find_names(new)
if type(res) == list:
arr.extend(res)
else:
arr.append(res)
return arr
elif type(selector) == cssselect.Selector:
return find_names(selector.parsed_tree)
elif type(selector) == cssselect.parser.CombinedSelector or type(selector) == cssselect.parser.Negation:
arr = []
for new in [find_names(selector.selector), find_names(selector.subselector)]:
res = find_names(new)
if type(res) == list:
arr.extend(res)
else:
arr.append(res)
return arr
elif type(selector) == cssselect.parser.Class:
arr = []
res = find_names(selector.selector)
if type(res) == list:
arr.extend(res)
else:
arr.append(res)
arr.append(selector.class_name.strip())
return arr
elif type(selector) == cssselect.parser.Pseudo or type(selector) == cssselect.parser.Attrib or type(selector) == cssselect.parser.Function:
return find_names(selector.selector)
elif type(selector) == cssselect.parser.Hash:
return find_names(f"#{selector.id}")
elif type(selector) == cssselect.parser.Element:
if selector.element == "to" or selector.element == "from":
return None
res = selector.element
if res is not None:
return res.strip()
return None
elif type(selector) == str:
return selector.strip()
elif selector is None:
return None
else:
thevariable = selector
print("yet to be handled!", selector, type(selector))
import code
code.InteractiveConsole(locals=globals()).interact()
return [None]
def parserer(string):
string = string[1:-1]
if string.startswith("@keyframes"):
return [string[11:]]
if string.startswith("@-webkit-keyframes"):
return [string[19:]]
else:
try:
return [x for x in find_names(cssselect.parse(string)) if x is not None]
except cssselect.parser.SelectorSyntaxError as e:
return ['SKIP']
CLASS_NAMES = []
PRE_CONVERTED_CLASS_NAMES = []
for line in f.readlines():
line = line.strip()
if line.startswith("+") and not line.startswith("+++"):
classnames = re.findall(r"(\.[_a-zA-Z]+[_a-zA-Z0-9-]*)", line[1:])
for x in classnames:
if x not in CLASS_NAMES:
CLASS_NAMES.append(x)
if line.startswith("-") and not line.startswith("---"):
classnames = re.findall(r"(\.[_a-zA-Z]+[_a-zA-Z0-9-]*)", line[1:])
for x in [re.sub(r"[^_a-zA-Z0-9-]", '', y) for y in classnames]:
if x not in PRE_CONVERTED_CLASS_NAMES:
PRE_CONVERTED_CLASS_NAMES.append(x)
# skip line if it isn't a CSS class (ends in '{')
if line[-1] != "{":
continue
if line[0] == "-":
before = line
if line[0] == "+":
after = parserer(line)
beforee = parserer(before)
if before == 'SKIP' or after == 'SKIP':
continue
if len(beforee) != len(after):
continue
for index, class_before in enumerate(beforee):
if class_before not in name_map:
name_map[class_before.strip()] = []
name_map[class_before.strip()].append(after[index].strip())
def find_best_fit(arr) -> str:
if len(arr) == 1:
return arr[0]
count = Counter(name_map[x]).most_common()
if len(count) == 1 or count[0][1] > count[1][1]:
return count[0][0]
# there are only like 6 of these
print("failure for:", x, count)
return "FAIL"
final = {}
for x in name_map:
# there will be an array of several different classes.
# we want to pick the one that shows up the most, as it is probably the correct one
final[x] = find_best_fit(name_map[x])
final.pop(None, None)
PRE_CONVERTED_CLASS_NAMES = [x for x in PRE_CONVERTED_CLASS_NAMES if x not in final]
print(f"Missed {len(PRE_CONVERTED_CLASS_NAMES)} classes")
for x in PRE_CONVERTED_CLASS_NAMES:
final[x] = "MISSED!"
fix1 = 0
fix2 = 0
still_wrong = []
# Pass 1
for x in final:
y = final[x]
if not x[-5:] in y[:7] and x != y:
print(x, y, "is probably wrong")
if re.match(r"^_[_a-zA-Z0-9-]{5}$", x[-6:]) is not None:
end = x[-6:]
found = []
for z in CLASS_NAMES:
if z.startswith("." + end):
print(f" -> Found possible fix: {z}")
found.append(z[1:])
if len(found) > 0:
final[x] = found[0]
fix1 += 1
else:
still_wrong.append(x)
else:
still_wrong.append(x)
# Pass 2
print()
still_still_wrong = []
for x in still_wrong:
if re.match(r"^_[_a-zA-Z0-9-]{5}$", x[-6:]) is not None:
print(f"Finding more lax fix for {x}")
end = x[-5:]
found = []
for z in CLASS_NAMES:
if z.startswith("." + end):
print(f" -> Found possible fix: {z}")
found.append(z[1:])
break
if len(found) > 0:
fix2 += 1
final[x] = found[0]
else:
still_still_wrong.append(x)
else:
still_still_wrong.append(x)
print()
for x in still_still_wrong:
print(f"Unable to fix {x} with value {final[x]}")
print(f"Fixed {fix1} in pass 1 and {fix2} in pass 2. Unable to fix {len(still_still_wrong)}.")
f.close()
translations = {}
if os.path.isfile("css_translations.json"):
with open("css_translations.json", encoding="utf-8") as fp:
translations = json.load(fp)
# TODO: Sometimes there's duplicates here
for x in final:
if final[x] in ["MISSED!", "FAIL", "SKIP"]:
print(f"Skipping storing {x} ({final[x]}) due to bad value")
continue
if (x == final[x]):
continue
found = False
for y in translations:
if x in translations[y]:
found = True
if final[x] not in translations[y]:
print(f"Extending {y} with {final[x]}")
translations[y].append(final[x])
break
if found:
continue
uid = str(uuid.uuid4())
translations[uid] = [x, final[x]]
with open("css_translations.json", 'w', encoding="utf-8") as fp:
json.dump(translations, fp)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment