diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..ea2c41b --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,42 @@ +name: Publish Docker Image + +on: + push: + branches: + - image_api + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} + +jobs: + build-and-push-image: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Log in to the Container registry + uses: docker/login-action@v1 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@v3 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + + - name: Build and push Docker image + uses: docker/build-push-action@v2 + with: + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + context: . diff --git a/lib/charts.js b/lib/charts.js index 367fc7d..83fb63a 100644 --- a/lib/charts.js +++ b/lib/charts.js @@ -1,7 +1,7 @@ const canvas = require('canvas'); const deepmerge = require('deepmerge'); const pattern = require('patternomaly'); -const { CanvasRenderService } = require('chartjs-node-canvas'); +const { ChartJSNodeCanvas } = require('chartjs-node-canvas'); const { fixNodeVmObject } = require('./util'); const { logger } = require('../logging'); @@ -48,7 +48,9 @@ async function getRenderer(width, height, version, format) { const key = `${width}__${height}__${version}__${format}`; if (!rendererCache[key]) { const Chart = await getChartJsForVersion(version); - rendererCache[key] = new CanvasRenderService(width, height, undefined, format, () => Chart); + var chartjsnodecanvas = new ChartJSNodeCanvas({ width, height, type: format }); + chartjsnodecanvas._chartJs = Chart; + rendererCache[key] = chartjsnodecanvas; } return rendererCache[key]; } @@ -422,15 +424,15 @@ async function renderChartJs( }); } - const canvasRenderService = await getRenderer(width, height, version, format); + const ChartJSNodeCanvas = await getRenderer(width, height, version, format); if (format === 'svg') { // SVG rendering doesn't work asychronously. return Buffer.from( - uniqueSvg(canvasRenderService.renderToBufferSync(chart, 'image/svg+xml').toString()), + uniqueSvg(ChartJSNodeCanvas.renderToBufferSync(chart, 'image/svg+xml').toString()), ); } - return canvasRenderService.renderToBuffer(chart); + return ChartJSNodeCanvas.renderToBuffer(chart); } module.exports = { diff --git a/package.json b/package.json index 8dea377..de650a8 100644 --- a/package.json +++ b/package.json @@ -15,14 +15,14 @@ "test:watch": "PORT=2998 NODE_ENV=test chokidar '**/*.js' --initial --ignore node_modules -c 'mocha --exit --recursive test/'" }, "overrides": { - "canvas": "2.9.3" + "canvas": "2.11.2" }, "resolutions": { - "canvas": "2.9.3" + "canvas": "2.11.2" }, "dependencies": { "bunyan": "^1.8.12", - "canvas": "2.9.3", + "canvas": "2.11.2", "canvas-5-polyfill": "^0.1.5", "chart.js": "^2.9.4", "chart.js-v3": "npm:chart.js@3.9.1", @@ -30,7 +30,7 @@ "chartjs-adapter-moment": "https://github.com/typpo/chartjs-adapter-moment.git#e9bc92ab6e0e500c91c4a9871db7b14d15b5c2e7", "chartjs-chart-box-and-violin-plot": "^2.4.0", "chartjs-chart-radial-gauge": "^1.0.3", - "chartjs-node-canvas": "^3.0.6", + "chartjs-node-canvas": "^4.1.3", "chartjs-plugin-annotation": "^0.5.7", "chartjs-plugin-colorschemes": "https://github.com/typpo/chartjs-plugin-colorschemes.git#979ef8e599265f65c85d5dae90b543d5589c734a", "chartjs-plugin-datalabels": "^0.5.0", @@ -68,4 +68,4 @@ "peerDependencies": { "chart.js": ">= 2.0.0" } -} +} \ No newline at end of file diff --git a/python_exemple.py b/python_exemple.py new file mode 100644 index 0000000..fc11bda --- /dev/null +++ b/python_exemple.py @@ -0,0 +1,79 @@ +import base64 +from io import BytesIO +from random import randint + +import requests +from PIL import Image + +from quickchart import QuickChart, QuickChartFunction + +qc = QuickChart() +qc.host = "ENTER HOST HERE" +qc.version = "3" + + +def imageURLtoBase64(url): + r = requests.get(url) + image = Image.open(BytesIO(r.content)) + buffered = BytesIO() + image.save(buffered, format=image.format) + img_base64 = base64.b64encode(buffered.getvalue()).decode("utf-8") + return f"data:image/{image.format.lower()};base64,{img_base64}" + + +labels = ["Red Vans", "Blue Vans", "Green Vans", "Gray Vans"] +images = [ + "https://i.sstatic.net/2RAv2.png", + "https://i.sstatic.net/Tq5DA.png", + "https://i.sstatic.net/3KRtW.png", + "https://i.sstatic.net/iLyVi.png", +] +# chart.js can't load image urls, so we need to convert them to base64 +images = [imageURLtoBase64(url) for url in images] +values = [randint(1, 50) for _ in range(len(labels))] + +qc.config = { + "type": "bar", + "plugins": [ + { + "id": "custom-labels", + "afterDraw": QuickChartFunction( + f""" + (chart, args, options) => {{ + const {{ctx}} = chart; + //console.log(chart.scales); + var xAxis = chart.scales['xAxes']; + var yAxis = chart.scales['yAxes']; + var images = {images}; + xAxis.ticks.forEach((value, index) => {{ + var x = xAxis.getPixelForTick(index); + var img = new Image(); + img.onload = function() {{ ctx.drawImage(img, x - 12, yAxis.bottom + 10, 24, 24); }}; + img.src = images[index]; + }}); + }} + """ + ), + } + ], + "data": { + "labels": labels, + "datasets": [ + { + "label": "My Dataset", + "data": values, + "backgroundColor": ["red", "blue", "green", "lightgray"], + } + ], + }, + "options": { + "plugins": {"legend": {"display": False}}, + "scales": { + "yAxes": {"beginAtZero": True}, + "xAxes": {"ticks": {"padding": 30}}, + }, + }, +} + +qc.to_file("/tmp/chart.png") +# print(qc.get_url()) diff --git a/yarn.lock b/yarn.lock index c6a9734..a509b55 100644 --- a/yarn.lock +++ b/yarn.lock @@ -990,13 +990,13 @@ canvas-5-polyfill@^0.1.5: resolved "https://registry.yarnpkg.com/canvas-5-polyfill/-/canvas-5-polyfill-0.1.5.tgz#2bc0a2c6b6da5fcd7f42aebc9566a1354c78ea14" integrity sha1-K8CixrbaX81/Qq68lWahNUx46hQ= -canvas@2.9.3, canvas@^2.6.0, canvas@^2.6.1: - version "2.9.3" - resolved "https://registry.yarnpkg.com/canvas/-/canvas-2.9.3.tgz#8723c4f970442d4cdcedba5221579f9660a58bdb" - integrity sha512-WOUM7ghii5TV2rbhaZkh1youv/vW1/Canev6Yx6BG2W+1S07w8jKZqKkPnbiPpQEDsnJdN8ouDd7OvQEGXDcUw== +canvas@2.11.2, canvas@^2.6.0, canvas@^2.8.0: + version "2.11.2" + resolved "https://registry.yarnpkg.com/canvas/-/canvas-2.11.2.tgz#553d87b1e0228c7ac0fc72887c3adbac4abbd860" + integrity sha512-ItanGBMrmRV7Py2Z+Xhs7cT+FNt5K0vPL4p9EZ/UX/Mu7hFbkxSjKF2KVtPwX7UYWp7dRKnrTvReflgrItJbdw== dependencies: "@mapbox/node-pre-gyp" "^1.0.0" - nan "^2.15.0" + nan "^2.17.0" simple-get "^3.0.3" capture-stack-trace@^1.0.0: @@ -1089,13 +1089,13 @@ chartjs-color@^2.1.0: chartjs-color-string "^0.6.0" color-convert "^1.9.3" -chartjs-node-canvas@^3.0.6: - version "3.2.0" - resolved "https://registry.yarnpkg.com/chartjs-node-canvas/-/chartjs-node-canvas-3.2.0.tgz#876deff999e3be1a3c182b30c9bafd9dfdd00901" - integrity sha512-MT0K28VG8BXwCdh5BdRXNw4nzJWKzcN3t/xgTr8zJ8M6uOXl7hRdtIW8rEUcylkLED8LGsnJmSkcnqlhPy841g== +chartjs-node-canvas@^4.1.3: + version "4.1.6" + resolved "https://registry.yarnpkg.com/chartjs-node-canvas/-/chartjs-node-canvas-4.1.6.tgz#034b0428a8accfa4963dd681497f00f900fefc81" + integrity sha512-UQJbPWrvqB/FoLclGA9BaLQmZbzSYlujF4w8NZd6Xzb+sqgACBb2owDX6m7ifCXLjUW5Nz0Qx0qqrTtQkkSoYw== dependencies: - canvas "^2.6.1" - tslib "^1.14.1" + canvas "^2.8.0" + tslib "^2.3.1" chartjs-plugin-annotation@^0.5.7: version "0.5.7" @@ -3930,11 +3930,16 @@ mv@~2: ncp "~2.0.0" rimraf "~2.4.0" -nan@^2.14.0, nan@^2.15.0: +nan@^2.14.0: version "2.17.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.17.0.tgz#c0150a2368a182f033e9aa5195ec76ea41a199cb" integrity sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ== +nan@^2.17.0: + version "2.20.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.20.0.tgz#08c5ea813dd54ed16e5bd6505bf42af4f7838ca3" + integrity sha512-bk3gXBZDGILuuo/6sKtr0DQmSThYHLtNCdSdXk9YkxD/jK6X2vmCyyXBBxyqZ4XcnzTyYEAThfX3DCEnLf6igw== + nanotimer@0.3.14: version "0.3.14" resolved "https://registry.yarnpkg.com/nanotimer/-/nanotimer-0.3.14.tgz#10d811f8d064788180096ce1f96c70846fd5a2ba" @@ -5513,11 +5518,6 @@ tsconfig-paths@^3.14.1: minimist "^1.2.6" strip-bom "^3.0.0" -tslib@^1.14.1: - version "1.14.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" - integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== - tslib@^1.9.0: version "1.13.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043" @@ -5528,6 +5528,11 @@ tslib@^2.2.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== +tslib@^2.3.1: + version "2.6.3" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.3.tgz#0438f810ad7a9edcde7a241c3d80db693c8cbfe0" + integrity sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ== + tunnel-agent@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd"