zendesk / basecrm-ruby Goto Github PK
View Code? Open in Web Editor NEWBase CRM API Client
License: Apache License 2.0
Base CRM API Client
License: Apache License 2.0
I tested this:
session.deals.create(name: "Test", entity_id: session.contacts.all[0].id)
=> {
...
"name" => "Test",
...
}
session.deals.all
=> [
[0] {
"user_id" => 491482,
...
}
I then changed the deal's stage on the website.
session.deals.all
=> []
Hello, I just noticed the number of errors increased a lot since jul 20th
BaseCRM::ErrorsCollection:
[...] http_status=404 Not Found
code=incorrect_path message=path was not found details=Requested path at '/429.json' was not found using the HTTP method 'GET'.
anyone knows what this could mean?
For example it happens when executing AssociatedContactsService's where method
Thanks!
I can't figure out how to set a custom field. Here's my code snippet so far:
token = "redacted"
client = BaseCRM::Client.new(access_token: token)
client.leads.create(first_name: "Test", last_name: "Test")
There's a custom field in my Base setup named Notes
. How do I update the custom field and save the change to Base?
I have to wrap the hash that I want to use for creating a deal into BaseCRM::Deal
If I don't do this I get a type error (lib tries to access #value on a hash). Would be nice if this was described in the documentation. Would be even better if this was handled properly.
> BASE_CRM_CLIENT.deals.create(value: "20.0", name: "My Deal")
NoMethodError: undefined method `value' for {:value=>"20.0", :name=>"My Deal"}:Hash
Did you mean? value?
values
from [..]gems/basecrm-1.2.0/lib/basecrm/services/deals_service.rb:134:in sanitize
> BASE_CRM_CLIENT.deals.create({"value" => "20.0", "name" => "My Deal"})
NoMethodError: undefined method `value' for {"value"=>"20.0", "name"=>"My Deal"}:Hash
Did you mean? value?
values
from [...]gems/2.3.0/gems/basecrm-1.2.0/lib/basecrm/services/deals_service.rb:134:in sanitize
This works:
> deal = BaseCRM::Deal
=> BaseCRM::Deal
> deal = BaseCRM::Deal.new(created_at: Time.now, value: 20.0)
=> #<BaseCRM::Deal created_at=2016-11-24 08:28:17 +0100, value=#<BigDecimal:7ff480aa94a8,'0.2E2',9(36)>>
> deal.value
=> #<BigDecimal:7ff480aa94a8,'0.2E2',9(36)>
> BASE_CRM_CLIENT.deals.create(deal)
BaseCRM::ErrorsCollection: [aoiku-5h5ro-n17ib-qo7h0] http_status=422 Unprocessable Entity
code=missing message=attribute is missing details=The required attribute '/contact_id' must be present in the request. resource=deal field=/contact_id
Something like session.deals.find([204, 307, 588])
Like if the token is rejected or the service is unreachable?
Forecasts are important!
They should be API accessible via https://app.futuresimple.com/apis/sales/api/v1/deals/777653/forecasting.json?_=1366334553597.
Is there a way to set custom fields when I create a lead ?
If I use the following commands
lead = session.leads.find(7157280)
lead.custom_field_values.registration['value'] = "423423"
lead.save
This is what I get for lead.custom_field_values.registration['value']
"{"id"=>"34234237777", "custom_field_id"=>"48116", "field_type"=>"string", "position"=>"1", "value"=>"423423"}"
Alternatively, is there a way to create a tag for a user?
Thanks,
Ben
Hi,
Could I please request that you add support for the Call
and Call Outcome
resources?
For now, I have monkey-patched a barebones support for this directly into my integration code:
module BaseCRM
class Call < Model
end
class CallOutcome < Model
end
class CallsService
def initialize(client)
@client = client
end
def where(options = {})
_, _, root = @client.get('/calls', options)
root[:items].map{ |item| Call.new(item[:data]) }
end
end
class CallOutcomesService
def initialize(client)
@client = client
end
def all
_, _, root = @client.get('/call_outcomes', {})
root[:items].map{ |item| CallOutcome.new(item[:data]) }
end
end
class Client
def calls
@calls ||= CallsService.new(http_client)
end
def call_outcomes
@call_outcomes ||= CallOutcomesService.new(http_client)
end
end
end
Thanks in advance.
Currently, the Sources
service only returns deal sources, but the api documentation makes a distinction between /deal_sources
and lead_sources
endpoints.
So I guess the name of the service should reflect that and a new service to retrieve lead sources has to be introduced.
However, that would be a fair amount of duplication :)
Would it make sense to include a resource_type
option to the BaseCRM::Sources.where
method, like you do with BaseCRM::Tags.where
?
Since all the services are generated, I guess the change has to be made at the underlying API schema.
Happy to provide a PR, but I don't know where the schema is defined.
The code is:
client = BaseCRM::Client.new(access_token: "mykey")
deals = client.deals.where(stage_id: 1337)
The error is:
/Users/lihorne/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/basecrm-1.1.3/lib/basecrm/http_client.rb:51:in `request': undefined method `to_h' for {}:Hash (NoMethodError)
from /Users/lihorne/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/basecrm-1.1.3/lib/basecrm/http_client.rb:34:in `get'
from /Users/lihorne/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/basecrm-1.1.3/lib/basecrm/services/deals_service.rb:41:in `where'
from jobs/base.rb:21:in `<main>'
I could not find documentation on how to retrieve custom fields from the API.
This is not a problem of this client though.
I'm working on trying to implement the fetch method into my ruby app, and it does fire, however when trying to run the execute script i get the following error
TypeError (wrong argument type Hash (expected String)):
I can see the console output and it's obvious of why it's happening:
{:table=>"user", :statement=>"created", :properties=>#<BaseCRM::User id=660957, name="Richard Walsh", email="", created_at="2015-03-17T23:49:00Z", updated_at="2015-07-28T18:38:45Z", confirmed=true, role="admin", status="active">}
It's passing a hash instead of forming a sql insert statement, has anyone had any luck working this into postgresql?
base_sync.fetch do |meta, resource|
options = {
table: meta.type,
statement: meta.sync.event_type,
properties: resource
}
ActiveRecord::Base.connection.execute(options) ? meta.sync.ack : meta.sync.nack
end
I've tried to break resource down to get the keys and values as separate arrays even to create a sql insert statement, but I can't even seem to manage to get that working. The resource returns nil as I try to decompose it.
I should also note that I've implemented via Rails 4.
>> session.deals.all(stage: "Contacted")
ApiClient::Errors::BadRequest: ApiClient::Errors::BadRequest
from /usr/local/rvm/gems/ruby-2.0.0-p451/gems/api_client-0.5.7/lib/api_client/connection/basic.rb:95:in `handle_response'
For some reason, when we call client.leads.all
we receive nil
, causing issues on subsequent code.
This can be handled by us, but should this return nil
? Could this be a case where an exception should occur?
I have such Contact structure:
> BASE_CRM_CLIENT.contacts.all.first
=> #<BaseCRM::Contact id=10000, creator_id=203, contact_id=nil, created_at="2016-09-09T07:30:00Z", updated_at="2016-09-09T07:30:00Z", name=“Name1”, custom_fields={:created_at=>"2016-12-05 07:00:00 UTC", :some_id=>"7", :contact_type=>”Type”}>
When I try to fetch contact by contact_type in custom_fields, all is ok:
> BASE_CRM_CLIENT.contacts.where(name: ’Name1’, custom_fields: { contact_type: ‘Type’ })
=> [#<BaseCRM::Contact id=10000, creator_id=203, contact_id=nil, created_at="2016-09-09T07:30:00Z", updated_at="2016-09-09T07:30:00Z", name=“Name1”, custom_fields={:created_at=>"2016-12-05 07:00:00 UTC", :some_id=>"7", :contact_type=>”Type”}>]
But when I use some_id or created_at I get:
> BASE_CRM_CLIENT.contacts.where(name: 'Name1', custom_fields: { some_id: '7' })
BaseCRM::ErrorsCollection: [apgub-jnkri-r9rah-54kgg] http_status=400 Bad Request
code=invalid_param message=invalid request query parameter details=The request query parameter 'custom_fields/some_id' is malformed, missing, or has an invalid value (is an unexpected parameter)
> BASE_CRM_CLIENT.contacts.where(name: ‘Name1’, custom_fields: { created_at: '2016-12-05 07:00:00 UTC' })
BaseCRM::ErrorsCollection: [apgub-q085v-uols5-9adeg] http_status=400 Bad Request
code=invalid_param message=invalid request query parameter details=The request query parameter 'custom_fields/created_at' is malformed, missing, or has an invalid value (is an unexpected parameter)
I am getting this error occasional.
my code:
results = base.contacts.where(is_organization: true, email: email)
The error:
vendor/bundle/ruby/2.1.0/gems/basecrm-1.2.3/lib/basecrm/middlewares/raise_error.rb:11 in on_complete
vendor/bundle/ruby/2.1.0/gems/faraday-0.9.1/lib/faraday/response.rb:9 in block in call
vendor/bundle/ruby/2.1.0/gems/faraday-0.9.1/lib/faraday/response.rb:57 in on_complete
vendor/bundle/ruby/2.1.0/gems/faraday-0.9.1/lib/faraday/response.rb:8 in call
vendor/bundle/ruby/2.1.0/gems/basecrm-1.2.3/lib/basecrm/middlewares/oauth_bearer_token.rb:13 in call
vendor/bundle/ruby/2.1.0/gems/faraday-0.9.1/lib/faraday/rack_builder.rb:139 in build_response
vendor/bundle/ruby/2.1.0/gems/faraday-0.9.1/lib/faraday/connection.rb:377 in run_request
vendor/bundle/ruby/2.1.0/gems/faraday-0.9.1/lib/faraday/connection.rb:140 in get
vendor/bundle/ruby/2.1.0/gems/basecrm-1.2.3/lib/basecrm/http_client.rb:67 in request
vendor/bundle/ruby/2.1.0/gems/basecrm-1.2.3/lib/basecrm/http_client.rb:66 in instance_eval
vendor/bundle/ruby/2.1.0/gems/basecrm-1.2.3/lib/basecrm/http_client.rb:66 in request
vendor/bundle/ruby/2.1.0/gems/basecrm-1.2.3/lib/basecrm/http_client.rb:34 in get
vendor/bundle/ruby/2.1.0/gems/basecrm-1.2.3/lib/basecrm/services/contacts_service.rb:43 in where
Is there any API end-point for converting a lead to a deal? If not, consider this issue as a feature request. :-)
My biz team updates Deals with Notes quite frequently, but unfortunately it appears a note does not update the deal's updated_at
value (e.g. a deal might be last updated May 24 even though someone added a note yesterday).
I tried sorting my array of deals based on the presence of a note, however this query is so unbelievably slow that the page times out every time:
session.deals.all(stage: :incoming).sort_by do |deal|
(deal.notes.all.present? && deal.notes.all.last[:updated_at]) ||
deal[:updated_at]
end
Any advice much appreciated!
Contrary to the Leads wiki, leads.persisted?
neither returns true nor false, it seems, after calling leads.create()
. It’s not even part of the method. The only thing that returns is:
BaseCRM::Lead
{
id: [ID of created lead],
owner_id: [ID of default owner],
…
}
followed by the rest of the lead data.
I’ve been using lead.id.present?
to validate success. Am I missing something? Or is the Wiki outdated / incorrect?
The api documentation for create_lead lists source_id
as an attribute that may be set thorugh a POST /leads
. Unfortunately source_id
is not included in the OPTS_KEYS_TO_PERSIST
for the LeadService
and thus gets thrown out during sanitization.
I get that the Services get auto-generated from a JSON schema and the fix probably has to made there. I'd be happy to provide a PR if somebody can point me to where this JSON schema is located.
Hey team,
Is it possible to upgrade basecrm-ruby
dependencies to use faraday
gem version 1.0.0
?
I already created a pull request.
Best regards,
Mohamed.
It looks like this library is not regularly maintained ... should it work with newer ruby versions?
For example, in Ruby 2.4.2, a simple require seems to not work...
irb(main):001:0> require "basecrm"
=> true
irb(main):002:0> BaseCRM::Client
NameError: uninitialized constant BaseCRM
...whereas in ruby 2.0.0, it does...
irb(main):001:0> require "basecrm"
=> true
irb(main):002:0> BaseCRM::Client
=> BaseCRM::Client
When I pull up a deal with an associated contact, I don't see the
contact ids. contacts_id shows as an empty arrays for deals.
Loving basecrm and the api.
Thanks,
Ben
pp deal = @session.deals.all
[{"contact_ids"=>[],
"stage_code"=>"incoming",
"sort_value"=>nil,
"name"=>"Asdfljasdlfkj -",
"user_name"=>"Ben Ward",
"id"=>2175076,
"exchange_rate"=>nil,
"overdue_tasks"=>0,
"loss_reason_id"=>nil,
"is_new"=>false,
"stage_id"=>1,
"last_stage_change_at"=>"2014-02-22T00:36:28Z",
"hot"=>false,
"deal_tags"=>"",
"entity_id"=>53025042,
"account_id"=>320136,
"deal_account"=>{"name"=>"Asdfljasdlfkj -", "id"=>53025042},
"stage_name"=>"New Lead",
"source_id"=>nil,
"user_id"=>399302,
"created_at"=>"2014-02-22T00:36:29Z",
"unread_emails"=>0,
"currency"=>"USD",
"dropbox_email"=>"[email protected]",
"updated_at"=>"2014-02-22T00:36:29Z",
"added_on"=>"2014-02-22",
"scope"=>0,
"is_closed"=>false,
"custom_fields"=>{}}]
I just tried to update a product and it seems that it is complaining about fields being null
. You should be able to "round trip" a read/write. As a workaround how to do remove those additional fields from the update?
(byebug) product = client.products.where(sku: "Hold").first
#<BaseCRM::Product id=3036919, updated_at="2019-03-31T09:13:12Z", created_at="2019-03-31T09:13:12Z", active=true, description=nil, name="Hold Plan", sku="Hold", prices=[{:currency=>"USD", :amount=>"25.00"}], max_discount=nil, max_markup=nil, cost=nil, cost_currency=nil>
(byebug) product.name = "New Name"
"New Name"
(byebug) client.products.update(product)
*** BaseCRM::ErrorsCollection Exception: [b9qc3-kh39i-cqe7c-usmp0] http_status=422 Unprocessable Entity
code=blank message=attribute can't be blank details=This field may not be null. resource=product field=/data/cost
code=blank message=attribute can't be blank details=This field may not be null. resource=product field=/data/cost_currency
code=blank message=attribute can't be blank details=This field may not be null. resource=product field=/data/description
code=blank message=attribute can't be blank details=This field may not be null. resource=product field=/data/max_markup
Its killing me that I cant just build an instance of a new resource and have the headers passed down to the instance, i.e.
l = session.leads.new(:first_name => "whatever")
l.save
will throw auth error as headers havent been passed.
I was digging through the api client gem source code but even if I wanted to attempt to fix that I couldnt as it's not hosted on github :)
It would be tremendously helpful to be able to query the number of items remaining in the queue when using the Sync API. The API endpoint for sync does deliver this info, but it's ignored in this wrapper gem. Having this info available is particularly useful when doing an initial sync with Base, when there could be thousands of records and this could take quite a long time.
I propose that an attr_reader
is added to the SyncService
object (e.g. count_left
as it's called in the API response) such that it can be queried in the body of the .fetch
method block.
Why don't you have upsert for your services. This is a very import feature for syncing data, and that is all I need this gem for is syncing information to base.
When I attempt to create a task with the basecrm API, I get the following.
require 'active_support/all'
require 'basecrm'
session = BaseCrm::Session.new(MY_TOKEN)
contact = session.contacts.create(name: '[email protected]')
deal = session.deals.create(name: "New Account", client_id: contact.id)
reminder = deal.tasks.create(:content => "Progress with trial", remind: true, date: (Date.current + 6.days).to_s(:db), client_id: contact.id)
# ApiClient::Errors::Forbidden: ApiClient::Errors::Forbidden
# from /Users/____/.rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/api_client-0.5.0/lib/api_client/connection/basic.rb:83:in `handle_response'
Any help would be greatly appreciated!
Thanks,
Kane
I tried to come up with a field name for a tag, and tried many options, e.g.:
base.deals.where(contact_id: contact.id, tag_list: 'vk-onboarding')
base.deals.where(contact_id: contact.id, tag: 'vk-onboarding')
base.deals.where(contact_id: contact.id, tags: 'vk-onboarding')
base.deals.where(contact_id: contact.id, tags: ['vk-onboarding'])
base.deals.where(contact_id: contact.id, tags: ['vk-onboarding'])
base.deals.where(contact_id: contact.id, tag_id: 1)
base.deals.where(contact_id: contact.id, tag_ids: 1)
None of this works. How to search by tag?
I am unable to get tags_joined_by_comma to save. For example:
session.contacts.create(first_name: "test", last_name: "run", tags_joined_by_comma: "Expired,Active")
=> {...
"tags_joined_by_comma" => "",
...
}
I see in API doc, it has: GET /v2/contact/custom_fields .
Is there a way to do that using SDK?
>> a = session.contacts.create(first_name: "Bobby", last_name: "Bobbers", tags_joined_by_comma: "multilocation,premium")
=> {...
"tags_joined_by_comma" => "",
...}
>> a.tags_joined_by_comma
=> ""
>> a.tags_joined_by_comma = "multilocation"
=> "multilocation"
>> a.save
=> {...
"tags_joined_by_comma" => "multilocation",
...}
Pretty simple code: @base.deals.create name: lead.email, contact_id: contact.id, stage_id: stage_id, tags_joined_by_comma: 'vk-onboarding'
There's absolutely no documentation on how to add tags to deal, leads and contacts. I found tags_joined_by_comma
in one of the tickets. I found tag_list
in a different one. None of this works.
The documentation of this gem is troubling. Also, I can pass any garbage (e.g. tag_list
which doesn't look to be supported) and that field is silently ignored. The API should throw an exception when an unmatched parameter is found (if you use Rails on the backend, it'd be config.action_controller.action_on_unpermitted_parameters = :raise
).
When I update source_id on a lead and do client.leads.update(lead) it doesn't actually update the lead.
The source_id remains nil
Hey guys,
This is more of a general API question vs a ruby specific question, but here goes.
When creating a lead you accept any arbitrary string for status
, but in fact there are only a limited set of valid values (eg new, working, unqualified
by default). However, I can't seem to find a way to fetch the set of supported values from anywhere.
I was hoping there might be a /lead_statuses
endpoint or something similar?
Am I missing something, or is this a known gap in the API?
Thanks so much,
Matthew
Hi there - I don't believe the source_id is being posted to the Base v2 API. I can't find it in the lead
model either
How do I search for a contact with a custom field? I want to be able to search alternative email addresses for contacts to see if I can get a match. I am guessing however that even if I could filter by custom fields that alternate email addresses are not supported for searching? Is there any way I can search for a contact via an alternative email address?
the code I'm running:
def create_lead(lead_fields = {})
lead = session.leads.create({
first_name: lead_fields['name'].split(' ')[0] || "N/A",
last_name: lead_fields['name'].split(' ',2)[1] || "N/A",
email: lead_fields['email'],
company_name: lead_fields['company_name'],
phone_number: lead_fields['phone_number']
})
lead.persisted?
end
The parameters being passed:
{"type"=>"request_invite", "name"=>"Ben Angel", "email"=>"[email protected]", "company_name"=>"Envoy", "phone_number"=>"3123639596", "content"=>"We are special"}
The error I'm receiving
21:32:50 web.1 | Completed 500 Internal Server Error in 21ms
21:32:50 web.1 |
21:32:50 web.1 | NoMethodError (undefined method `strip' for nil:NilClass):
21:32:50 web.1 | app/services/base_integration.rb:84:in `create_lead'
21:32:50 web.1 | app/controllers/statics_controller.rb:51:in `send_contact_email'
I don't have anything going on with strip in my code, but I also don't see anything in the basecrm gem. Is this something that could be going on in the API?
Best,
Ben
Looks like it's missing here: https://github.com/basecrm/basecrm-ruby/blob/master/lib/basecrm/services/deals_service.rb#L5
When I try to access a deal's forecast, everything blows up in my face.
irb(main):016:0> deal = session.deals.all[1]
<SNIP>
````ruby
irb(main):019:0> deal.forecasting
{}
NoMethodError: undefined method `strip' for nil:NilClass
from /usr/local/opt/rbenv/versions/2.0.0-p0/lib/ruby/2.0.0/net/http/header.rb:17:in `block in initialize_http_header'
from /usr/local/opt/rbenv/versions/2.0.0-p0/lib/ruby/2.0.0/net/http/header.rb:15:in `each'
from /usr/local/opt/rbenv/versions/2.0.0-p0/lib/ruby/2.0.0/net/http/header.rb:15:in `initialize_http_header'
from /usr/local/opt/rbenv/versions/2.0.0-p0/lib/ruby/2.0.0/net/http/generic_request.rb:44:in `initialize'
from /usr/local/opt/rbenv/versions/2.0.0-p0/lib/ruby/2.0.0/net/http/request.rb:14:in `initialize'
from /usr/local/opt/rbenv/versions/2.0.0-p0/lib/ruby/2.0.0/net/http.rb:1125:in `new'
from /usr/local/opt/rbenv/versions/2.0.0-p0/lib/ruby/2.0.0/net/http.rb:1125:in `get'
from /usr/local/opt/rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/faraday-0.8.7/lib/faraday/adapter/net_http.rb:73:in `perform_request'
from /usr/local/opt/rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/faraday-0.8.7/lib/faraday/adapter/net_http.rb:38:in `call'
from /usr/local/opt/rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/faraday-0.8.7/lib/faraday/request/url_encoded.rb:14:in `call'
from /usr/local/opt/rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/faraday-0.8.7/lib/faraday/connection.rb:247:in `run_request'
from /usr/local/opt/rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/faraday-0.8.7/lib/faraday/connection.rb:100:in `get'
from /usr/local/opt/rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/api_client-0.4.1/lib/api_client/connection/basic.rb:33:in `get'
from /usr/local/opt/rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/api_client-0.4.1/lib/api_client/scope.rb:84:in `request'
from /usr/local/opt/rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/api_client-0.4.1/lib/api_client/scope.rb:89:in `get'
from /usr/local/opt/rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/basecrm-0.0.2/lib/base_crm/forecasting.rb:8:in `fetch_for_deal'
from /usr/local/opt/rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/api_client-0.4.1/lib/api_client/scope.rb:108:in `block in method_missing'
from /usr/local/opt/rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/api_client-0.4.1/lib/api_client/mixins/scoping.rb:29:in `scoped'
from /usr/local/opt/rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/api_client-0.4.1/lib/api_client/scope.rb:107:in `method_missing'
from /usr/local/opt/rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/basecrm-0.0.2/lib/base_crm/deal.rb:33:in `forecasting'
from (irb):19
from /usr/local/opt/rbenv/versions/2.0.0-p0/bin/irb:12:in `<main>'irb(main):020:0>
irb(main):021:0*
Hey, guys,
Doesn't look like notes are being paginated properly:
irb(main):011:0> session.notes.all.count
{}
=> 20
irb(main):012:0>
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.