Giter VIP home page Giter VIP logo

o2r-ui's Introduction

o2r-UI

Test

This project is a re-implementation of the o2r-platform using React.

Configuration

The API endpoint and the base URL for sharing ERCs on other platforms must be configured in ui/public/config.js.

Development environment with docker-compose

You can start all required o2r microservices (using latest images from Docker Hub) with just two commands using docker-compose (version 1.20.0+) and Docker (version 1.18.0+).

First, read the instructions on "Basics" and "Prerequisites" to prepare your host machine in the reference-implementation project.

This project contains one docker-compose configuration (file ui/docker-compose-dev.yml) to run all microservices & databases, and mount the client application directly from the source directory client. If you see an error related to the MongoDB or HTTP request timeouts during the first "up", abort the execution, then try again.

UI container

The container for the development of the UI is built locally based on ui/Dockerfile.dev. Only the directory /ui is mounted into the container, so if dependencies in ui/package.json change, you must update the container with

docker-compose --file docker-compose-dev.yml build --no-cache ui

Also note that the ui/node_modules directory is mounted so that your host's version of node best matches the one used in the Dockerfile.

Running the platform

cd ui/
docker-compose --file docker-compose-dev.yml up

The platform is available at http://localhost and the API at http://localhost/api.

Production environment with docker-compose

This project has another docker-compose configuration for the deployment of a production build (file ui/docker-compose.yml). This configuration has no ui container. Instead the webserver container creates a static production build with the command npm run build using a multi-stage docker file (file ui/Dockerfile) which is then served through nginx. For this reason there is also another nginx configuration (file ui/dev/nginx.conf). Because it shares a lot of the architecture with development configuration most of the endpoints are defined in a shared partial nginx configuration file (file ui/dev/nginx-share.conf).

To start the platform with the production build:

cd ui/

docker-compose up 

# force rebuild of images
docker-compose up --build

If you want to change the webserver container use:

docker-compose --file o2r-UI/ui/docker-compose.yml build --no-cache webserver

Accessing the API directly

  1. Click the "Log in" button in the UI
  2. Obtain the authentication token from the cookies
  3. Use it for requests outside of the UI, e.g. with curl as described in the API docs

Using with local microservices

If you want to run the UI based on locally running microservices, i.e., the o2r service runs no in containers but in Node.js processes locally on your machine, you can use a special docker-compose file, which uses an nginx configuration that does (a) not start database servers and (b) redirects all requests to the respective ports of the host IP (only works on Linux!):

docker-compose --file o2r-UI/ui/docker-compose-dev-local-microservices.yml up

Installing new packages

After you installed further packages from npm, you have to rebuild the container. The easiest way to do this are these two commands:

docker-compose down -v
docker-compose up --build

After the build the packages are available to use.

Attention! If apache is running, it may interfere with the nginx container. You may have to stop the apache service in order to run the platform successfully. See here for more information.

Create a release

This repository contains two pieces of software, whose versions are managed in ui/bindings/package.json and ui/package.json respectively. The major.minor versions must be kept in sync, .bugfix versions may differ.

cd ui/
npm install

cd bindings
npm install

The images are built on Docker Hub:

Test

Tests run as GitHub actions and you can inspect them over here:

Test

To run the test by yourself, complete the following steps:

  • Run the platform
    • cd ui/
    • docker-compose up
  • Install all packages local
    • npm install
  • Run all the test
    • npm test

You can debug the GithHub Actions. Collaborators can manually start the debug workflow and can connect via ssh to the host system on which the actual Action run. For further infromation read the debug docs

o2r-ui's People

Contributors

dependabot[bot] avatar eftyk avatar fmazin avatar njakuschona avatar nuest avatar

Stargazers

 avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

o2r-ui's Issues

Add display and creation of public links

The API now has a public link feature, see documentation at o2r-project/api#80

This PR adds this feature to the development environment of the UI: #117

It would be great if for users of level editor or higher (admin):

  • the existing public link is displayed on a compendium page (also for the author of ERC)
  • if no link exists, one can be added with a button click
  • if a link exists, it can be deleted with a button click

Substitution view

implement view for

  • selecting from other ERCs
  • selectinig data files
  • for comparing datafiles that were selected
  • make view for comparing data files editable (e.g. rename columns)
  • create new ERC out of the two

Upload functionality

  • HTML view
  • Adapt startpage to logged in/not logged in (not possible for logged out users)
  • include examples
  • Upload workspace as . ZIP file
  • Load from public share
  • progress bar while uploading the folder
  • cancel upload
  • error message
  • redirect to metadata

Show licenses if available

Exampe ERC: INSYDE
Rmd in workspace has licenses specified but these are not displayed in metadata view. Reason is that the abbreviations in the Rmd do not match the abbreviations in the license list. By default, the metadata view should display whatever is specified in the Rmd. Changes can then be made using the list.
image

module not found error

After installing a new module with npm install <module> --save the platform still cannot find the module.
Try out:
rm -rf node_modules and then npm install
OR
docker-compose down -v and then sudo docker-compose up
@NJaku01 Do you have similar issues?

Required Metadata form issues

  • save but shouldn't be enabled from the beginning, only when users change sth.
  • go to erc should be enabled from the beginning and not, when users changed sth.
  • reset should not reload the page but just replace the change value with the original one
  • save should trigger a message: https://material-ui.com/de/components/snackbars/
  • Reset should be enabled only when users changes sth., this means it should also be diabled after click on save
  • there is some space between the tabs bar and the header, check createERC.css
  • reset/save/go to erc should be available also for spatiotemporal data

Dashboard

@nuest a dashboard is to be created for the UI. Here information about ERCs shall be displayed. This information should be interesting for publishers and authors. Enclosed is the first draft of a dashboard as mock-up. Do you have any ideas, criticism or suggestions for improvement?
dashboard_mockup

Metadata form - Main

  • Redux or Formik
  • title (required)
  • show Authors from metadata (name, affiliation, orcid)
  • add further authors
  • edit/remove authors
  • show Abstract from metadata
  • editable
  • show publication date from metadata
  • show display file from metadata
  • Show main file from metadata
  • all fields are required
  • show licenses from metadata
  • least/most restrictive

Inspect view

  • implement data view for Rdata (see https://o2r.info/api/compendium/files/#file-inspection-rdata)
  • implement data view for geojson (based on already existing json) and show map?
  • use case: user marks variable/function (similar to when creating a binding), var/func oes to the bindings servcie which checks if there is a binding for it - if this is the case, the user receives a notification that the selected var/func is associated with a binding.
  • show somewhere the remaining files of an ERC, Dockerfile, metadata etc.

Use ERC image for executing bindings

Current the bindings microservice executes R scripts in one massive container calling R directly via exec.

Instead, the ERC's image should be used. Here is how that could work:

  • the bindings scripts are be mounted into the countainer
  • a library path with plumber is mounted into the container
  • an R session using this library in addition path is started
  • the entrypoint of the container is overwritten with a suitable plumber entrypoint
  • start the container (not start the R process)

The required snippets to do this from Java are in the muncher executor.js (though hard to read, I can help with that)

Inputdata not visible

The ERC contain some input data files (.csv) which are not visible in the ERC view:

image

Deploy UI to demo server

For this to work, we need Docker images on DockerHub, and have versioned releases.

Startpage View

  • header
  • footer
  • Impressum
  • privacy policy
  • about ERC
  • API docs and endpoint
  • ERC specification
  • Implementation
  • Version
  • Find more information
  • Logo
  • reference to project website

See o2r startpage

Manipulate view

  • activate comparison button only if there two or three (not more for now) saved settings for comparison, disable otherwise
  • implement radio buttons (check for widget type 'radio' or 'slider')
  • make slider draggable: there is some issue with the setState method. Commenting out the setState functions makes the slider work as expected
  • progress circle/bar at the beginning once a user clicks on manipulate (bindings service takes a second to start)
  • replace setTimeout function by a proper implementation (does not work if starting the manipulate service takes more than 1,5 secs - which can happen)

Bindings form

  • Find a way to extract results list automatically (manual input?)
  • include video/gif showing how bindings can be created
  • in the parameter section: next button is activated although no parameter is selected
  • slider input fields do not work with rationale numbers
  • add parameter should delete input field for parameters. In that case, the next button should also be disabled
  • Catch 'Cannot read caption of undefined' in Manipulate.js l. 93: happens e.g. after finishing parameter and widget - preview - add parameter - click next OR click on preview before caption has been entered bindings view completely overhauled.
  • Save should be disabled a long as users did not click on preview
  • implement radio buttons
  • bindings service always includes as.numeric for parameters, hence textual parameters do not work

Spatiotemporal view

I already solved some of the following issues and removed some elements to have the app ready for the demo. I think the view is a bit confusing. We should think about a better way to arrange the veiw

  • The search for address, region, country is not working. I removed it for the demo.
  • Text mentions two options, but three are listed - inconsistent.
  • "Edit" text plus "Start edit" redundant (same for draw)
  • "Save edit" should be "Stop editing" (save implies saving to metadata)
    image

Allow users to plaudit ERC loaded from a DOI

Plaudit is a simple, light-weight mechanism that allows an individual with an ORCID to endorse an object with a DOI.

https://plaudit.pub/

This would obviously only work with ERC loaded from a DOI, but it would be awesome to enable more users to break out of traditional academic ways of giving credit right from a binder.
Should also display plaudits for ERC, of course.

IndexSizeError in ERC view

At some point when I interact with the ERC view, I get the following error. I guess a simple catch for '0' should be enough.

image

Substitution: Minor issues

  • Add Header: "Substitution of input data files: Select one dataset of each ERC and then click on "Substitute""
  • Substitution tab should list only those ERCs that have some input data to avoid empty substiution views
    image
  • the two selected datasets should still be selected after selecting the second dataset
    image
  • change color of selected input to o2r blue :)
  • unclear what it means when the second two selected datasets are yellow. I guess it's just the mapping but this could be unlclear to users. Yellow often means sth. might not work, not be possible
    image
  • add labelling to each ERC: "Input files"
  • implement check if two datasets have the same format: Ideally allready for the listing of ERCs (show only those that have the same format)
  • "This is a substituted ERC"
  • "a base compendium..."
  • Provide link to forward users to the Check tab of the ERC

Providing too large datasets as download only

Some datasets, for example, deaths.csv in tidy data are too large for ou platform. We need to check if a file is larger than, for example, 5mb and if so, only provide it as a download. A message like "This file is too large but you can download it here" should be shown instead.

Create ERC - Spatiotemporal view

  • pull newest commit including createERC view
  • include leaflet map
  • include tools to create/edit rectangles
  • get geojson out from rectangle
  • include two widgets for selecting start and end date (calendar? slider?)
  • show spatiotemporal information if there is already information in the metadata of an ERC (map + time)

Cannot upload workspace because of problem during metadata extraction

Request:

curl -F "compendium=@/home/daniel/ownCloud/o2r-data/Korpus/public/workspace-rmd-data.zip;type=application/zip" -F content_type=workspace --cookie "connect.sid=s:ADOteuwd4fUZcGTBuc6cH7UaK0Czk7cd.Bb9x3m1vodnlEyvVZWH9vZeC4gxloqtBS1IsD9g7YZI" http://localhost/api/v1/compendium

Stacktrace:

loader_1          | 2019-05-23T07:23:56.037Z loader:steps [9EUG7] Input dir: /tmp/o2r/compendium/9EUG7 | Output dir: /tmp/o2r/compendium/9EUG7/.erc
loader_1          | 2019-05-23T07:23:56.037Z loader:steps [9EUG7] Created output dir: /tmp/o2r/compendium/9EUG7/.erc
loader_1          | 2019-05-23T07:23:56.038Z loader:steps [9EUG7] volume is configured, overwriting binds configuration (was [ '/tmp/o2r/compendium/9EUG7:/tmp/o2r/compendium/9EUG7', '/tmp/o2r/compendium/9EUG7/.erc:/tmp/o2r/compendium/9EUG7/.erc' ])
loader_1          | 2019-05-23T07:23:56.038Z loader:steps [9EUG7] Binds: [ 'o2rplatform_o2rstorage:/tmp/o2r/' ]
loader_1          | 2019-05-23T07:23:56.038Z loader:steps [9EUG7] Starting Docker container now with options and command:
loader_1          |     create_options: {"CpuShares":128,"Env":["O2R_LOADER=true"],"Memory":1073741824,"MemorySwap":2147483648,"User":"root","name":"meta_extract_9EUG7","HostConfig":{"Binds":["o2rplatform_o2rstorage:/tmp/o2r/"],"AutoRemove":true}}
loader_1          |     start_options: {}
loader_1          |     cmd: -debug extract --inputdir /tmp/o2r/compendium/9EUG7 --outputdir /tmp/o2r/compendium/9EUG7/.erc --metafiles --ercid 9EUG7 --basedir /tmp/o2r/compendium/9EUG7
loader_1          | 2019-05-23T07:23:56.041Z modem Sending: { path: '/containers/create?Hostname=&User=root&AttachStdin=false&AttachStdout=true&AttachStderr=true&Tty=true&OpenStdin=false&StdinOnce=false&Env=%5B%22O2R_LOADER%3Dtrue%22%5D&Cmd=%5B%22-debug%22%2C%22extract%22%2C%22--inputdir%22%2C%22%2Ftmp%2Fo2r%2Fcompendium%2F9EUG7%22%2C%22--outputdir%22%2C%22%2Ftmp%2Fo2r%2Fcompendium%2F9EUG7%2F.erc%22%2C%22--metafiles%22%2C%22--ercid%22%2C%229EUG7%22%2C%22--basedir%22%2C%22%2Ftmp%2Fo2r%2Fcompendium%2F9EUG7%22%5D&Image=o2rproject%2Fo2r-meta%3A9198450&Volumes=%7B%7D&VolumesFrom=%5B%5D&CpuShares=128&Memory=1073741824&MemorySwap=2147483648&name=meta_extract_9EUG7&HostConfig=%7B%22Binds%22%3A%5B%22o2rplatform_o2rstorage%3A%2Ftmp%2Fo2r%2F%22%5D%2C%22AutoRemove%22%3Atrue%7D',
loader_1          |   method: 'POST',
loader_1          |   headers: { 'Content-Type': 'application/json', 'Content-Length': 583 },
loader_1          |   key: undefined,
loader_1          |   cert: undefined,
loader_1          |   ca: undefined,
loader_1          |   socketPath: '/var/run/docker.sock' }
loader_1          | 2019-05-23T07:23:56.487Z modem Received: {"Id":"628154d9d3ecd416d9bd1757615d10d92627afcaad649d8770dd0e68e3139a9f","Warnings":null}
loader_1          | 
loader_1          | 2019-05-23T07:23:56.489Z modem Sending: { path: '/containers/628154d9d3ecd416d9bd1757615d10d92627afcaad649d8770dd0e68e3139a9f/attach?stream=true&stdout=true&stderr=true',
loader_1          |   method: 'POST',
loader_1          |   headers: { 'Content-Type': 'application/json', 'Content-Length': 43 },
loader_1          |   key: undefined,
loader_1          |   cert: undefined,
loader_1          |   ca: undefined,
loader_1          |   socketPath: '/var/run/docker.sock' }
loader_1          | 2019-05-23T07:23:56.503Z modem Sending: { path: '/containers/628154d9d3ecd416d9bd1757615d10d92627afcaad649d8770dd0e68e3139a9f/start',
loader_1          |   method: 'POST',
loader_1          |   headers: {},
loader_1          |   key: undefined,
loader_1          |   cert: undefined,
loader_1          |   ca: undefined,
loader_1          |   socketPath: '/var/run/docker.sock' }
loader_1          | 2019-05-23T07:23:58.516Z modem Received: 
loader_1          | 2019-05-23T07:23:58.536Z modem Sending: { path: '/containers/628154d9d3ecd416d9bd1757615d10d92627afcaad649d8770dd0e68e3139a9f/wait',
loader_1          |   method: 'POST',
loader_1          |   headers: {},
loader_1          |   key: undefined,
loader_1          |   cert: undefined,
loader_1          |   ca: undefined,
loader_1          |   socketPath: '/var/run/docker.sock' }
loader_1          | 2019-05-23T07:23:58.891Z loader:steps [9EUG7] [extract container] [o2rmeta] 20190523.072358 received arguments: {'debug': True, 'tool': 'extract', 'formats': False, 'inputdir': '/tmp/o2r/compendium/9EUG7', 'outputdir': '/tmp/o2r/compendium/9EUG7/.erc', 'outputtostdout': False, 'ercid': '9EUG7', 'basedir': '/tmp/o2r/compendium/9EUG7', 'modexml': False, 'stayoffline': False, 'metafiles': True, 'default_metadata_license': None}
loader_1          | [o2rmeta] 20190523.072358 launching extractor
loader_1          | 2019-05-23T07:23:58.892Z loader:steps [9EUG7] [extract container] [o2rmeta][debug: extract.metaextract @ start] 20190523.072358 Using CC-BY-4.0 as default license for metadata
loader_1          | 2019-05-23T07:23:58.893Z loader:steps [9EUG7] [extract container] [o2rmeta] 20190523.072358 directory </tmp/o2r/compendium/9EUG7/.erc> will be created during extraction...
loader_1          | 2019-05-23T07:23:58.894Z loader:steps [9EUG7] [extract container] [o2rmeta][error][debug: extract.metaextract @ start] 20190523.072358 ! error, input dir </tmp/o2r/compendium/9EUG7> does not exist
loader_1          | [o2rmeta][debug: extract.metaextract @ start] 20190523.072358 ! error, input dir </tmp/o2r/compendium/9EUG7> does not exist
loader_1          | 2019-05-23T07:23:59.900Z modem Received: {"Error":null,"StatusCode":1}
loader_1          | 
loader_1          | 2019-05-23T07:23:59.901Z loader:steps [9EUG7] container running: [object Object]
loader_1          | 2019-05-23T07:23:59.901Z loader:steps [9EUG7] Error during meta container run: { Error: null, StatusCode: 1 }
loader_1          | 2019-05-23T07:23:59.902Z modem Sending: { path: '/containers/628154d9d3ecd416d9bd1757615d10d92627afcaad649d8770dd0e68e3139a9f/logs?follow=true&stdout=true&stderr=true&timestamps=true',
loader_1          |   method: 'GET',
loader_1          |   headers: {},
loader_1          |   key: undefined,
loader_1          |   cert: undefined,
loader_1          |   ca: undefined,
loader_1          |   socketPath: '/var/run/docker.sock' }
loader_1          | 2019-05-23T07:23:59.923Z loader:directuploader [9EUG7] Rejection or unhandled failure during upload: Error: Received non-zero statuscode from container at docker.run (/loader/lib/steps.js:868:28) at /loader/node_modules/dockerode/lib/docker.js:1448:11 at /loader/node_modules/dockerode/lib/container.js:783:7 at Modem.buildPayload (/loader/node_modules/docker-modem/lib/modem.js:270:7) at IncomingMessage.<anonymous> (/loader/node_modules/docker-modem/lib/modem.js:229:14) at emitNone (events.js:111:20) at IncomingMessage.emit (events.js:208:7) at endReadableNT (_stream_readable.js:1064:12) at _combinedTickCallback (internal/process/next_tick.js:139:11) at process._tickCallback (internal/process/next_tick.js:181:9)
loader_1          | 2019-05-23T07:23:59.926Z loader:directuploader [{ id: '9EUG7',
loader_1          |   user: '0000-0001-6225-344X',
loader_1          |   content: 'workspace',
loader_1          |   req: 
loader_1          |    IncomingMessage {
loader_1          |      _readableState: [Object],
loader_1          |      readable: false,
loader_1          |      domain: null,
loader_1          |      _events: [Object],
loader_1          |      _eventsCount: 1,
loader_1          |      _maxListeners: undefined,
loader_1          |      socket: [Object],
loader_1          |      connection: [Object],
loader_1          |      httpVersionMajor: 1,
loader_1          |      httpVersionMinor: 0,
loader_1          |      httpVersion: '1.0',
loader_1          |      complete: true,
loader_1          |      headers: [Object],
loader_1          |      rawHeaders: [Array],
loader_1          |      trailers: {},
loader_1          |      rawTrailers: [],
loader_1          |      aborted: false,
loader_1          |      upgrade: false,
loader_1          |      url: '/api/v1/compendium',
loader_1          |      method: 'POST',
loader_1          |      statusCode: null,
loader_1          |      statusMessage: null,
loader_1          |      client: [Object],
loader_1          |      _consuming: true,
loader_1          |      _dumped: false,
loader_1          |      next: [Function: next],
loader_1          |      baseUrl: '',
loader_1          |      originalUrl: '/api/v1/compendium',
loader_1          |      _parsedUrl: [Object],
loader_1          |      params: {},
loader_1          |      query: {},
loader_1          |      res: [Object],
loader_1          |      body: [Object],
loader_1          |      _parsedOriginalUrl: [Object],
loader_1          |      sessionStore: [Object],
loader_1          |      sessionID: 'ADOteuwd4fUZcGTBuc6cH7UaK0Czk7cd',
loader_1          |      session: [Object],
loader_1          |      _passport: [Object],
loader_1          |      user: [Object],
loader_1          |      route: [Object],
loader_1          |      read: [Function],
loader_1          |      file: [Object] },
loader_1          |   res: 
loader_1          |    ServerResponse {
loader_1          |      domain: null,
loader_1          |      _events: [Object],
loader_1          |      _eventsCount: 1,
loader_1          |      _maxListeners: undefined,
loader_1          |      output: [],
loader_1          |      outputEncodings: [],
loader_1          |      outputCallbacks: [],
loader_1          |      outputSize: 0,
loader_1          |      writable: true,
loader_1          |      _last: false,
loader_1          |      upgrading: false,
loader_1          |      chunkedEncoding: false,
loader_1          |      shouldKeepAlive: false,
loader_1          |      useChunkedEncodingByDefault: false,
loader_1          |      sendDate: true,
loader_1          |      _removedConnection: false,
loader_1          |      _removedContLen: false,
loader_1          |      _removedTE: false,
loader_1          |      _contentLength: null,
loader_1          |      _hasBody: true,
loader_1          |      _trailer: '',
loader_1          |      finished: false,
loader_1          |      _headerSent: false,
loader_1          |      socket: [Object],
loader_1          |      connection: [Object],
loader_1          |      _header: null,
loader_1          |      _onPendingData: [Function: bound updateOutgoingData],
loader_1          |      _sent100: false,
loader_1          |      _expect_continue: false,
loader_1          |      req: [Object],
loader_1          |      locals: {},
loader_1          |      writeHead: [Function: writeHead],
loader_1          |      end: [Function: end],
loader_1          |      [Symbol(outHeadersKey)]: [Object] },
loader_1          |   archive: '/tmp/o2r/incoming/9EUG7',
loader_1          |   compendium_path: '/tmp/o2r/compendium/9EUG7',
loader_1          |   textFiles: [ '/tmp/o2r/compendium/9EUG7/main.Rmd' ],
loader_1          |   isBag: false,
loader_1          |   bagValid: null,
loader_1          |   configurationFile: '/tmp/o2r/compendium/9EUG7/erc.yml' }] Passon object:
loader_1          | %s
loader_1          | 2019-05-23T07:23:59.929Z loader:ctrl:directupload Error during upload: Error: Received non-zero statuscode from container at docker.run (/loader/lib/steps.js:868:28) at /loader/node_modules/dockerode/lib/docker.js:1448:11 at /loader/node_modules/dockerode/lib/container.js:783:7 at Modem.buildPayload (/loader/node_modules/docker-modem/lib/modem.js:270:7) at IncomingMessage.<anonymous> (/loader/node_modules/docker-modem/lib/modem.js:229:14) at emitNone (events.js:111:20) at IncomingMessage.emit (events.js:208:7) at endReadableNT (_stream_readable.js:1064:12) at _combinedTickCallback (internal/process/next_tick.js:139:11) at process._tickCallback (internal/process/next_tick.js:181:9)
webserver_1       | 172.20.0.1 - - [23/May/2019:07:23:59 +0000] "POST /api/v1/compendium HTTP/1.1" 500 50 "-" "curl/7.58.0"
loader_1          | 2019-05-23T07:23:59.948Z loader:steps [9EUG7] Error getting container logs after non-zero status code

Upload workspace zip glitch

When I drag and drop or select a workspace archive an then click on upload, the "Select file" dialog opens again, see (note the already uploaded archive file)

image

When I close that window and click on "upload" again, then the upload starts.

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.