Giter VIP home page Giter VIP logo

leanplum-data-export's Introduction

CircleCI

Leanplum Data Export

This repository is the job to export Mozilla data from Leanplum and into BQ.

It's dockerized to run on GKE. To run locally:

pip install .
leanplum-data-export export-leanplum \
  --app-id $LEANPLUM_APP_ID \
  --client-key $LEANPLUM_CLIENT_KEY \
  --date 20190101 \
  --bucket gcs-leanplum-export \
  --table-prefix leanplum \
  --bq-dataset dev_external \
  --prefix dev

Doing it this way will, by default, use your local GCP credentials. GCP only allows you to do this a few times

Alternatively, run in Docker.

First, create a service account with access to GCS and BigQuery. Download a JSON key file and make it available in your environment as GCLOUD_SERVICE_KEY. Then run:

bq mk leanplum
make run COMMAND="leanplum-data-export export-leanplum \
  --app-id $LEANPLUM_APP_ID \
  --client-key $LEANPLUM_CLIENT_KEY \
  --date 20190101 \
  --bucket gcs-leanplum-export \
  --table-prefix leanplum \
  --bq-dataset leanplum \
  --prefix dev"

That will create the dataset in BQ, download the files, and make them available in BQ in that dataset as external tables.

Development and Testing

While iterating on development, we recommend using virtualenv to run the tests locally.

Run tests locally

Install requirements locally:

python3 -m virtualenv venv
source venv/bin/activate
make install-requirements

Run tests locally:

pytest tests/

Run tests in docker

You can run the tests just as CI does by building the container and running the tests.

make clean && make build
make test

Deployment

This project deploys automatically to GCR. The latest release is used to run the job.

leanplum-data-export's People

Contributors

benwu avatar fbertsch avatar haroldwoo avatar whd avatar

Stargazers

 avatar  avatar  avatar  avatar

Watchers

 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

leanplum-data-export's Issues

Set schemas for csv external tabless

Currently the schemas of the external tables are autodetected from the csv. This is causing tasks to fail if the autodetected type does not match the target type. For example, app version should be a string because of values like 60.0.1 but if for a particular date, all values are coercible to a float, e.g. 60.0, then the detected type will be a float.

Setting the schema will allow values like 60.0 to be treated as a string.

Frequent intermittent errors

There are still errors happening frequently, recently it's almost everyday. This is the log for one error but there may be different ones. Airflow logs get purged too quickly so they're hard to catch.

[2020-10-24 04:21:07,743] {logging_mixin.py:112} INFO - [2020-10-24 04:21:07,743] {pod_launcher.py:125} INFO - b'INFO:root:Creating external table tmp.firefox_android_release_external_leanplum_eventparameters_v2_20201023\n'
[2020-10-24 04:21:08,012] {logging_mixin.py:112} INFO - [2020-10-24 04:21:08,012] {pod_launcher.py:125} INFO - b'INFO:root:Creating external table tmp.firefox_android_release_external_leanplum_events_v2_20201023\n'
[2020-10-24 04:22:08,176] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,176] {pod_launcher.py:125} INFO - b'Traceback (most recent call last):\n'
[2020-10-24 04:22:08,176] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,176] {pod_launcher.py:125} INFO - b'  File "/usr/local/lib/python3.9/site-packages/urllib3/connectionpool.py", line 426, in _make_request\n'
[2020-10-24 04:22:08,200] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,200] {pod_launcher.py:125} INFO - b'    six.raise_from(e, None)\n'
[2020-10-24 04:22:08,201] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,201] {pod_launcher.py:125} INFO - b'  File "<string>", line 3, in raise_from\n'
[2020-10-24 04:22:08,202] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,202] {pod_launcher.py:125} INFO - b'  File "/usr/local/lib/python3.9/site-packages/urllib3/connectionpool.py", line 421, in _make_request\n'
[2020-10-24 04:22:08,202] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,202] {pod_launcher.py:125} INFO - b'    httplib_response = conn.getresponse()\n'
[2020-10-24 04:22:08,202] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,202] {pod_launcher.py:125} INFO - b'  File "/usr/local/lib/python3.9/http/client.py", line 1347, in getresponse\n'
[2020-10-24 04:22:08,238] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,238] {pod_launcher.py:125} INFO - b'    response.begin()\n'
[2020-10-24 04:22:08,240] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,240] {pod_launcher.py:125} INFO - b'  File "/usr/local/lib/python3.9/http/client.py", line 307, in begin\n'
[2020-10-24 04:22:08,240] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,240] {pod_launcher.py:125} INFO - b'    version, status, reason = self._read_status()\n'
[2020-10-24 04:22:08,240] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,240] {pod_launcher.py:125} INFO - b'  File "/usr/local/lib/python3.9/http/client.py", line 268, in _read_status\n'
[2020-10-24 04:22:08,240] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,240] {pod_launcher.py:125} INFO - b'    line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1")\n'
[2020-10-24 04:22:08,240] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,240] {pod_launcher.py:125} INFO - b'  File "/usr/local/lib/python3.9/socket.py", line 704, in readinto\n'
[2020-10-24 04:22:08,274] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,274] {pod_launcher.py:125} INFO - b'    return self._sock.recv_into(b)\n'
[2020-10-24 04:22:08,274] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,274] {pod_launcher.py:125} INFO - b'  File "/usr/local/lib/python3.9/ssl.py", line 1241, in recv_into\n'
[2020-10-24 04:22:08,316] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,315] {pod_launcher.py:125} INFO - b'    return self.read(nbytes, buffer)\n'
[2020-10-24 04:22:08,317] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,317] {pod_launcher.py:125} INFO - b'  File "/usr/local/lib/python3.9/ssl.py", line 1099, in read\n'
[2020-10-24 04:22:08,321] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,321] {pod_launcher.py:125} INFO - b'    return self._sslobj.read(len, buffer)\n'
[2020-10-24 04:22:08,321] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,321] {pod_launcher.py:125} INFO - b'socket.timeout: The read operation timed out\n'
[2020-10-24 04:22:08,322] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,321] {pod_launcher.py:125} INFO - b'\n'
[2020-10-24 04:22:08,322] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,322] {pod_launcher.py:125} INFO - b'During handling of the above exception, another exception occurred:\n'
[2020-10-24 04:22:08,322] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,322] {pod_launcher.py:125} INFO - b'\n'
[2020-10-24 04:22:08,322] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,322] {pod_launcher.py:125} INFO - b'Traceback (most recent call last):\n'
[2020-10-24 04:22:08,322] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,322] {pod_launcher.py:125} INFO - b'  File "/usr/local/lib/python3.9/site-packages/requests/adapters.py", line 439, in send\n'
[2020-10-24 04:22:08,337] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,336] {pod_launcher.py:125} INFO - b'    resp = conn.urlopen(\n'
[2020-10-24 04:22:08,337] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,337] {pod_launcher.py:125} INFO - b'  File "/usr/local/lib/python3.9/site-packages/urllib3/connectionpool.py", line 726, in urlopen\n'
[2020-10-24 04:22:08,341] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,341] {pod_launcher.py:125} INFO - b'    retries = retries.increment(\n'
[2020-10-24 04:22:08,342] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,342] {pod_launcher.py:125} INFO - b'  File "/usr/local/lib/python3.9/site-packages/urllib3/util/retry.py", line 403, in increment\n'
[2020-10-24 04:22:08,365] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,365] {pod_launcher.py:125} INFO - b'    raise six.reraise(type(error), error, _stacktrace)\n'
[2020-10-24 04:22:08,365] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,365] {pod_launcher.py:125} INFO - b'  File "/usr/local/lib/python3.9/site-packages/urllib3/packages/six.py", line 735, in reraise\n'
[2020-10-24 04:22:08,374] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,374] {pod_launcher.py:125} INFO - b'    raise value\n'
[2020-10-24 04:22:08,374] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,374] {pod_launcher.py:125} INFO - b'  File "/usr/local/lib/python3.9/site-packages/urllib3/connectionpool.py", line 670, in urlopen\n'
[2020-10-24 04:22:08,375] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,374] {pod_launcher.py:125} INFO - b'    httplib_response = self._make_request(\n'
[2020-10-24 04:22:08,375] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,375] {pod_launcher.py:125} INFO - b'  File "/usr/local/lib/python3.9/site-packages/urllib3/connectionpool.py", line 428, in _make_request\n'
[2020-10-24 04:22:08,375] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,375] {pod_launcher.py:125} INFO - b'    self._raise_timeout(err=e, url=url, timeout_value=read_timeout)\n'
[2020-10-24 04:22:08,375] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,375] {pod_launcher.py:125} INFO - b'  File "/usr/local/lib/python3.9/site-packages/urllib3/connectionpool.py", line 335, in _raise_timeout\n'
[2020-10-24 04:22:08,376] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,376] {pod_launcher.py:125} INFO - b'    raise ReadTimeoutError(\n'
[2020-10-24 04:22:08,376] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,376] {pod_launcher.py:125} INFO - b"urllib3.exceptions.ReadTimeoutError: HTTPSConnectionPool(host='bigquery.googleapis.com', port=443): Read timed out. (read timeout=60)\n"
[2020-10-24 04:22:08,376] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,376] {pod_launcher.py:125} INFO - b'\n'
[2020-10-24 04:22:08,376] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,376] {pod_launcher.py:125} INFO - b'During handling of the above exception, another exception occurred:\n'
[2020-10-24 04:22:08,376] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,376] {pod_launcher.py:125} INFO - b'\n'
[2020-10-24 04:22:08,376] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,376] {pod_launcher.py:125} INFO - b'Traceback (most recent call last):\n'
[2020-10-24 04:22:08,377] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,376] {pod_launcher.py:125} INFO - b'  File "/usr/local/bin/leanplum-data-export", line 33, in <module>\n'
[2020-10-24 04:22:08,420] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,420] {pod_launcher.py:125} INFO - b"    sys.exit(load_entry_point('leanplum-data-export', 'console_scripts', 'leanplum-data-export')())\n"
[2020-10-24 04:22:08,420] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,420] {pod_launcher.py:125} INFO - b'  File "/usr/local/lib/python3.9/site-packages/click/core.py", line 829, in __call__\n'
[2020-10-24 04:22:08,432] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,432] {pod_launcher.py:125} INFO - b'    return self.main(*args, **kwargs)\n'
[2020-10-24 04:22:08,432] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,432] {pod_launcher.py:125} INFO - b'  File "/usr/local/lib/python3.9/site-packages/click/core.py", line 782, in main\n'
[2020-10-24 04:22:08,437] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,437] {pod_launcher.py:125} INFO - b'    rv = self.invoke(ctx)\n'
[2020-10-24 04:22:08,437] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,437] {pod_launcher.py:125} INFO - b'  File "/usr/local/lib/python3.9/site-packages/click/core.py", line 1259, in invoke\n'
[2020-10-24 04:22:08,438] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,438] {pod_launcher.py:125} INFO - b'    return _process_result(sub_ctx.command.invoke(sub_ctx))\n'
[2020-10-24 04:22:08,438] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,438] {pod_launcher.py:125} INFO - b'  File "/usr/local/lib/python3.9/site-packages/click/core.py", line 1066, in invoke\n'
[2020-10-24 04:22:08,438] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,438] {pod_launcher.py:125} INFO - b'    return ctx.invoke(self.callback, **ctx.params)\n'
[2020-10-24 04:22:08,438] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,438] {pod_launcher.py:125} INFO - b'  File "/usr/local/lib/python3.9/site-packages/click/core.py", line 610, in invoke\n'
[2020-10-24 04:22:08,438] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,438] {pod_launcher.py:125} INFO - b'    return callback(*args, **kwargs)\n'
[2020-10-24 04:22:08,439] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,438] {pod_launcher.py:125} INFO - b'  File "/app/leanplum_data_export/leanplum_data_export/__main__.py", line 31, in export_leanplum\n'
[2020-10-24 04:22:08,463] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,463] {pod_launcher.py:125} INFO - b'    exporter.export(date, s3_bucket, bucket, prefix, bq_dataset, table_prefix, version, clean)\n'
[2020-10-24 04:22:08,464] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,464] {pod_launcher.py:125} INFO - b'  File "/app/leanplum_data_export/leanplum_data_export/export.py", line 62, in export\n'
[2020-10-24 04:22:08,464] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,464] {pod_launcher.py:125} INFO - b'    self.create_external_tables(gcs_bucket, prefix, date, self.DATA_TYPES,\n'
[2020-10-24 04:22:08,464] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,464] {pod_launcher.py:125} INFO - b'  File "/app/leanplum_data_export/leanplum_data_export/export.py", line 208, in create_external_tables\n'
[2020-10-24 04:22:08,464] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,464] {pod_launcher.py:125} INFO - b'    self.bq_client.delete_table(table, not_found_ok=True)\n'
[2020-10-24 04:22:08,464] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,464] {pod_launcher.py:125} INFO - b'  File "/usr/local/lib/python3.9/site-packages/google/cloud/bigquery/client.py", line 1055, in delete_table\n'
[2020-10-24 04:22:08,510] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,510] {pod_launcher.py:125} INFO - b'    self._call_api(retry, method="DELETE", path=table.path)\n'
[2020-10-24 04:22:08,512] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,512] {pod_launcher.py:125} INFO - b'  File "/usr/local/lib/python3.9/site-packages/google/cloud/bigquery/client.py", line 476, in _call_api\n'
[2020-10-24 04:22:08,512] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,512] {pod_launcher.py:125} INFO - b'    return call()\n'
[2020-10-24 04:22:08,512] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,512] {pod_launcher.py:125} INFO - b'  File "/usr/local/lib/python3.9/site-packages/google/api_core/retry.py", line 281, in retry_wrapped_func\n'
[2020-10-24 04:22:08,538] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,538] {pod_launcher.py:125} INFO - b'    return retry_target(\n'
[2020-10-24 04:22:08,539] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,539] {pod_launcher.py:125} INFO - b'  File "/usr/local/lib/python3.9/site-packages/google/api_core/retry.py", line 184, in retry_target\n'
[2020-10-24 04:22:08,540] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,540] {pod_launcher.py:125} INFO - b'    return target()\n'
[2020-10-24 04:22:08,540] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,540] {pod_launcher.py:125} INFO - b'  File "/usr/local/lib/python3.9/site-packages/google/cloud/_http.py", line 424, in api_request\n'
[2020-10-24 04:22:08,554] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,554] {pod_launcher.py:125} INFO - b'    response = self._make_request(\n'
[2020-10-24 04:22:08,554] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,554] {pod_launcher.py:125} INFO - b'  File "/usr/local/lib/python3.9/site-packages/google/cloud/_http.py", line 288, in _make_request\n'
[2020-10-24 04:22:08,556] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,555] {pod_launcher.py:125} INFO - b'    return self._do_request(\n'
[2020-10-24 04:22:08,556] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,556] {pod_launcher.py:125} INFO - b'  File "/usr/local/lib/python3.9/site-packages/google/cloud/_http.py", line 326, in _do_request\n'
[2020-10-24 04:22:08,556] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,556] {pod_launcher.py:125} INFO - b'    return self.http.request(\n'
[2020-10-24 04:22:08,556] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,556] {pod_launcher.py:125} INFO - b'  File "/usr/local/lib/python3.9/site-packages/google/auth/transport/requests.py", line 464, in request\n'
[2020-10-24 04:22:08,559] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,559] {pod_launcher.py:125} INFO - b'    response = super(AuthorizedSession, self).request(\n'
[2020-10-24 04:22:08,559] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,559] {pod_launcher.py:125} INFO - b'  File "/usr/local/lib/python3.9/site-packages/requests/sessions.py", line 533, in request\n'
[2020-10-24 04:22:08,559] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,559] {pod_launcher.py:125} INFO - b'    resp = self.send(prep, **send_kwargs)\n'
[2020-10-24 04:22:08,559] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,559] {pod_launcher.py:125} INFO - b'  File "/usr/local/lib/python3.9/site-packages/requests/sessions.py", line 646, in send\n'
[2020-10-24 04:22:08,560] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,559] {pod_launcher.py:125} INFO - b'    r = adapter.send(request, **kwargs)\n'
[2020-10-24 04:22:08,560] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,560] {pod_launcher.py:125} INFO - b'  File "/usr/local/lib/python3.9/site-packages/requests/adapters.py", line 529, in send\n'
[2020-10-24 04:22:08,560] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,560] {pod_launcher.py:125} INFO - b'    raise ReadTimeout(e, request=request)\n'
[2020-10-24 04:22:08,561] {logging_mixin.py:112} INFO - [2020-10-24 04:22:08,561] {pod_launcher.py:125} INFO - b"requests.exceptions.ReadTimeout: HTTPSConnectionPool(host='bigquery.googleapis.com', port=443): Read timed out. (read timeout=60)\n"
[2020-10-24 04:22:09,647] {logging_mixin.py:112} INFO - [2020-10-24 04:22:09,646] {pod_launcher.py:142} INFO - Event: firefox-android-release-export-af18059c had an event of type Failed
[2020-10-24 04:22:09,647] {logging_mixin.py:112} INFO - [2020-10-24 04:22:09,647] {pod_launcher.py:234} INFO - Event with job id firefox-android-release-export-af18059c Failed
[2020-10-24 04:22:09,687] {logging_mixin.py:112} INFO - [2020-10-24 04:22:09,687] {pod_launcher.py:142} INFO - Event: firefox-android-release-export-af18059c had an event of type Failed
[2020-10-24 04:22:09,687] {logging_mixin.py:112} INFO - [2020-10-24 04:22:09,687] {pod_launcher.py:234} INFO - Event with job id firefox-android-release-export-af18059c Failed

Tmp table names should include final dataset

Currently we load everything from the table name, but multiple datasets will have that same table name, resulting in improperly loading data from a different application.

If we include the destination dataset in the tmp table name, we should be able to load just the correct data.

Allow for jagged data rows

The Leanplum data doesn't always fill in every row with all columns of data. If we allow for jagged row this will make all subsequent columns NULL.

Delete existing data before running

If the job fails before deleting the GCS data, it will remain for the next run. We should delete the data before running to ensure that data isn't loaded multiple times.

Leanplum export failing due to KeyError

    return ctx.invoke(self.callback, **ctx.params)\n'
  File "/usr/local/lib/python3.8/site-packages/click/core.py", line 610, in invoke\n'
    return callback(*args, **kwargs)\n'
  File "/app/leanplum_data_export/leanplum_data_export/__main__.py", line 31, in export_leanplum\n'
    exporter.export(date, s3_bucket, bucket, prefix, bq_dataset, table_prefix, version, clean)\n'
  File "/app/leanplum_data_export/leanplum_data_export/export.py", line 36, in export\n'
    data_file_keys = self.get_files(date, s3_bucket, prefix)\n'
  File "/app/leanplum_data_export/leanplum_data_export/export.py", line 84, in get_files\n'
    data_file_keys.extend([content["Key"] for content in object_list["Contents"]\n'
KeyError: 'Contents'\n"

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.