diff --git a/app.py b/app.py index e104f65..20af1eb 100644 --- a/app.py +++ b/app.py @@ -1,15 +1,11 @@ # -*- coding: utf-8 -*- -""" -My super duper codelookup replacement. -""" - -from flask import Flask, redirect, render_template, request, url_for, Markup import time -import requests -import json import re +from flask import Flask, redirect, render_template, request, url_for +import requests + app = Flask(__name__) HEADERS = { @@ -29,10 +25,7 @@ def get_language_code(code, state="code"): "zh-min-nan": "nan", "zh-yue": "yue" } - if code in remap: - return remap[code] - else: - return code.split("-")[0] + return remap.get(code, code.partition("-")[0]) elif state == "sitecode": remap = { "sgs": "bat-smg", @@ -43,26 +36,27 @@ def get_language_code(code, state="code"): "nan": "zh-min-nan", "yue": "zh-yue" } - if code in remap: - return remap[code] - else: - return code + return remap.get(code, code) else: return code else: return "INVALID" def get_code_data(lang): - api = "https://hub.toolforge.org/{}:{}?format=json&lang=en" - if len(lang) == 2: - api = api.format("P218", lang) - else: - api = api.format("P220", lang) - req = requests.get(api, headers=HEADERS).json() - if "destination" in req: - return req - else: - return False + prop = "P218" if len(lang) == 2 else "P220" + + res = requests.get( + "https://hub.toolforge.org/{}:{}".format(prop, lang), + headers=HEADERS, + params={"format": "json", "lang": "en"} + ) + res.raise_for_status() + data = res.json() + + if "destination" not in data: + return None + + return data def get_twn_stats(lang): api = "https://translatewiki.net/w/api.php" @@ -72,20 +66,23 @@ def get_twn_stats(lang): "meta": "languagestats", "lslanguage": lang } - req = requests.get(api, params=params, headers=HEADERS).json() - if "error" in req and req["error"]["code"] == "translate-invalidlanguage": - return False - else: - return req["query"]["languagestats"] + + res = requests.get(api, params=params, headers=HEADERS) + data = res.json() + errorCode = data.get("error", {}).get("code") + if errorCode == "translate-invalidlanguage": + return None + return data["query"]["languagestats"] def get_group_stats(stats, groups): ret = {} for group in stats: - if group["group"] in groups: + _group = group["group"] + if _group in groups: total = group["total"] translated = group["translated"] percentage = int(translated) / int(total) * 100 - ret[group["group"]] = { + ret[_group] = { "translated": translated, "total": total, "percentage": round(percentage, 2) @@ -100,14 +97,14 @@ def get_mediawiki_languages(code): "meta": "siteinfo", "siprop": "languages" } - req = requests.get(api, params=params, headers=HEADERS).json()["query"]["languages"] - match = False - for entry in req: - if entry["code"] == code: - match = True - break - return match - + + res = requests.get(api, params=params, headers=HEADERS) + data = res.json() + + languages = data["query"]["languages"] + + return any(entry.get("code") == code for entry in languages) + def get_wikidata_languages(code): api = "https://www.wikidata.org/w/api.php" params = { @@ -115,11 +112,13 @@ def get_wikidata_languages(code): "format": "json", "meta": "wbcontentlanguages" } - req = requests.get(api, params=params, headers=HEADERS).json()["query"]["wbcontentlanguages"] - if code in req: - return True - else: - return False + + res = requests.get(api, params=params, headers=HEADERS) + data = res.json() + + wbcontentlanguages = data["query"]["wbcontentlanguages"] + + return code in wbcontentlanguages def get_wikimedia_sites(code): api = "https://meta.wikimedia.org/w/api.php" @@ -129,25 +128,31 @@ def get_wikimedia_sites(code): "smtype": "language", "smlimit": 5000 } - req = requests.get(api, params=params, headers=HEADERS).json()["sitematrix"] - match = False - for entry in req: + + req = requests.get(api, params=params, headers=HEADERS) + data = req.json() + sitematrix = data["sitematrix"] + + match = None + for entry, val in sitematrix.items(): if entry == "count": continue - else: - if req[entry]["code"] == code: - match = req[entry] - break - if match: - ret = {} - for site in match["site"]: - if "closed" in site: - continue - else: - ret[site["code"]] = site["sitename"] - return ret - else: - return False + + if val.get("code") == code: + match = val + break + + if match is None: + return None + + ret = {} + + for site in match["site"]: + if "closed" in site: + continue + ret[site["code"]] = site["sitename"] + + return ret def get_everything(langcode): code = get_language_code(langcode) @@ -175,179 +180,57 @@ def get_everything(langcode): }, "groupstats": {} } - - if code != "INVALID": - res["wellformed"] = True - else: + + if code == "INVALID": return res - - if get_code_data(lang): - res["actual"] = get_code_data(lang)["origin"]["qid"] - try: - res["languagename"] = get_code_data(lang)["destination"]["preferedSitelink"]["title"] - except: - pass - + + res["wellformed"] = True + + codeData = get_code_data(lang) + + if codeData: + res["actual"] = codeData["origin"]["qid"] + languagename = codeData.get("destination", {}).get("preferedSitelink", {}).get("title") + if languagename is not None: + res["languagename"] = languagename + twnstats = get_twn_stats(code) - groups_to_find = ["core-0-mostused", "core", "ext-proofreadpage-user", "ext-collection-user", "wikimedia-main", "out-wikimedia-mobile-wikipedia-android-strings", "out-wikimedia-mobile-wikipedia-ios"] - + + groups_to_find = ( + "core-0-mostused", + "core", + "ext-proofreadpage-user", + "ext-collection-user", + "wikimedia-main", + "out-wikimedia-mobile-wikipedia-android-strings", + "out-wikimedia-mobile-wikipedia-ios" + ) + if twnstats: res["twn-enabled"] = True res["groupstats"] = get_group_stats(twnstats, groups_to_find) - + if get_mediawiki_languages(code): res["mw-enabled"] = True res["wd-enabled"] = True else: res["wd-enabled"] = get_wikidata_languages(code) - + wmsites = get_wikimedia_sites(sitecode) - if wmsites: - res["wm-sites"].update(wmsites) - - return res -def encapsulate_p(x): - if x[0] == "<": - return x - else: - return "

" + x + "

" - -def statblurb(code, stats, group, displaystats=True): - groupnames = { - "core-0-mostused": "Most important messages", - "core": "MediaWiki core messages", - "ext-collection-user": "Collection extension", - "ext-proofreadpage-user": "ProofreadPage extension", - "wikimedia-main": "Main extensions used by Wikimedia", - "out-wikimedia-mobile-wikipedia-android-strings": "Android Wikipedia app", - "out-wikimedia-mobile-wikipedia-ios": "iOS Wikipedia app" - } - svg = """ - - - - - - - """.format(stats["percentage"], "stat-" + group) - locstats = "" - if displaystats: - locstats = "{0} {6}: {3} %: {4} / {5} messages" - else: - locstats = "{0} {6}" - - return locstats.format( - svg, - code, - group, - stats["percentage"], - stats["translated"], - stats["total"], - groupnames[group] - ) + if wmsites is not None: + res["wm-sites"].update(wmsites) -def build_content(langcode): - results = get_everything(langcode) - content = [] - infobutton = "" - infobox = "" - svg = """ - - - - - - - """ - - locstats = "{0} {6}" - - if len(langcode) == 0: - return "" - if not results["wellformed"]: - content.append("❌ {} is not a well-formed language code.".format(langcode) + infobutton.format("wellformed")) - content.append(infobox.format("wellformed", "A well-formed language code conists of 2 letters (ISO 639-1) or three letters (ISO 639-3), and optionally has one or more subtags separated by hyphens. The code you entered does not conform to this.")) - return Markup("\n".join(content)) - else: - verifybox = "
\nVerify this language code:\n\n
\n" - content.append(verifybox) - content.append("✔️ {} is a well-formed language code.".format(langcode) + infobutton.format("wellformed")) - content.append(infobox.format("wellformed", "A well-formed language code consists of 2 letters (ISO 639-1) or three letters (ISO 639-3), and optionally has one or more subtags separated by hyphens. The code you entered conforms to this, but that doesn't necessarily mean that it is a valid language code.")) - if results["actual"]: - content.append("✔️ This seems to be a code for an actual language.") - content.insert(0, "
{0}: {1}
".format(results["lang"], results["languagename"])) - else: - content.append("❌ This doesn't seem to be an actual language code." + infobutton.format("actual")) - content.append(infobox.format("actual", "This means that there is currently no item on Wikidata with this language code. Since there could be shortcomings in the data on Wikidata, please verify the language code with the SIL link on the right.")) - if results["twn-enabled"]: - content.append("✔️ This language code is enabled on Translatewiki.".format(langcode)) - statbox = "
\n" - for group in results["groupstats"]: - statbox += statblurb(results["code"], results["groupstats"][group], group) + "
\n" - statbox += "
" - content.append(statbox) - else: - content.append("❌ This language code is not enabled on Translatewiki." + infobutton.format("twnenabled")) - content.append(infobox.format("twnenabled", "In order to enable a language for translation on Translatewiki, you need to request that it be added on the Support page. Translatewiki is independent of Wikimedia wikis, so you may need to register an account there first.

Please only request a language if you know it well and intend to translate into it.")) - if results["mw-enabled"]: - content.append("✔️ This language code is available for general use in MediaWiki.") - else: - content.append("❌ This language is not yet available for general use in MediaWiki." + infobutton.format("mwenabled")) - content.append(infobox.format("mwenabled", "The best way to make a language code available for general use in MediaWiki is to complete the translation of the most important messages into that language.".format(results["code"]))) - if results["wd-enabled"]: - content.append("✔️ This language is available for general use on Wikidata.") - else: - content.append("❌ This language is not yet available for general use on Wikidata." + infobutton.format("wdenabled")) - content.append(infobox.format("wdenabled", "The best way to enable a language code for Wikidata is the same as for MediaWiki: Translate the most important messages into that language. However, if you are not able to do that for some reason, but the language code is still needed in Wikidata, you can see the appropriate steps to take on Help:Monolingual text languages.".format(results["code"]))) - - tablestart = "\n\n\n\n\n\n\n\n" - tablerows = [] - tablerow = "\n\n\n" - incubator = "https://incubator.wikimedia.org/wiki/" - projectmatrix = { - "wiki": ["Wikipedia", "https://{}.wikipedia.org/", incubator + "Wp/{}"], - "wiktionary": ["Wiktionary", "https://{}.wiktionary.org/", incubator + "Wt/{}"], - "wikibooks": ["Wikibooks", "https://{}.wikibooks.org/", incubator + "Wb/{}"], - "wikiquote": ["Wikiquote", "https://{}.wikiquote.org/", incubator + "Wq/{}"], - "wikisource": ["Wikisource", "https://{}.wikisource.org/", "https://wikisource.org/wiki/Main_Page/{}"], - "wikiversity": ["Wikiversity", "https://{}.wikiversity.org/", "https://beta.wikiversity.org/wiki/"], - "wikivoyage": ["Wikivoyage", "https://{}.wikivoyage.org/", incubator + "Wy/{}"] - } - - for project in results["wm-sites"]: - col2 = "" - if len(results["groupstats"]) == 0: - col2 = "The language is not enabled in Translatewiki." - else: - col2 = statblurb(results["code"], results["groupstats"]["core-0-mostused"], "core-0-mostused", False) - col2 += statblurb(results["code"], results["groupstats"]["core"], "core", False) - if project == "wikibooks" or project == "wikisource": - col2 += statblurb(results["code"], results["groupstats"]["ext-collection-user"], "ext-collection-user", False) - if project == "wikisource": - col2 += statblurb(results["code"], results["groupstats"]["ext-proofreadpage-user"], "ext-proofreadpage-user", False) - if results["wm-sites"][project]: - tablerows.append(tablerow.format(project, "" + results["wm-sites"][project] + "", col2)) - else: - tablerows.append(tablerow.format(project, projectmatrix[project][0] + " (Incubator)", col2)) - tableend = "
ProjectLocalization status
{}{}
\n" - - content.append(tablestart + "".join(tablerows) + tableend) - content = map(encapsulate_p, content) - return Markup("\n".join(content)) + return res @app.route("/", methods=["GET", "POST"], defaults={"path": ""}) @app.route("/", methods=["GET", "POST"]) def index(path): - if 'langcode' in request.args: - return redirect(url_for('index', path=request.args['langcode'])) - path = path[:20] - return render_template("index.html", langcode=path, content=build_content(path)) + langcode = request.args.get('langcode') + if langcode is not None: + return redirect(url_for('index', path=langcode)) + langcode = path[:20] + results = {} + if len(langcode) != 0: + results = get_everything(langcode) + return render_template("index.html", langcode=langcode, results=results) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..20d96be --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,11 @@ +[project] +name = "codelookup" +version = "0.1.0" +description = "My super duper codelookup replacement" +readme = "README.md" +license = "GPL-3.0-or-later" +requires-python = ">=3.13" +dependencies = [ + "flask>=3.1.3", + "requests>=2.34.2", +] diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 199ae3d..0000000 --- a/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -flask >= 2.0.2 -requests \ No newline at end of file diff --git a/templates/_helpers.html b/templates/_helpers.html new file mode 100644 index 0000000..90670dd --- /dev/null +++ b/templates/_helpers.html @@ -0,0 +1,156 @@ +{% macro infobutton(target) -%} + +{%- endmacro %} + +{% macro infobox(x) -%} + +{%- endmacro %} + +{% macro progressRing(percentage, statGroup) -%} + + + + + + + + +{%- endmacro %} + +{% macro statblurb(code, stats, group, displaystats=True) -%} + {% set groupnames = { + 'core-0-mostused': 'Most important messages', + 'core': 'MediaWiki core messages', + 'ext-collection-user': 'Collection extension', + 'ext-proofreadpage-user': 'ProofreadPage extension', + 'wikimedia-main': 'Main extensions used by Wikimedia', + 'out-wikimedia-mobile-wikipedia-android-strings': 'Android Wikipedia app', + 'out-wikimedia-mobile-wikipedia-ios': 'iOS Wikipedia app' + } %} + + {% set title -%} + {{ stats['percentage'] }} %: {{ stats['translated'] }} / {{ stats['total'] }} messages + {%- endset %} + + + {{ progressRing(stats['percentage'], 'stat-' + group) }} {{ groupnames[group] }}{% if displaystats %}: {{ title }}{% endif %} + +{%- endmacro %} + +{% macro buildContent(langcode, results) -%} + {% if not results['wellformed'] -%} +

{{ langcode }} is not a well-formed language code. {{ infobutton('wellformed') }}

+ {% call infobox('wellformed') -%} + A well-formed language code conists of 2 letters (ISO 639-1) or three letters (ISO 639-3), and optionally has one or more subtags separated by hyphens. The code you entered does not conform to this. + {%- endcall %} + {%- else -%} + {% if results['actual'] -%} +
{{ results['lang'] }}: {{ results['languagename'] }}
+ {%- endif %} +
+ Verify this language code: + +
+

{{ langcode }} is a well-formed language code. {{ infobutton('wellformed') }}

+ {% call infobox('wellformed') -%} + A well-formed language code consists of 2 letters (ISO 639-1) or three letters (ISO 639-3), and optionally has one or more subtags separated by hyphens. The code you entered conforms to this, but that doesn't necessarily mean that it is a valid language code. + {%- endcall %} + {% if results['actual'] -%} +

✔ This seems to be a code for an actual language.

+ {%- else -%} +

✖ This doesn't seem to be an actual language code. {{ infobutton('actual') }}

+ {% call infobox('actual') -%} + This means that there is currently no item on Wikidata with this language code. Since there could be shortcomings in the data on Wikidata, please verify the language code with the SIL link on the right. + {%- endcall %} + {%- endif %} + {% if results['twn-enabled'] -%} +

✔ This language code is enabled on Translatewiki.

+
+ {% for group in results['groupstats'] -%} + {{ statblurb(results['code'], results['groupstats'][group], group) }}
+ {%- endfor %} +
+ {%- else -%} +

✖ This language code is not enabled on Translatewiki. {{ infobutton('twnenabled') }}

+ {% call infobox('twnenabled') -%} + In order to enable a language for translation on Translatewiki, you need to request that it be added on the Support page. Translatewiki is independent of Wikimedia wikis, so you may need to register an account there first.

Please only request a language if you know it well and intend to translate into it. + {%- endcall %} + {%- endif %} + {% if results['mw-enabled'] -%} +

✔ This language code is available for general use in MediaWiki.

+ {%- else -%} +

✖ This language is not yet available for general use in MediaWiki. {{ infobutton('mwenabled') }}

+ {% call infobox('mwenabled') -%} + The best way to make a language code available for general use in MediaWiki is to complete the translation of the most important messages into that language. + {%- endcall %} + {%- endif %} + {% if results['wd-enabled'] -%} +

✔ This language is available for general use on Wikidata.

+ {%- else -%} +

✖ This language is not yet available for general use on Wikidata. {{ infobutton('wdenabled') }}

+ {% call infobox('wdenabled') -%} + The best way to enable a language code for Wikidata is the same as for MediaWiki: Translate the most important messages into that language. However, if you are not able to do that for some reason, but the language code is still needed in Wikidata, you can see the appropriate steps to take on Help:Monolingual text languages. + {%- endcall %} + {%- endif %} + {{ buildContentTable(results) }} + {% endif %} +{%- endmacro %} + +{% macro buildContentTable(results) -%} + {% set incubator = 'https://incubator.wikimedia.org/wiki/' %} + {% set projectmatrix = { + 'wiki': ('Wikipedia', 'https://{}.wikipedia.org/', incubator + 'Wp/{}'), + 'wiktionary': ('Wiktionary', 'https://{}.wiktionary.org/', incubator + 'Wt/{}'), + 'wikibooks': ('Wikibooks', 'https://{}.wikibooks.org/', incubator + 'Wb/{}'), + 'wikiquote': ('Wikiquote', 'https://{}.wikiquote.org/', incubator + 'Wq/{}'), + 'wikisource': ('Wikisource', 'https://{}.wikisource.org/', 'https://wikisource.org/wiki/Main_Page/{}'), + 'wikiversity': ('Wikiversity', 'https://{}.wikiversity.org/', 'https://beta.wikiversity.org/wiki/'), + 'wikivoyage': ('Wikivoyage', 'https://{}.wikivoyage.org/', incubator + 'Wy/{}') + } %} + + + + + + + + + {% for project, site in results['wm-sites'].items() -%} + + + + + {%- endfor %} + +
ProjectLocalization status
+ {%- if site -%} + {{ site }} + {%- else -%} + {{ projectmatrix[project][0] }} (Incubator) + {%- endif -%} + + {%- if results['groupstats'] | length == 0 -%} + The language is not enabled in Translatewiki. + {%- else -%} + {{ statblurb(results['code'], results['groupstats']['core-0-mostused'], 'core-0-mostused', False) }} + {{ statblurb(results['code'], results['groupstats']['core'], 'core', False) }} + {% if project in ('wikibooks', 'wikisource') -%} + {{ statblurb(results['code'], results['groupstats']['ext-collection-user'], 'ext-collection-user', False) }} + {%- elif project == 'wikisource' -%} + {{ statblurb(results['code'], results['groupstats']['ext-proofreadpage-user'], 'ext-proofreadpage-user', False) }} + {%- endif %} + {%- endif -%} +
+{%- endmacro %} diff --git a/templates/index.html b/templates/index.html index 11b5fd2..0e76483 100644 --- a/templates/index.html +++ b/templates/index.html @@ -14,10 +14,10 @@ form.addEventListener( 'submit', function( e ) { e.preventDefault(); if ( newlangcode.value ) { - window.location.href = "http://codelookup.toolforge.org/" + newlangcode.value; + window.location.href = window.location.origin + '/' + newlangcode.value; } }); - + let infobuttons = document.querySelectorAll( '.infobutton' ); infobuttons.forEach( function( infobutton ) { infobutton.addEventListener( 'click', function( e ) { @@ -26,7 +26,7 @@ targetElem.classList.toggle( 'open' ); }); }); - + document.querySelectorAll( 'svg.progress-ring' ).forEach( function( item ) { const circle = item.querySelector( '.progress-ring__circle' ); const radius = circle.r.baseVal.value; @@ -56,16 +56,19 @@ {% block langcode %}
{% endblock langcode %} - {% if content %} -
{{ content }}
- {% endif %} + {% if results -%} + {%- from '_helpers.html' import buildContent with context -%} +
+ {{ buildContent(langcode, results) }} +
+ {%- endif %} - \ No newline at end of file +