Last active
March 14, 2024 16:49
-
-
Save suchmememanyskill/943447b6b35ac5aa2d921e8071c6cad0 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # 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