From 47010cfe00c6607730750818ec6d9b4a22ec733b Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Sat, 6 Aug 2016 17:47:42 +0200 Subject: [PATCH 1/3] Added script for generating sponsors.csv. --- web/bountysource.nim | 144 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 web/bountysource.nim diff --git a/web/bountysource.nim b/web/bountysource.nim new file mode 100644 index 0000000000..a7c900df7a --- /dev/null +++ b/web/bountysource.nim @@ -0,0 +1,144 @@ +# Based on bountysource.cr located at https://github.com/crystal-lang/crystal-website/blob/master/scripts/bountysource.cr +import httpclient, asyncdispatch, json, strutils, os, strtabs, sequtils, future, + algorithm, times + +type + BountySource = ref object + client: AsyncHttpClient + team: string + + Sponsor = object + name, url, logo: string + amount, allTime: float + since: TimeInfo + +const + team = "nim" + apiUrl = "https://api.bountysource.com" + githubApiUrl = "https://api.github.com" + +proc newBountySource(team, token: string): BountySource = + result = BountySource( + client: newAsyncHttpClient(userAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36"), + team: team + ) + + # Set up headers + result.client.headers["Accept"] = "application/vnd.bountysource+json; version=2" + result.client.headers["Authorization"] = "token " & token + result.client.headers["Referer"] = "https://salt.bountysource.com/teams/nim/admin/supporters" + result.client.headers["Origin"] = "https://salt.bountysource.com/" + +proc getSupportLevels(self: BountySource): Future[JsonNode] {.async.} = + let response = await self.client.get(apiUrl & + "/support_levels?supporters_for_team=" & self.team) + doAssert response.status.startsWith($Http200) # TODO: There should be a == + return parseJson(response.body) + +proc getSupporters(self: BountySource): Future[JsonNode] {.async.} = + let response = await self.client.get(apiUrl & + "/supporters?order=monthly&per_page=200&team_slug=" & self.team) + doAssert response.status.startsWith($Http200) + return parseJson(response.body) + +proc getGithubUser(username: string): Future[JsonNode] {.async.} = + let client = newAsyncHttpClient() + let response = await client.get(githubApiUrl & "/users/" & username) + if response.status.startsWith($Http200): + return parseJson(response.body) + else: + return nil + +proc processLevels(supportLevels: JsonNode) = + var before = supportLevels.elems.len + supportLevels.elems.keepIf( + item => item["status"].getStr == "active" and + item["owner"]["display_name"].getStr != "Anonymous" + ) + echo("Found ", before - supportLevels.elems.len, " sponsors that cancelled or didn't pay.") + echo("Found ", supportLevels.elems.len, " active sponsors!") + + supportLevels.elems.sort( + (x, y) => cmp(y["amount"].getFNum, x["amount"].getFNum) + ) + +proc getSupporter(supporters: JsonNode, displayName: string): JsonNode = + for supporter in supporters: + if supporter["display_name"].getStr == displayName: + return supporter + doAssert false + +proc quote(text: string): string = + if {' ', ','} in text: + return "\"" & text & "\"" + else: + return text + +proc getLevel(amount: float): int = + result = 0 + const levels = [250, 150, 75, 25, 10, 5, 1] + for i in levels: + if amount.int <= i: + result = i + +proc writeCsv(sponsors: seq[Sponsor]) = + var csv = "" + csv.add "logo, name, url, this_month, all_time, since, level\n" + for sponsor in sponsors: + csv.add "$#, $#, $#, $#, $#, $#, $#\n" % [ + sponsor.logo.quote, sponsor.name.quote, + sponsor.url.quote, $sponsor.amount.int, + $sponsor.allTime.int, sponsor.since.format("MMM d, yyyy").quote, + $sponsor.amount.getLevel + ] + writeFile("sponsors.new.csv", csv) + +when isMainModule: + if paramCount() == 0: + quit("You need to specify the BountySource access token on the command\n" & + "line, you can find it by going onto https://www.bountysource.com/people/25278-dom96\n" & + "and looking at your browser's network inspector tab to see the token being\n" & + "sent to api.bountysource.com") + + let token = paramStr(1) + let bountysource = newBountySource(team, token) + + echo("Getting sponsors...") + let supporters = waitFor bountysource.getSupporters() + + var supportLevels = waitFor bountysource.getSupportLevels() + processLevels(supportLevels) + + echo("Generating sponsors list... (please be patient)") + var sponsors: seq[Sponsor] = @[] + for supportLevel in supportLevels: + let name = supportLevel["owner"]["display_name"].getStr + var url = "" + let ghUser = waitFor getGithubUser(name) + if not ghUser.isNil: + if ghUser["blog"].kind != JNull: + url = ghUser["blog"].getStr + else: + url = ghUser["html_url"].getStr + + if url.len > 0 and not url.startsWith("http"): + url = "http://" & url + + let amount = supportLevel["amount"].getFNum + # Only show URL when user donated at least $5. + if amount < 5: + url = "" + + let supporter = getSupporter(supporters, supportLevel["owner"]["display_name"].getStr) + var logo = "" + if amount >= 75: + discard # TODO + + sponsors.add(Sponsor(name: name, url: url, logo: logo, amount: amount, + allTime: supporter["alltime_amount"].getFNum(), + since: parse(supporter["created_at"].getStr, "yyyy-MM-dd'T'hh:mm:ss") + )) + + echo("Generated ", sponsors.len, " sponsors") + writeCsv(sponsors) + echo("Written csv file to sponsors.new.csv") From ccd4daaedbc440f63f85eccef94ce9aafdbe7a1e Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Sat, 6 Aug 2016 19:37:40 +0200 Subject: [PATCH 2/3] No spaces after commas in bountysource.nim output. --- web/bountysource.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/bountysource.nim b/web/bountysource.nim index a7c900df7a..a4f76417eb 100644 --- a/web/bountysource.nim +++ b/web/bountysource.nim @@ -85,7 +85,7 @@ proc writeCsv(sponsors: seq[Sponsor]) = var csv = "" csv.add "logo, name, url, this_month, all_time, since, level\n" for sponsor in sponsors: - csv.add "$#, $#, $#, $#, $#, $#, $#\n" % [ + csv.add "$#,$#,$#,$#,$#,$#,$#\n" % [ sponsor.logo.quote, sponsor.name.quote, sponsor.url.quote, $sponsor.amount.int, $sponsor.allTime.int, sponsor.since.format("MMM d, yyyy").quote, From 872f3a4ab1fcc9f4467fa92e3e34e9f2afe9000b Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Sat, 6 Aug 2016 19:39:11 +0200 Subject: [PATCH 3/3] Updated sponsors.csv --- web/sponsors.csv | 62 ++++++++++++++++++++++++------------------------ 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/web/sponsors.csv b/web/sponsors.csv index 9c28f43fff..516151c4fc 100644 --- a/web/sponsors.csv +++ b/web/sponsors.csv @@ -1,31 +1,31 @@ -logo,name,url,this_month,all_time,since,level -assets/bountysource/secondspectrum.png,Second Spectrum,http://www.secondspectrum.com/,250,500,"May 5, 2016",250 -assets/bountysource/xored.png,Xored Software,http://www.xored.com/,250,250,"Jun 20, 2016",250 -,flyx,http://flyx.org,35,70,"Apr 7, 2016",25 -,Adrian Veith,,25,50,"Apr 20, 2016",25 -,Federico Ceratto,http://firelet.net,25,50,"Apr 7, 2016",25 -,Lloyd Moore,https://github.com/iolloyd,25,50,"Apr 29, 2016",25 -,Ruslan Mustakov,https://github.com/endragor,25,50,"Apr 7, 2016",25 -,Yuriy Glukhov,https://github.com/yglukhov/,25,50,"Apr 6, 2016",25 -,Euan T.,http://euantorano.co.uk,25,25,"Jun 7, 2016",25 -,TedSinger,https://github.com/TedSinger,15,30,"Apr 9, 2016",10 -,Pradeep Gowda,https://www.btbytes.com/,10,20,"Apr 6, 2016",10 -,Jonathan Arnett,http://j3rn.com,10,10,"May 20, 2016",10 -,Matthew Baulch,,10,10,"Jun 7, 2016",10 -,niebaopeng,https://github.com/niebaopeng,10,10,"Apr 15, 2016",10 -,Oskari Timperi,,10,10,"Jun 8, 2016",10 -,RationalG,https://github.com/RationalG,10,10,"Jun 17, 2016",10 -,Sergey Avseyev,http://avsej.net,10,10,"Jun 10, 2016",10 -,Zach Aysan,http://venn.lc,10,10,"Jun 7, 2016",10 -,swalf,https://joindiaspora.com/people/889b5d8ff6ed0b98,5,30,"May 9, 2016",5 -,Handojo Goenadi,,5,10,"Apr 19, 2016",5 -,John Novak,http://www.johnnovak.net/,5,10,"Apr 29, 2016",5 -,Konstantin Molchanov,http://sloth-ci.com,5,10,"May 13, 2016",5 -,Matthew Newton,,5,10,"Apr 20, 2016",5 -,Mirek Rusin,http://quartzcomposer.com,5,10,"Apr 9, 2016",5 -,pyloor ,,5,10,"May 16, 2016",5 -,Sokolov Yura,https://github.com/funny-falcon,5,10,"Apr 7, 2016",5 -,calind,,5,5,"Jun 7, 2016",5 -,Ivan Florentin,https://github.com/ivanflorentin,5,5,"May 2, 2016",5 -,Michael D. Sklaroff,,1,2,"Apr 27, 2016",1 -,Svend Knudsen,,1,2,"Apr 11, 2016",1 +logo, name, url, this_month, all_time, since, level +assets/bountysource/secondspectrum.png,Second Spectrum,http://www.secondspectrum.com/,250,750,"May 5, 2016",250 +assets/bountysource/xored.svg,"Xored Software, Inc.",http://xored.com/,250,500,"Jun 20, 2016",250 +,avsej,http://avsej.net,75,85,"Jun 10, 2016",75 +,shkolnick-kun,https://github.com/shkolnick-kun,75,75,"Jul 6, 2016",75 +,flyx,http://flyx.org,35,140,"Apr 7, 2016",75 +,endragor,https://github.com/endragor,25,100,"Apr 7, 2016",25 +,euantorano,http://euantorano.co.uk,25,50,"Jun 7, 2016",25 +,FedericoCeratto,http://firelet.net,25,100,"Apr 7, 2016",25 +,"Adrian Veith",,25,100,"Apr 20, 2016",25 +,xxlabaza,https://github.com/xxlabaza,25,45,"Jun 17, 2016",25 +,"Yuriy Glukhov",,25,100,"Apr 6, 2016",25 +,"Jonathan Arnett",,10,30,"May 20, 2016",10 +,"Oskari Timperi",,10,20,"Jun 8, 2016",10 +,zachaysan,http://venn.lc,10,20,"Jun 7, 2016",10 +,"Matthew Baulch",,10,20,"Jun 7, 2016",10 +,RationalG,https://github.com/RationalG,10,20,"Jun 17, 2016",10 +,btbytes,https://www.btbytes.com/,10,40,"Apr 6, 2016",10 +,niebaopeng,https://github.com/niebaopeng,10,30,"Apr 15, 2016",10 +,moigagoo,http://sloth-ci.com,5,15,"May 13, 2016",5 +,calind,http://calindon.net,5,10,"Jun 7, 2016",5 +,swalf,https://github.com/swalf,5,35,"May 9, 2016",5 +,johnnovak,http://www.johnnovak.net/,5,20,"Apr 29, 2016",5 +,RyanMarcus,http://rmarcus.info,5,5,"Jul 19, 2016",5 +,Blumenversand,https://github.com/blumenversand,5,5,"Jul 21, 2016",5 +,lenzenmi,https://github.com/lenzenmi,5,5,"Jul 28, 2016",5 +,"Handojo Goenadi",,5,20,"Apr 19, 2016",5 +,"Date in Asia",,5,5,"Jul 30, 2016",5 +,"Matthew Newton",,5,20,"Apr 20, 2016",5 +,"Michael D. Sklaroff",,1,4,"Apr 27, 2016",1 +,"Svend Knudsen",,1,4,"Apr 11, 2016",1