feat: auto-generated attribution page

This commit is contained in:
barredterra 2024-01-08 17:51:25 +01:00
parent 3fd26775b3
commit a307ec6c88
2 changed files with 120 additions and 0 deletions

View file

@ -0,0 +1,42 @@
{% extends "templates/web.html" %}
{% block page_content %}
<h1>{{ _("Attribution") }}</h1>
<p>
{{ _("This software is built on top of many open source packages. We would like to thank the authors of these packages for their contribution.") }}
</p>
{% for app, packages in packages_by_app.items() %}
<section id="{{ app }}">
<h2><a href="#{{ app }}">{{ app }}</a></h2>
<table class="table table-striped">
<thead>
<tr>
<td>{{ _("Package") }}</td>
<td>{{ _("Version") }}</td>
<td>{{ _("License") }}</td>
<td>{{ _("Author") }}</td>
</tr>
</thead>
<tbody>
{% for package in packages %}
<tr>
<td>
{% if package.homepage %}
<a href="{{ (package.homepage | e) }}" target="_blank">{{ package.name | e }}</a>
{% else %}
{{ package.name | e }}
{% endif %}
</td>
<td>{{ package.version | e }}</td>
<td>{{ (package.license | e) or _("Unknown") }}</td>
<td>{{ (package.author | e) or _("Unknown") }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</section>
{% endfor %}
{% endblock %}

78
frappe/www/attribution.py Normal file
View file

@ -0,0 +1,78 @@
import json
import re
import tomllib
import requests
import frappe
def get_context(context):
packages_by_app = {}
for app in frappe.get_installed_apps():
packages_by_app[app] = get_app_deps(app)
context.packages_by_app = packages_by_app
def get_app_deps(app: str):
dependencies = []
app_info = get_pyproject_info(app)
for requirement in app_info.get("dependencies", []):
name, version = parse_pip_requirement(requirement)
info = get_py_registry_info(name)
info["name"] = name
info["version"] = version
dependencies.append(info)
for name, version in get_js_deps(app).items():
info = get_js_registry_info(name)
info["name"] = name
info["version"] = version
dependencies.append(info)
return dependencies
def get_js_deps(app: str):
package_json = frappe.get_app_path(app, "..", "package.json")
with open(package_json) as f:
package = json.load(f)
return package.get("dependencies", {})
def get_js_registry_info(package):
registry_url = f"https://registry.npmjs.org/{package}"
registry_info = requests.get(registry_url).json()
return {
"license": registry_info.get("license"),
"author": registry_info.get("author", {}).get("name"),
"homepage": registry_info.get("homepage"),
}
def get_pyproject_info(app: str) -> list[str]:
pyproject_toml = frappe.get_app_path(app, "..", "pyproject.toml")
with open(pyproject_toml, "rb") as f:
pyproject = tomllib.load(f)
return pyproject.get("project", {})
def get_py_registry_info(package):
registry_url = f"https://pypi.org/pypi/{package}/json"
registry_info = requests.get(registry_url).json().get("info", {})
return {
"license": registry_info.get("license"),
"author": registry_info.get("author"),
"homepage": registry_info.get("home_page"),
}
def parse_pip_requirement(requirement: str) -> tuple[str, str]:
"""Parse pip requirement string to package name and version"""
match = re.match(r"^([A-Za-z0-9_\-\[\]]+)(.*)$", requirement)
return (match[1], match[2]) if match else (requirement, "")