GithubHelp home page GithubHelp logo

ptarmiganlabs / butler Goto Github PK

View Code? Open in Web Editor NEW
54.0 8.0 8.0 13.07 MB

Butler brings superpowers to Qlik Sense Enterprise on Windows! Advanced reload failure alerts, task scheduler, key-value store, file system access and much more.

Home Page: https://butler.ptarmiganlabs.com

License: MIT License

JavaScript 96.93% Handlebars 3.00% Dockerfile 0.08%
butler qliksense slack mqtt sensei senseops qlik-sense influxdb newrelic

butler's People

Contributors

dependabot[bot] avatar github-actions[bot] avatar mountaindude avatar renovate-bot avatar renovate[bot] avatar snyk-bot avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

butler's Issues

Add partial reload flag to Butler API

Is your feature request related to a problem? Please describe.
It's currently possible to start reload tasks via Butler's REST API
It would be useful to have a flag in the API through which partial/full reloads could be controlled.

Describe the solution you'd like
Add an optional flag in the /v4/reloadtask/{taskId}/start endpoint

If not included in the call the old behavior (full reload) should be started..

Memory leak in Butler when starting Sense tasks

Describe the bug
There is a slight memory leak that occurred when Sense tasks were started using Butler's API endpoint for this purpose.
Nothing major but still something that should be addressed.
https://github.com/ptarmiganlabs/butler/blob/master/src/qrsUtil/senseStartTask.js

The issue occurs when the options object is mutated and then passed to the Sense APIs over https.
Also, this code does not use the config data already loaded into the config object, but rather loads the data again. Inefficient.

Suggested solution:
Refactor the code, possibly using QRS Interact module instead of https calls. Would give better CORS handling among other things.

Update scripts with lib paths

:) sense scripts included point to http:// but should rather use lib paths or variables to be defined in Sense script

Change v2 API endpoints so they follow established standards for which http methods are used when

Describe the bug
Not as much a bug as a poor initial design design.

When Butler was first created, it was done to solve some immediate challenges, and there wasn't a lot of consideration put towards following established standards... "Solve the problem" was the motto.
As a result its API is as of v2 all GET based, even where PUT or POST should be used.

An example: the /v4/mqttpublishmessage can be used to send MQTT messages. It uses a GET method and all parameters as URL query parameters ( /v4/mqttpublishmessage?topic=...&message=...)
A more correct way is to place the payload in the body of the http request and use a PUT method.

Suggested change
Revise all endpoints and make them follow http standards to the degree possible.

Breaking change
This will for sure be a breaking change for many endpoints.
For that reason the API version number should also be raised to come into parity with Butler's own version number.
While Butler could be made to handle several API versions in parallel, that would significantly increase code/app size. Questionable if that's meaningful at this point.

Store script logs for failed reloads on disk

Assuming Butler can send notification emails (with script log extracts in them) when scheduled reloads fail in QSEoW, it would also be possible to save the full script logs to disk.

A new directory could be created per day, in this directory all reload scripts for failed reloads will be stored.
This would make post-mortem debugging easier, as it's otherwise quite time consuming to find a particular script log in among the many, many archived script logs in a large Sense cluster.

Add uptime monitor

The sibling project Butler SOS has a good uptime monitor which tells how long the service has been running, how much memory it uses etc. It also stores this data to InfluxDB for later charting/analysis.

Add similar feature to Butler.

upgrade to butler v3.1.0 error rest api qlik sense

Hi,

We have used butler for a bit more then a year to sent slack messages from our qlik sense server. We installed it on our local qlik sense server.

We installed a new server and upgraded our qlik environment to april 2020 and I thought I will upgrade butler as well to the latest version. I followed the configuration steps and it is running when I start node butler.js from an elevated prompt.

But now I have the following issue.
I created a rest api connector in Qlik to use this to send messages to slack. But when I create the rest api connector with the following settings, copied from our previous server, it gives this error:
failed to connect to server. Please check connection parameters. see screenshots for the settings I have used

I don't have a lot of experience with this but the previous version worked great so hopefully somebody can help to give some guidance to fix this.

Kind Regards,

Hvdbunte

Describe environment:

  • Windows Server 2019
  • Containerisation no
  • Version of Butler used: v 3.1.0
  • Command used to start Butler: Node butler.js

Config file(s)
What's the content of the config file(s) you use?


Butler:
Logging configuration
logLevel: info # Log level. Possible log levels are silly, debug, verbose, info, warn, error
fileLogging: false # true/false to enable/disable logging to disk file
logDirectory: log # Subdirectory where log files are stored (no trailing / )

Heartbeats can be used to send "I'm alive" messages to any other tool, e.g. a infrastructure monitoring tool
heartbeat:
enabled: false
remoteURL: http://my.monitoring.server/some/path/
frequency: every 1 hour # https://bunkat.github.io/later/parsers.html

slackConfig:
enable: true
webhookURL: https://hooks.slack.com/services/my_webhook
loginNotificationChannel: team-bi-monitoring
taskFailureChannel: team-bi-monitoring

mqttConfig:
enable: false
brokerHost:
brokerPort: 1883
taskFailureTopic: qliksense/task_failure
taskFailureServerStatusTopic: qliksense/butler/task_failure_server
sessionStartTopic: qliksense/session/start
sessionStopTopic: qliksense/session/stop
connectionOpenTopic: qliksense/connection/open
connectionCloseTopic: qliksense/connection/close
sessionServerStatusTopic: qliksense/butler/session_server
activeUserCountTopic: qliksense/users/active/count
activeUsersTopic: qliksense/users/active/usernames

udpServerConfig:
enable: true
serverHost: qliksense-p
portSessionConnectionEvents: 9997
portTaskFailure: 9998

restServerConfig:
enable: true
serverHost: qliksense-p
serverPort: 8080

Enable/disable individual REST API endpoints. Set config item below to true to enable that endpoint.
restServerEndpointsEnable:
activeUserCount: true
activeUsers: true
slackPostMessage: true
createDir: true
createDirQVD: true
mqttPublishMessage: true
senseStartTask: true
senseAppDump: true
senseListApps: true
butlerping: true
base62ToBase16: true
base16ToBase62: true

Certificates to use when connecting to Sense. Get these from the Certificate Export in QMC.
cert:
clientCert: C:\ProgramData\Qlik\Sense\Repository\Exported Certificates.Local Certificates\client.pem
clientCertKey: C:\ProgramData\Qlik\Sense\Repository\Exported Certificates.Local Certificates\client_key.pem
clientCertCA: C:\ProgramData\Qlik\Sense\Repository\Exported Certificates.Local Certificates\root.pem
# If running Butler in a Docker container, the cert paths MUST be the following
# clientCert: /nodeapp/config/certificate/client.pem
# clientCertKey: /nodeapp/config/certificate/client_key.pem
# clientCertCA: /nodeapp/config/certificate/root.pem

configEngine:
engineVersion: 12.170.2 # Qlik Associative Engine version to use with Enigma.js. Ver 12.170.2 works with Feb 2019
host: qliksense-p
port: 4747
useSSL: true
headers:
X-Qlik-User: UserDirectory=Internal;UserId=sa_repository
rejectUnauthorized: false

configQRS:
authentication: certificates
host: qliksense-p
useSSL: true
port: 4242
headerKey: X-Qlik-User
headerValue: UserDirectory=Internal; UserId=sa_repository

configDirectories:
# enableDirectoryCreation: false
qvdPath:

gitHub:
host: api.github.com
pathPrefix: ''

image
image
image

Heartbeats sent to https endpoints fail

Describe the bug
If heartbeats are enabled and sent to a https endpoint, the heartbeat call will fail with a "...unable to verify the first certificate" error.

Expected behavior
It should be possible to send heartbeats to either http or https endpoints.

Describe environment:
Bug verified in Node.js 14.5 on mac OS Big Sur, Docker and in Kubernetes.

Add more descriptive error messages

Is your feature request related to a problem? Please describe.
As of 4.2.1 there is decent error logging coverage, but it lacks somewhat in what details are reported about errors that occur.
A bit more context when errors occur => easier debugging and better bug reports.

Review and streamline YAML config file

Some entries are not very consistent, for example

Butler.udpServerConfig.serverIP
Butler.configEngine.server
Butler.configQRS.host
Butler.gitHub.host

Use consistent naming policy, e.g.

Butler.udpServerConfig.host
Butler.configEngine.host
Butler.configQRS.host
Butler.gitHub.host

Output basic Sense info to logs on Butler startup

It would be nice to get some basic info about the underlying Qlik Sense environment written to the Butler logs, whenever Butler is started.
Things such as overall Sense version, QIX engine version, info about the hardware Sense is running on etc.

Add basic scheduler features

Given the limitations of QSEoW's built-in scheduler (ex not possible to limit jobs to certain times of the day), it would be useful to have a reliable external scheduler that knows how to talk to QSEoW.

The Butler already knows how to start Sense tasks, so if a scheduler is added it would be a nice complement to Sense's standard scheduler.

Feature: Send Task failed mail to app owner

Is your feature request related to a problem? Please describe.
Featurerequest. Bigger Qlik Sense environments have separated admins and Developer, therefore most of the task failed notification mails can only be forwarded to the Developer.

Describe the solution you'd like
Butler 4.2 has an amazing flexibility for failed Task notification.What we miss: the right Person (App Owner=App Developer) needs this information, not only the Qlik Sense admin.

Describe alternatives you've considered
Hope for new Qlik Sense Upgrade with this feature...

Additional context
Qlik Sense with AD Connected have this information synced.
https://help.qlik.com/en-US/sense-developer/September2020/Subsystems/ProxyServiceAPI/Content/Sense_ProxyServiceAPI/ProxyServiceAPI-Session-Module-API-User-Get.htm
image

Error on NPM Install

Following the instructions at https://mountaindude.github.io/butler/install-config/#installation, I installed the latest version of node.js, downloaded and extracted to c:\node\butler and ran the npm install which worked for a bit and then threw errors. My buffer was too small to capture the entire output, but I've incldued what I was able to grab below. Any suggestions?
...
| | | -- [email protected] | | -- [email protected]
| +-- [email protected]
| | +-- [email protected]
| | -- [email protected] | -- [email protected]
| +-- [email protected]
| -- [email protected] +-- [email protected] +-- [email protected] | -- [email protected]
+-- [email protected]
+-- [email protected]
| +-- [email protected]
| +-- [email protected]
| -- [email protected] | +-- [email protected] | | -- [email protected]
| +-- [email protected]
| +-- [email protected]
| +-- [email protected]
| -- [email protected] +-- [email protected] | -- [email protected]
+-- [email protected]
+-- [email protected]
+-- [email protected]
| +-- [email protected]
| +-- [email protected]
| +-- [email protected]
| +-- [email protected]
| +-- [email protected]
| -- [email protected] +-- [email protected] | +-- [email protected] | +-- [email protected] | +-- [email protected] | | -- [email protected]
| | -- [email protected] | | -- [email protected]
| -- [email protected] +-- [email protected] | +-- [email protected] | +-- [email protected] | +-- [email protected] | | +-- [email protected] | | -- [email protected]
| +-- [email protected]
| | -- [email protected] | +-- [email protected] | | -- [email protected]
| +-- [email protected]
| +-- [email protected]
| +-- [email protected]
| +-- [email protected]
| | +-- [email protected]
| | | +-- [email protected]
| | | -- [email protected] | | | -- [email protected]
| | +-- [email protected]
| | +-- [email protected]
| | -- [email protected] | -- [email protected]
| +-- [email protected]
| -- [email protected] +-- [email protected] | -- [email protected]
+-- [email protected]
| -- [email protected] +-- [email protected] | -- [email protected]
+-- [email protected]
| -- [email protected] +-- [email protected] +-- [email protected] | +-- [email protected] | +-- [email protected] | | -- [email protected]
| -- [email protected] +-- [email protected] | +-- [email protected] | | +-- [email protected] | | | -- [email protected]
| | -- [email protected] | +-- [email protected] | | -- [email protected]
| | -- [email protected] | -- [email protected]
| -- [email protected] +-- [email protected] | +-- [email protected] | +-- [email protected] | -- [email protected]
| -- [email protected] +-- [email protected] | +-- [email protected] | -- [email protected]
| -- [email protected] +-- [email protected] +-- [email protected] | -- [email protected]
| -- [email protected] +-- [email protected] +-- [email protected] | -- [email protected]
+-- [email protected]
| +-- [email protected]
| | +-- [email protected]
| | -- [email protected] | -- [email protected]
+-- [email protected]
+-- [email protected]
| +-- [email protected]
| +-- [email protected]
| | +-- [email protected]
| | +-- [email protected]
| | | +-- [email protected]
| | | -- [email protected] | | | +-- [email protected] | | | +-- [email protected] | | | -- [email protected]
| | +-- [email protected]
| | | -- [email protected] | | +-- [email protected] | | | +-- [email protected] | | | -- [email protected]
| | +-- [email protected]
| | +-- [email protected]
| | +-- [email protected]
| | | -- [email protected] | | +-- [email protected] | | | +-- [email protected] | | | | -- [email protected]
| | | +-- [email protected]
| | | | +-- [email protected]
| | | | -- [email protected] | | | +-- [email protected] | | | | -- [email protected]
| | | -- [email protected] | | -- [email protected]
| +-- [email protected]
| | +-- [email protected]
| | +-- [email protected]
| | | -- [email protected] | | -- [email protected]
| +-- [email protected]
| | +-- [email protected]
| | | -- [email protected] | | -- [email protected]
| | -- [email protected] | +-- [email protected] | +-- [email protected] | +-- [email protected] | | +-- [email protected] | | +-- [email protected] | | -- [email protected]
| +-- [email protected]
| | -- [email protected] | | +-- [email protected] | | -- [email protected]
| +-- [email protected]
| +-- [email protected]
| | -- [email protected] | | -- [email protected]
| +-- [email protected]
| | +-- [email protected]
| | +-- [email protected]
| | | -- [email protected] | | | -- [email protected]
| | +-- [email protected]
| | -- [email protected] | +-- [email protected] | | +-- [email protected] | | -- [email protected]
| +-- [email protected]
| +-- [email protected]
| | +-- [email protected]
| | +-- [email protected]
| | -- [email protected] | | -- [email protected]
| | -- [email protected] | +-- [email protected] | -- [email protected]
| +-- [email protected]
| +-- [email protected]
| | -- [email protected] | +-- [email protected] | +-- [email protected] | +-- [email protected] | | -- [email protected]
| +-- [email protected]
| | -- [email protected] | | -- [email protected]
| +-- [email protected]
| | +-- [email protected]
| | -- [email protected] | | -- [email protected]
| -- [email protected] | -- [email protected]
+-- [email protected]
| +-- [email protected]
| | +-- [email protected]
| | -- [email protected] | +-- [email protected] | -- [email protected]
+-- [email protected]
| +-- [email protected]
| | -- [email protected] | +-- [email protected] | | +-- [email protected] | | | -- [email protected]
| | -- [email protected] | +-- [email protected] | +-- [email protected] | +-- [email protected] | | -- [email protected]
| -- [email protected] +-- [email protected] | -- [email protected]
| -- [email protected] +-- [email protected] | -- [email protected]
+-- [email protected]
| +-- [email protected]
| | +-- [email protected]
| | | -- [email protected] | | +-- [email protected] | | | +-- [email protected] | | | -- [email protected]
| | -- [email protected] | +-- [email protected] | +-- [email protected] | +-- [email protected] | | -- [email protected]
| | -- [email protected] | +-- [email protected] | | +-- [email protected] | | -- [email protected]
| -- [email protected] +-- [email protected] | +-- [email protected] | | +-- [email protected] | | +-- [email protected] | | | -- [email protected]
| | +-- [email protected]
| | -- [email protected] | +-- [email protected] | | -- [email protected]
| | -- [email protected] | | +-- [email protected] | | | +-- [email protected] | | | -- [email protected]
| | +-- [email protected]
| | -- [email protected] | | +-- [email protected] | | -- [email protected]
| +-- [email protected]
| | -- [email protected] | +-- [email protected] | | -- [email protected]
| +-- [email protected]
| +-- [email protected]
| | +-- [email protected]
| | -- [email protected] | +-- [email protected] | -- [email protected]
| -- [email protected] -- [email protected]

**npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@^1.0.0 (node_modules\ch
okidar\node_modules\fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@
1.0.17: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"
})
npm ERR! Windows_NT 6.2.9200
npm ERR! argv "C:\Program Files\nodejs\node.exe" "C:\Program Files\nodejs\
node_modules\npm\bin\npm-cli.js" "install"
npm ERR! node v6.9.4
npm ERR! npm v3.10.10
npm ERR! code ELIFECYCLE

npm ERR! [email protected] install: node-gyp rebuild
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the [email protected] install script 'node-gyp rebuild'.
npm ERR! Make sure you have the latest version of node.js and npm installed.
npm ERR! If you do, this is most likely a problem with the ref package,
npm ERR! not with npm itself.
npm ERR! Tell the author that this fails on your system:
npm ERR! node-gyp rebuild
npm ERR! You can get information on how to open an issue for this project with:
npm ERR! npm bugs ref
npm ERR! Or if that isn't available, you can get their info via:
npm ERR! npm owner ls ref
npm ERR! There is likely additional logging output above.

npm ERR! Please include the following file with any support request:
npm ERR! C:\Node\butler\src\npm-debug.log**

C:\Node\butler\src>npm install

Add API endpoint for deleting and moving files

When running Qlik Sense Enterprise on Windows in "standard mode", you have no direct access to the file system from the app scripts. But sometimes you have a real, legitimate need to delete or move for example temporary QVD files that are no longer needed.

Suggestion:
Specify in Butler's main config file in which directories it should be possible to delete files, and between what directories file moves should be allowed. Then check that the file delete/move request coming in via the API really refers to a file in one of those directories.
That way it should be possible to avoid nasty things such as using relative paths, and that way deleting important system files etc.

Error: EISDIR: illegal operation on a directory, read on "node butler.js" execution

Hi,
I have a couple of installations made recently that have the same error:

C:\Program Files\nodejs\butler-master\src>node butler.js
fs.js:675
return binding.read(fd, buffer, offset, length, position);
^

Error: EISDIR: illegal operation on a directory, read
at Object.fs.readSync (fs.js:675:18)
at tryReadSync (fs.js:540:20)
at Object.fs.readFileSync (fs.js:583:19)
at Object. (C:\Program Files\nodejs\butler-master\src\qrsUtil\sen
seStartTask.js:14:13)
at Module._compile (module.js:653:30)
at Object.Module._extensions..js (module.js:664:10)
at Module.load (module.js:566:32)
at tryModuleLoad (module.js:506:12)
at Function.Module._load (module.js:498:3)
at Module.require (module.js:597:17)

Can't really understand what's happening.
Thanks

Make Butler aware of *ALL* failed reloads, including ones triggered by external sources using APIs or during app development

By hooking into the engine logs it's possible to detect any failed reload in the Sense engine.

While it's desirable to detect app reloads also outside of the scheduler context, some form of white listing is probably needed.
Otherwise every failed reload during development will trigger an alert.
Maybe it's possible to while list which user IDs alerts should be sent for... Internal system accounts that are used to run reloads would be included, leaving end user triggered reloads out.

Feature: Keep track of what virtual proxy users are accessing Sense via

Should be possible to keep real-time info on what virtual proxy users are connected via.

This will be useful from an operations perspective, for example when a server restart is needed.
This feature would answer the question how many users that are connected through this particular server.

Add Docker healthcheck

Docker containers are nice, but Docker containers with built-in health checks are even better..
Use the standard health check feature of the Dockerfile.

When Sense servers returns non-ok result, error should be logged in Butler log files

Describe the bug
In current version nothing is logged by Butler when Sens returns a non-204 result code.
Only way to see that an error (e.g. 404 returned from Sense) occurred is to turn on verbose logging. While this works, this kind of scenario should result in a clear feedback to the user calling the Butler.

Expected behavior
Butler should log an error to its own logging system, as well as return an error via the REST API call response

Add heart beat feature

A heart beat every 10 sec would enable active monitoring of whether Butler is up or down.

Consolidate app related API endpoints into single path format

Is your feature request related to a problem? Please describe.
Due to incremental development during past years, the naming of API endpoints is not very consistent.
The APIs still work, but it would of course be better if related API endpoints had similar names...

Describe the solution you'd like
The new app reload endpoint is found at /v4/app/{appId}/reload.

The older list-all-apps and app-dump endpoints are found at /v4/senselistapps and /v4/senseappdump/{appId}.
Keep those two endpoints for backward compatibility, but also add:

/v4/apps/list
/v4/app/{appId}/dump

Add generic key-value store in Butler, expose new endpoints for manipulating it

By having a key-value store in Butler and exposing set/get methods via a REST API, other systems could set values, which could then be accessed by Qlik apps' load scripts.

The same thing can be achieved using external key-value stores (Redis etc), having it in Butler would merely be a convenience.

A related enhancement would be to add some parameters to the start task endpoint, where key-value pairs could be passed as parameters to that endpoint.
The reloading app could then retrieve the key-value pairs during app reload.
A kind of parameter passing to app reloads, thus.

Send failed reload notifications via email

Sending such notifications using Slack or MS Teams is great, but for companies with email driven workflows the IM channels are a complement, not the main notification channel.

Enhance the current, basic setup for email notifications (which use log4net log appenders) with features such as

  • Templating for email subject and body. This makes it possible to dynamically inject info from the failed reload into the email message
  • Rate limiting. Don't spam users with email notifications.
  • Include script logs in notification emails.
  • Flexibility wrt configuration of smtp/email servers.
  • Set email priority flag
  • Multiple recipients
  • Links to QMC/Hub from within the notification email

Feature: Find and use MQTT enabled charting tool

Would be neat if there was a real-time charting tool that would take MQTT messages as input, and plot (nice looking) charts..

Node-RED does this, but the line charts do not currently handle charts covering more than a few thousand data points (at least not in a good way).
Mozaik is looking promising... there might be others too. #

Use Sense notification APIs rather than log appenders

While log appenders will forward reload failure events to Butler just fine, they are somewhat tricky to set up.
It would be a smoother user experience to have Butler use the Sense notification API and that way simply subscribe to the relevant events.

This is what Butler Notifier does, so the scaffolding is already there.

issue with new line in msg to slack

Hi,

Thanks for this add-on for the Slack integration.

I am new to this, so maybe it is an issue but maybe my code is not correct. But when I try to put in a new line in the message by using \n. The \n is just shown as text.

I tried the send.slack command from the node.slack on it's own and then it works.

Can you let me know if it is possible to add a new line in the Slack message or what I can do to fix this?

Thanks

Great work! Can I add extension deployment code

Hey this is really impressive, thanks for sharing this. I have some functionality that is used for deploying Qlik Sense extensions through the API to Qlik Sense Enterprise via push to GIT repository (where multiple branches of the same visualisation will then exist in Qlik Sense for development and testing. Would you mind if I try adding this into butler also?

Multi-arch Docker manifest is not working as intended

Describe the bug
When running in a multi-arch (amd64 and arm64) k8s cluster, containers fail to start on arm64 nodes.

Expected behavior
Containers should automatically start on amd64, arm64 or arm nodes.

Solution idea
The issue might be caused by the variant field in the manifest. It seems that the manifests of other (working) multi-arch images Docker images don't have that variant field. Might be worth removing it as a first attempt at resolving the issue.

Start Sense reload task is broken in 4.2.1

Describe the bug
Not possible to start Sense tasks using the REST API in v4.2.1

Screenshots
Error message is

error: STARTTASK: Failed starting task: fbf645f0-0c92-40a4-af9a-6e3eb1d3c35c,

This is a confirmed bug that will be fixed in next release.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.