diff --git a/Makefile b/Makefile index 5bc7895..14f21b8 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,6 @@ html: markdown --include-before-body $(include_dir)/share.html \ --include-after-body $(include_dir)/stats.html \ --title-prefix $(title) \ - -smart \ --toc epub: markdown diff --git a/chapters/02-github-fundamentals.md b/chapters/02-github-fundamentals.md index 13f1318..31a2e01 100644 --- a/chapters/02-github-fundamentals.md +++ b/chapters/02-github-fundamentals.md @@ -107,7 +107,7 @@ jQuery[^jQuery]在发布版本``2.1.3``,一共有152个commit。我们可以 > GitHub可以托管各种git库,并提供一个web界面,但与其它像 SourceForge或Google Code这样的服务不同,GitHub的独特卖点在于从另外一个项目进行分支的简易性。为一个项目贡献代码非常简单:首先点击项目站点的“fork”的按钮,然后将代码检出并将修改加入到刚才分出的代码库中,最后通过内建的“pull request”机制向项目负责人申请代码合并。已经有人将GitHub称为代码玩家的MySpace。 -### 在GitHub创建项目 +### 在 GitHub 创建项目 接着,我们试试在上面创建一个项目: @@ -139,7 +139,7 @@ git push -u origin master 如果你完成了上面的步骤之后,那么我想你想知道你需要怎样的项目。 -##GitHub流行项目分析 +## GitHub 流行项目分析 之前曾经分析过一些GitHub的用户行为,现在我们先来说说GitHub上的Star吧。(截止: 2015年3月9日23时。) diff --git a/chapters/03-build-github-project.md b/chapters/03-build-github-project.md index bfbe0bd..062baaa 100644 --- a/chapters/03-build-github-project.md +++ b/chapters/03-build-github-project.md @@ -324,7 +324,7 @@ branches: 这是一种驱动写出更规范js的方法。 -###Mocha +### Mocha > Mocha 是一个优秀的JS测试框架,支持TDD/BDD,结合 should.js/expect/chai/better-assert,能轻松构建各种风格的测试用例。 @@ -405,7 +405,7 @@ it('should return book label & url', function () { 这就是个问题了,于是偶然间看到了一个叫code climate的网站。 -###Code Climate +### Code Climate > Code Climate consolidates the results from a suite of static analysis tools into a single, real-time report, giving your team the information it needs to identify hotspots, evaluate new approaches, and improve code quality. @@ -435,7 +435,7 @@ A | lib/url_handler.js | 9 | 0 | 5 | 2.2 | 94.1% | 0 ![Coverage][1] -###代码的坏味道 +### 代码的坏味道 于是我们就打开``lib/database/sqlite_helper.js``,因为其中有两个坏味道 diff --git a/chapters/06-refactor-project.md b/chapters/06-refactor-project.md index e91b443..dc7ba4f 100644 --- a/chapters/06-refactor-project.md +++ b/chapters/06-refactor-project.md @@ -309,7 +309,7 @@ Windows/Linux: 木有 鼠标: **Refactor** | ``Replace Temp with Query`` -####重构之前 +#### 重构之前 过多的临时变量会让我们写出更长的函数,函数不应该太多,以便使功能单一。这也是重构的另外的目的所在,只有函数专注于其功能,才会更容易读懂。 @@ -366,7 +366,7 @@ public class replaceTemp { 3. 选择``basePrice``再``Inline Method`` -####Intellij IDEA重构 +#### Intellij IDEA重构 在Intellij IDEA的文档中对此是这样的例子 diff --git a/chapters/07-tdd-with-autotest.md b/chapters/07-tdd-with-autotest.md index e47b0c5..d581f7a 100644 --- a/chapters/07-tdd-with-autotest.md +++ b/chapters/07-tdd-with-autotest.md @@ -96,7 +96,7 @@ req.end(); pip install twill -###Twill 登陆测试 +### Twill 登陆测试 1.启动我们的应用。 @@ -143,7 +143,7 @@ req.end(); 发现重定向到首页了。 -###Twill 测试脚本 +### Twill 测试脚本 当然我们也可以用脚本直接来测试``login.twill``: diff --git a/chapters/11-analytics.md b/chapters/11-analytics.md index 51fb798..be19208 100644 --- a/chapters/11-analytics.md +++ b/chapters/11-analytics.md @@ -146,7 +146,7 @@ draw_date("data/2014-01-01-0.json") 不过这个是osrc的分析结果。 -###python github 每周情况分析 +### python github 每周情况分析 看一张分析后的结果 @@ -183,7 +183,7 @@ draw_date("data/2014-01-01-0.json") 8474, 7984, 12933, 13504, 13763, 13544, 12940, 7119, 7346, 13412, 14008, 12555 -###Python 数据分析 +### Python 数据分析 重写了一个新的方法用于计算提交数,直至后面才意识到其实我们可以算行数就够了,但是方法上有点hack @@ -232,7 +232,7 @@ def get_month_total(): 接着我们需要去遍历每个结果,后面的后面会发现这个效率真的是太低了,为什么木有多线程? -###Python Matplotlib图表 +### Python Matplotlib图表 让我们的matplotlib来做这些图表的工作 @@ -335,7 +335,7 @@ sudo zypper install sqlite3 不过,用yast2也很不错,不是么。。 -###数据导入 +### 数据导入 需要注意的是这里是需要python2.7,起源于对gzip的上下文管理器的支持问题 @@ -413,7 +413,7 @@ date_re = re.compile(r"([0-9]{4})-([0-9]{2})-([0-9]{2})-([0-9]+)\.json.gz") 更好的方案? -###Redis +### Redis 查询用户事件总数 @@ -462,7 +462,7 @@ pipe.execute() 到这里我们算是知道了OSRC的数据库部分是如何工作的。 -####Redis 查询 +#### Redis 查询 主要代码如下所示 @@ -505,7 +505,7 @@ def get_vector(user, pipe=None): osrc最有意思的一部分莫过于flann,当然说的也是系统后台的设计的一个很关键及有意思的部分。 -##邻近算法与相似用户 +## 邻近算法与相似用户 邻近算法是在这个分析过程中一个很有意思的东西。 diff --git a/chapters/14-streak-your-github.md b/chapters/14-streak-your-github.md index 430cb98..fa359b0 100644 --- a/chapters/14-streak-your-github.md +++ b/chapters/14-streak-your-github.md @@ -136,7 +136,7 @@ GitHub连击 这个可以从两部分说起: -####重构Skill Tree +#### 重构 Skill Tree 原来的是 @@ -149,7 +149,7 @@ GitHub连击 代码: [https://github.com/phodal/skillock](https://github.com/phodal/skillock) -####技能树Sherlock +#### 技能树Sherlock - D3.js - Dagre-D3.js @@ -311,7 +311,7 @@ GitHub连击 这也是下一个值得提高的地方。 -###其他 +### 其他 是时候写这个小结了。从不会写代码,到写代码是从0到1的过程,但是要从1到60都不是一件容易的事。无论是刷GitHub也好(不要是自动提交),或者是换工作也好,我们都在不断地练习。 diff --git a/github-roam.md b/github-roam.md index a9762be..5057504 100644 --- a/github-roam.md +++ b/github-roam.md @@ -324,7 +324,7 @@ jQuery[^jQuery]在发布版本``2.1.3``,一共有152个commit。我们可以 > GitHub可以托管各种git库,并提供一个web界面,但与其它像 SourceForge或Google Code这样的服务不同,GitHub的独特卖点在于从另外一个项目进行分支的简易性。为一个项目贡献代码非常简单:首先点击项目站点的“fork”的按钮,然后将代码检出并将修改加入到刚才分出的代码库中,最后通过内建的“pull request”机制向项目负责人申请代码合并。已经有人将GitHub称为代码玩家的MySpace。 -### 在GitHub创建项目 +### 在 GitHub 创建项目 接着,我们试试在上面创建一个项目: @@ -356,7 +356,7 @@ git push -u origin master 如果你完成了上面的步骤之后,那么我想你想知道你需要怎样的项目。 -##GitHub流行项目分析 +## GitHub 流行项目分析 之前曾经分析过一些GitHub的用户行为,现在我们先来说说GitHub上的Star吧。(截止: 2015年3月9日23时。) @@ -759,7 +759,7 @@ branches: 这是一种驱动写出更规范js的方法。 -###Mocha +### Mocha > Mocha 是一个优秀的JS测试框架,支持TDD/BDD,结合 should.js/expect/chai/better-assert,能轻松构建各种风格的测试用例。 @@ -840,7 +840,7 @@ it('should return book label & url', function () { 这就是个问题了,于是偶然间看到了一个叫code climate的网站。 -###Code Climate +### Code Climate > Code Climate consolidates the results from a suite of static analysis tools into a single, real-time report, giving your team the information it needs to identify hotspots, evaluate new approaches, and improve code quality. @@ -870,7 +870,7 @@ A | lib/url_handler.js | 9 | 0 | 5 | 2.2 | 94.1% | 0 ![Coverage][1] -###代码的坏味道 +### 代码的坏味道 于是我们就打开``lib/database/sqlite_helper.js``,因为其中有两个坏味道 @@ -1452,7 +1452,7 @@ Windows/Linux: 木有 鼠标: **Refactor** | ``Replace Temp with Query`` -####重构之前 +#### 重构之前 过多的临时变量会让我们写出更长的函数,函数不应该太多,以便使功能单一。这也是重构的另外的目的所在,只有函数专注于其功能,才会更容易读懂。 @@ -1509,7 +1509,7 @@ public class replaceTemp { 3. 选择``basePrice``再``Inline Method`` -####Intellij IDEA重构 +#### Intellij IDEA重构 在Intellij IDEA的文档中对此是这样的例子 @@ -1647,7 +1647,7 @@ req.end(); pip install twill -###Twill 登陆测试 +### Twill 登陆测试 1.启动我们的应用。 @@ -1694,7 +1694,7 @@ req.end(); 发现重定向到首页了。 -###Twill 测试脚本 +### Twill 测试脚本 当然我们也可以用脚本直接来测试``login.twill``: @@ -2280,7 +2280,7 @@ draw_date("data/2014-01-01-0.json") 不过这个是osrc的分析结果。 -###python github 每周情况分析 +### python github 每周情况分析 看一张分析后的结果 @@ -2317,7 +2317,7 @@ draw_date("data/2014-01-01-0.json") 8474, 7984, 12933, 13504, 13763, 13544, 12940, 7119, 7346, 13412, 14008, 12555 -###Python 数据分析 +### Python 数据分析 重写了一个新的方法用于计算提交数,直至后面才意识到其实我们可以算行数就够了,但是方法上有点hack @@ -2366,7 +2366,7 @@ def get_month_total(): 接着我们需要去遍历每个结果,后面的后面会发现这个效率真的是太低了,为什么木有多线程? -###Python Matplotlib图表 +### Python Matplotlib图表 让我们的matplotlib来做这些图表的工作 @@ -2469,7 +2469,7 @@ sudo zypper install sqlite3 不过,用yast2也很不错,不是么。。 -###数据导入 +### 数据导入 需要注意的是这里是需要python2.7,起源于对gzip的上下文管理器的支持问题 @@ -2547,7 +2547,7 @@ date_re = re.compile(r"([0-9]{4})-([0-9]{2})-([0-9]{2})-([0-9]+)\.json.gz") 更好的方案? -###Redis +### Redis 查询用户事件总数 @@ -2596,7 +2596,7 @@ pipe.execute() 到这里我们算是知道了OSRC的数据库部分是如何工作的。 -####Redis 查询 +#### Redis 查询 主要代码如下所示 @@ -2639,7 +2639,7 @@ def get_vector(user, pipe=None): osrc最有意思的一部分莫过于flann,当然说的也是系统后台的设计的一个很关键及有意思的部分。 -##邻近算法与相似用户 +## 邻近算法与相似用户 邻近算法是在这个分析过程中一个很有意思的东西。 @@ -3139,7 +3139,7 @@ GitHub连击 这个可以从两部分说起: -####重构Skill Tree +#### 重构 Skill Tree 原来的是 @@ -3152,7 +3152,7 @@ GitHub连击 代码: [https://github.com/phodal/skillock](https://github.com/phodal/skillock) -####技能树Sherlock +#### 技能树Sherlock - D3.js - Dagre-D3.js @@ -3314,7 +3314,7 @@ GitHub连击 这也是下一个值得提高的地方。 -###其他 +### 其他 是时候写这个小结了。从不会写代码,到写代码是从0到1的过程,但是要从1到60都不是一件容易的事。无论是刷GitHub也好(不要是自动提交),或者是换工作也好,我们都在不断地练习。 diff --git a/index.html b/index.html index 33d5162..2fc2fe2 100644 --- a/index.html +++ b/index.html @@ -8,63 +8,73 @@ - + @@ -133,8 +143,9 @@ code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Infor
  • GitHub
  • +
  • GitHub 流行项目分析
  • Pull Request
  • -
  • 代码质量与重构
  • +
  • 代码质量与重构
  • Git 提交信息及几种不同的规范
  • 功能测试
  • Fake Server
  • @@ -209,8 +226,8 @@ code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Infor
  • Git 与 GitHub 工具推荐
  • 如何在GitHub“寻找灵感(fork)”

    于是有了

    -
    public static void main(String[] args) {
    -
    int result_add=new Cal().add(1,2);
    -
    int result_sub=new Cal2().sub(2,1);
    -
    System.out.println("Hello,s");
    -
    mprint(result_add);
    -
    mprint(result_sub);
    -
    }
    -
    -
    private static void mprint(int result_sub) {
    -
    System.out.println(result_sub);
    -
    }
    +
    public static void main(String[] args) {
    +    int result_add=new Cal().add(1,2);
    +    int result_sub=new Cal2().sub(2,1);
    +    System.out.println("Hello,s");
    +    mprint(result_add);
    +    mprint(result_sub);
    +}
    +
    +private static void mprint(int result_sub) {
    +    System.out.println(result_sub);
    +}

    似乎我们不应该这样对待System.out.println,那么让我们内联回去

    Inline Method

    快捷键:alt+command+n

    @@ -1499,41 +1524,41 @@ React.render(
  • 选中Inline all invocations and remove the method(2 occurrences) 点确定
  • 然后我们等于什么也没有做了~~:

    -
    public static void main(String[] args) {
    -
    int result_add=new Cal().add(1,2);
    -
    int result_sub=new Cal2().sub(2,1);
    -
    System.out.println("Hello,s");
    -
    System.out.println(result_add);
    -
    System.out.println(result_sub);
    -
    }
    +
    public static void main(String[] args) {
    +    int result_add=new Cal().add(1,2);
    +    int result_sub=new Cal2().sub(2,1);
    +    System.out.println("Hello,s");
    +    System.out.println(result_add);
    +    System.out.println(result_sub);
    +}

    似乎这个例子不是很好,但是够用来说明了。

    Pull Members Up

    开始之前让我们先看看Cal2类:

    -
    public class Cal2 extends Cal {
    -
    -
    public int sub(int a,int b){
    -
    return a-b;
    -
    }
    -
    }
    +
    public class Cal2 extends Cal {
    +
    +    public int sub(int a,int b){
    +        return a-b;
    +    }
    +}

    以及Cal2的父类Cal

    -
    public class Cal {
    -
    -
    public int add(int a,int b){
    -
    return a+b;
    -
    }
    -
    -
    }
    +
    public class Cal {
    +
    +    public int add(int a,int b){
    +        return a+b;
    +    }
    +
    +}

    最后的结果,就是将Cal2类中的sub方法,提到父类:

    -
    public class Cal {
    -
    -
    public int add(int a,int b){
    -
    return a+b;
    -
    }
    -
    -
    public int sub(int a,int b){
    -
    return a-b;
    -
    }
    -
    }
    +
    public class Cal {
    +
    +    public int add(int a,int b){
    +        return a+b;
    +    }
    +
    +    public int sub(int a,int b){
    +        return a-b;
    +    }
    +}

    而我们所要做的就是鼠标右键

    重构之以查询取代临时变量

    快捷键

    @@ -1541,42 +1566,42 @@ React.render(

    Windows/Linux: 木有

    或者: Shift+alt+command+T 再选择 Replace Temp with Query

    鼠标: Refactor | Replace Temp with Query

    -

    ####重构之前

    +

    重构之前

    过多的临时变量会让我们写出更长的函数,函数不应该太多,以便使功能单一。这也是重构的另外的目的所在,只有函数专注于其功能,才会更容易读懂。

    以书中的代码为例

    -
    import java.lang.System;
    -
    -
    public class replaceTemp {
    -
    public void count() {
    -
    double basePrice = _quantity * _itemPrice;
    -
    if (basePrice > 1000) {
    -
    return basePrice * 0.95;
    -
    } else {
    -
    return basePrice * 0.98;
    -
    }
    -
    }
    -
    }
    +
    import java.lang.System;
    +
    +public class replaceTemp {
    +    public void count() {
    +        double basePrice = _quantity * _itemPrice;
    +        if (basePrice > 1000) {
    +            return basePrice * 0.95;
    +        } else {
    +            return basePrice * 0.98;
    +        }
    +    }
    +}

    重构

    选中basePrice很愉快地拿鼠标点上面的重构

    Replace Temp With Query
    Replace Temp With Query

    便会返回

    -
    import java.lang.System;
    -
    -
    public class replaceTemp {
    -
    public void count() {
    -
    if (basePrice() > 1000) {
    -
    return basePrice() * 0.95;
    -
    } else {
    -
    return basePrice() * 0.98;
    -
    }
    -
    }
    -
    -
    private double basePrice() {
    -
    return _quantity * _itemPrice;
    -
    }
    -
    }
    +
    import java.lang.System;
    +
    +public class replaceTemp {
    +    public void count() {
    +        if (basePrice() > 1000) {
    +            return basePrice() * 0.95;
    +        } else {
    +            return basePrice() * 0.98;
    +        }
    +    }
    +
    +    private double basePrice() {
    +        return _quantity * _itemPrice;
    +    }
    +}

    而实际上我们也可以

    1. 选中

      @@ -1584,17 +1609,17 @@ React.render(
    2. 对其进行Extrace Method

    3. 选择basePriceInline Method

    -

    ####Intellij IDEA重构

    +

    Intellij IDEA重构

    在Intellij IDEA的文档中对此是这样的例子

    -
    public class replaceTemp {
    -
    -
    public void method() {
    -
    String str = "str";
    -
    String aString = returnString().concat(str);
    -
    System.out.println(aString);
    -
    }
    -
    -
    }
    +
    public class replaceTemp {
    +
    +    public void method() {
    +        String str = "str";
    +        String aString = returnString().concat(str);
    +        System.out.println(aString);
    +    }
    +
    +}

    接着我们选中aString,再打开重构菜单,或者

    Command+Alt+Shift+T 再选中Replace Temp with Query

    便会有下面的结果:

    @@ -1620,28 +1645,28 @@ public class replaceTemp {

    之前正在重写一个物联网的服务端,主要便是结合CoAP、MQTT、HTTP等协议构成一个物联网的云服务。现在,主要的任务是集中于协议与授权。由于,不同协议间的授权是不一样的,最开始的时候我先写了一个http put授权的功能,而在起先的时候是如何测试的呢?

    curl --user root:root -X PUT -d '{ "dream": 1 }' -H "Content-Type: application/json" http://localhost:8899/topics/test

    我只要顺利在request中看有无req.headers.authorization,我便可以继续往下,接着给个判断。毕竟,我们对HTTP协议还是蛮清楚的。

    -
    if (!req.headers.authorization) {
    -
    res.statusCode = 401;
    -
    res.setHeader('WWW-Authenticate', 'Basic realm="Secure Area"');
    -
    return res.end('Unauthorized');
    -
    }
    +
    if (!req.headers.authorization) {
    +  res.statusCode = 401;
    +  res.setHeader('WWW-Authenticate', 'Basic realm="Secure Area"');
    +  return res.end('Unauthorized');
    +}

    可是除了HTTP协议,还有MQTT和CoAP。对于MQTT协议来说,那还算好,毕竟自带授权,如:

    -
    mosquitto_pub -u root -P root -h localhost -d -t lettuce -m "Hello, MQTT. This is my first message."
    +
    mosquitto_pub -u root -P root -h localhost -d -t lettuce -m "Hello, MQTT. This is my first message."

    便可以让我们简单地完成这个功能,然而有的协议是没有这样的功能如CoAP协议中是用Option来进行授权的。现在的工具如libcoap只能有如下的简单功能

    -
    coap-client -m get coap://127.0.0.1:5683/topics/zero -T
    +
    coap-client -m get coap://127.0.0.1:5683/topics/zero -T

    于是,先写了个测试脚本来验证功能。

    -
    var coap = require('coap');
    -
    var request = coap.request;
    -
    var req = request({hostname: 'localhost',port:5683,pathname: '',method: 'POST'});
    -
    -
    ...
    -
    -
    req.setHeader("Accept", "application/json");
    -
    req.setOption('Block2', [new Buffer('phodal'), new Buffer('phodal')]);
    -
    -
    ...
    -
    -
    req.end();
    +
    var coap     = require('coap');
    +var request  = coap.request;
    +var req = request({hostname: 'localhost',port:5683,pathname: '',method: 'POST'});
    +
    +...
    +
    +req.setHeader("Accept", "application/json");
    +req.setOption('Block2',  [new Buffer('phodal'), new Buffer('phodal')]);
    +
    +...
    +
    +req.end();

    写完测试脚本后发现不对了,这个不应该是测试的代码吗? 于是将其放到了spec中,接着发现了上面的全部功能的实现过程为什么不用TDD实现呢?

    说说TDD

    测试驱动开发是一个很“古老”的程序开发方法,然而由于国内的开发流程的问题——即开发人员负责功能的测试,导致这么好的一项技术没有在国内推广。

    @@ -1672,7 +1697,7 @@ public class replaceTemp {

    看了一下源码,大概原理就是用requests下载html,接着用lxml解析html,比较有意思的是内嵌了一个DSL

    这是一个Python的库。

     pip install twill
    -

    ###Twill 登陆测试

    +

    Twill 登陆测试

    1.启动我们的应用。

    2.进入twill shell

    twill-sh
    @@ -1704,7 +1729,7 @@ fv 1 password test
    Note: submit is using submit button: name="login", value="登入" current page: http://127.0.0.1:5000/

    发现重定向到首页了。

    -

    ###Twill 测试脚本

    +

    Twill 测试脚本

    当然我们也可以用脚本直接来测试login.twill:

    go http://127.0.0.1:5000/login
     
    @@ -2030,37 +2055,37 @@ Set up your git name and email, this is important so that your commits can be id
     

    ==, 这个文件代表什么?

    2014年1月1日零时到一时,用户在github上的操作,这里的用户指的是很多。。一共有4814条数据,从commit、create到issues都有。

    数据解析

    -
    import json
    -
    for line in open(jsonfile):
    -
    line = f.readline()
    +

    然后再解析json

    -
    import dateutil.parser
    -
    -
    lin = json.loads(line)
    -
    date = dateutil.parser.parse(lin["created_at"])
    +

    这里用到了dateutil,因为新鲜出炉的数据是string需要转换为dateutil,再到数据放到数组里头。最后有就有了parse_data

    -
    def parse_data(jsonfile):
    -
    f = open(jsonfile, "r")
    -
    dataarray = []
    -
    datacount = 0
    -
    -
    for line in open(jsonfile):
    -
    line = f.readline()
    -
    lin = json.loads(line)
    -
    date = dateutil.parser.parse(lin["created_at"])
    -
    datacount += 1
    -
    dataarray.append(date.minute)
    -
    -
    minuteswithcount = [(x, dataarray.count(x)) for x in set(dataarray)]
    -
    f.close()
    -
    return minuteswithcount
    +

    下面这句代码就是将上面的解析为

    -
    minuteswithcount = [(x, dataarray.count(x)) for x in set(dataarray)]
    +

    这样的数组以便于解析

    -
    [(0, 92), (1, 67), (2, 86), (3, 73), (4, 76), (5, 67), (6, 61), (7, 71), (8, 62), (9, 71), (10, 70), (11, 79), (12, 62), (13, 67), (14, 76), (15, 67), (16, 74), (17, 48), (18, 78), (19, 73), (20, 89), (21, 62), (22, 74), (23, 61), (24, 71), (25, 49), (26, 59), (27, 59), (28, 58), (29, 74), (30, 69), (31, 59), (32, 89), (33, 67), (34, 66), (35, 77), (36, 64), (37, 71), (38, 75), (39, 66), (40, 62), (41, 77), (42, 82), (43, 95), (44, 77), (45, 65), (46, 59), (47, 60), (48, 54), (49, 66), (50, 74), (51, 61), (52, 71), (53, 90), (54, 64), (55, 67), (56, 67), (57, 55), (58, 68), (59, 91)]
    +

    Matplotlib

    开始之前需要安装``matplotlib

    -
    sudo pip install matplotlib
    +

    然后引入这个库

      import matplotlib.pyplot as plt

    如上面的那个结果,只需要

    @@ -2071,47 +2096,47 @@ Set up your git name and email, this is important so that your commits can be id plt.show()

    最后代码可见

    -
    #!/usr/bin/env python
    -
    # -*- coding: utf-8 -*-
    -
    -
    import json
    -
    import dateutil.parser
    -
    import numpy as np
    -
    import matplotlib.mlab as mlab
    -
    import matplotlib.pyplot as plt
    -
    -
    -
    def parse_data(jsonfile):
    -
    f = open(jsonfile, "r")
    -
    dataarray = []
    -
    datacount = 0
    -
    -
    for line in open(jsonfile):
    -
    line = f.readline()
    -
    lin = json.loads(line)
    -
    date = dateutil.parser.parse(lin["created_at"])
    -
    datacount += 1
    -
    dataarray.append(date.minute)
    -
    -
    minuteswithcount = [(x, dataarray.count(x)) for x in set(dataarray)]
    -
    f.close()
    -
    return minuteswithcount
    -
    -
    -
    def draw_date(files):
    -
    x = []
    -
    y = []
    -
    mwcs = parse_data(files)
    -
    for mwc in mwcs:
    -
    x.append(mwc[0])
    -
    y.append(mwc[1])
    -
    -
    plt.figure(figsize=(8,4))
    -
    plt.plot(x, y,label = files)
    -
    plt.legend()
    -
    plt.show()
    -
    -
    draw_date("data/2014-01-01-0.json")
    +
    #!/usr/bin/env python
    +# -*- coding: utf-8 -*-
    +
    +import json
    +import dateutil.parser
    +import numpy as np
    +import matplotlib.mlab as mlab
    +import matplotlib.pyplot as plt
    +
    +
    +def parse_data(jsonfile):
    +    f = open(jsonfile, "r")
    +    dataarray = []
    +    datacount = 0
    +
    +    for line in open(jsonfile):
    +        line = f.readline()
    +        lin = json.loads(line)
    +        date = dateutil.parser.parse(lin["created_at"])
    +        datacount += 1
    +        dataarray.append(date.minute)
    +
    +    minuteswithcount = [(x, dataarray.count(x)) for x in set(dataarray)]
    +    f.close()
    +    return minuteswithcount
    +
    +
    +def draw_date(files):
    +    x = []
    +    y = []
    +    mwcs = parse_data(files)
    +    for mwc in mwcs:
    +        x.append(mwc[0])
    +        y.append(mwc[1])
    +
    +    plt.figure(figsize=(8,4))
    +    plt.plot(x, y,label = files)
    +    plt.legend()
    +    plt.show()
    +
    +draw_date("data/2014-01-01-0.json")

    每周分析

    继上篇之后,我们就可以分析用户的每周提交情况,以得出用户的真正的工具效率,每个程序员的工作时间可能是不一样的,如

    @@ -2120,7 +2145,7 @@ Set up your git name and email, this is important so that your commits can be id

    这是我的每周情况,显然如果把星期六移到前面的话,随着工作时间的增长,在github上的使用在下降,作为一个

      a fulltime hacker who works best in the evening (around 8 pm).

    不过这个是osrc的分析结果。

    -

    ###python github 每周情况分析

    +

    python github 每周情况分析

    看一张分析后的结果

    Feb Results
    Feb Results
    @@ -2152,159 +2177,159 @@ Set up your git name and email, this is important so that your commits can be id
      6570, 7420, 11274, 12073, 12160, 12378, 12897,
       8474, 7984, 12933, 13504, 13763, 13544, 12940,
       7119, 7346, 13412, 14008, 12555
    -

    ###Python 数据分析

    +

    Python 数据分析

    重写了一个新的方法用于计算提交数,直至后面才意识到其实我们可以算行数就够了,但是方法上有点hack

    -
    def get_minutes_counts_with_id(jsonfile):
    -
    datacount, dataarray = handle_json(jsonfile)
    -
    minuteswithcount = [(x, dataarray.count(x)) for x in set(dataarray)]
    -
    return minuteswithcount
    -
    -
    -
    def handle_json(jsonfile):
    -
    f = open(jsonfile, "r")
    -
    dataarray = []
    -
    datacount = 0
    -
    -
    for line in open(jsonfile):
    -
    line = f.readline()
    -
    lin = json.loads(line)
    -
    date = dateutil.parser.parse(lin["created_at"])
    -
    datacount += 1
    -
    dataarray.append(date.minute)
    -
    -
    f.close()
    -
    return datacount, dataarray
    -
    -
    -
    def get_minutes_count_num(jsonfile):
    -
    datacount, dataarray = handle_json(jsonfile)
    -
    return datacount
    -
    -
    -
    def get_month_total():
    -
    """
    -
    -
    :rtype : object
    -
    """
    -
    monthdaycount = []
    -
    for i in range(1, 20):
    -
    if i < 10:
    -
    filename = 'data/2014-02-0' + i.__str__() + '-0.json'
    -
    else:
    -
    filename = 'data/2014-02-' + i.__str__() + '-0.json'
    -
    monthdaycount.append(get_minutes_count_num(filename))
    -
    return monthdaycount
    +

    接着我们需要去遍历每个结果,后面的后面会发现这个效率真的是太低了,为什么木有多线程?

    -

    ###Python Matplotlib图表

    +

    Python Matplotlib图表

    让我们的matplotlib来做这些图表的工作

    -
    if __name__ == '__main__':
    -
    results = pd.get_month_total()
    -
    print results
    -
    -
    plt.figure(figsize=(8, 4))
    -
    plt.plot(results.__getslice__(0, 7), label="first week")
    -
    plt.plot(results.__getslice__(7, 14), label="second week")
    -
    plt.plot(results.__getslice__(14, 21), label="third week")
    -
    plt.legend()
    -
    plt.show()
    +

    蓝色的是第一周,绿色的是第二周,红色的是第三周就有了上面的结果。

    我们还需要优化方法,以及多线程的支持。

    让我们分析之前的程序,然后再想办法做出优化。网上看到一篇文章http://www.huyng.com/posts/python-performance-analysis/讲的就是分析这部分内容的。

    存储到数据库中

    SQLite3

    我们创建了一个名为userdata.db的数据库文件,然后创建了一个表,里面有owner,language,eventtype,name url

    -
    def init_db():
    -
    conn = sqlite3.connect('userdata.db')
    -
    c = conn.cursor()
    -
    c.execute('''CREATE TABLE userinfo (owner text, language text, eventtype text, name text, url text)''')
    +

    接着我们就可以查询数据,这里从结果讲起。

    -
    def get_count(username):
    -
    count = 0
    -
    userinfo = []
    -
    condition = 'select * from userinfo where owener = \'' + str(username) + '\''
    -
    for zero in c.execute(condition):
    -
    count += 1
    -
    userinfo.append(zero)
    -
    -
    return count, userinfo
    +

    当我查询gmszone的时候,也就是我自己就会有如下的结果

    -
    (u'gmszone', u'ForkEvent', u'RESUME', u'TeX', u'https://github.com/gmszone/RESUME')
    -
    (u'gmszone', u'WatchEvent', u'iot-dashboard', u'JavaScript', u'https://github.com/gmszone/iot-dashboard')
    -
    (u'gmszone', u'PushEvent', u'wechat-wordpress', u'Ruby', u'https://github.com/gmszone/wechat-wordpress')
    -
    (u'gmszone', u'WatchEvent', u'iot', u'JavaScript', u'https://github.com/gmszone/iot')
    -
    (u'gmszone', u'CreateEvent', u'iot-doc', u'None', u'https://github.com/gmszone/iot-doc')
    -
    (u'gmszone', u'CreateEvent', u'iot-doc', u'None', u'https://github.com/gmszone/iot-doc')
    -
    (u'gmszone', u'PushEvent', u'iot-doc', u'TeX', u'https://github.com/gmszone/iot-doc')
    -
    (u'gmszone', u'PushEvent', u'iot-doc', u'TeX', u'https://github.com/gmszone/iot-doc')
    -
    (u'gmszone', u'PushEvent', u'iot-doc', u'TeX', u'https://github.com/gmszone/iot-doc')
    -
    109
    +

    一共有109个事件,有Watch,Create,Push,Fork还有其他的, 项目主要有iot,RESUME,iot-dashboard,wechat-wordpress, 接着就是语言了,Tex,Javascript,Ruby,接着就是项目的url了。

    值得注意的是。

    -
    -rw-r--r-- 1 fdhuang staff 905M Apr 12 14:59 userdata.db
    +

    这个数据库文件有905M,不过查询结果相当让人满意,至少相对于原来的结果来说。

    Python自带了对SQLite3的支持,然而我们还需要安装SQLite3

    -
    brew install sqlite3
    +

    或者是

    -
    sudo port install sqlite3
    +

    或者是Ubuntu的

    -
    sudo apt-get install sqlite3
    +

    openSUSE自然就是

    -
    sudo zypper install sqlite3
    +

    不过,用yast2也很不错,不是么。。

    -

    ###数据导入

    +

    数据导入

    需要注意的是这里是需要python2.7,起源于对gzip的上下文管理器的支持问题

    -
    def handle_gzip_file(filename):
    -
    userinfo = []
    -
    with gzip.GzipFile(filename) as f:
    -
    events = [line.decode("utf-8", errors="ignore") for line in f]
    -
    -
    for n, line in enumerate(events):
    -
    try:
    -
    event = json.loads(line)
    -
    except:
    -
    -
    continue
    -
    -
    actor = event["actor"]
    -
    attrs = event.get("actor_attributes", {})
    -
    if actor is None or attrs.get("type") != "User":
    -
    continue
    -
    -
    key = actor.lower()
    -
    -
    repo = event.get("repository", {})
    -
    info = str(repo.get("owner")), str(repo.get("language")), str(event["type"]), str(repo.get("name")), str(
    -
    repo.get("url"))
    -
    userinfo.append(info)
    -
    -
    return userinfo
    -
    -
    def build_db_with_gzip():
    -
    init_db()
    -
    conn = sqlite3.connect('userdata.db')
    -
    c = conn.cursor()
    -
    -
    year = 2014
    -
    month = 3
    -
    -
    for day in range(1,31):
    -
    date_re = re.compile(r"([0-9]{4})-([0-9]{2})-([0-9]{2})-([0-9]+)\.json.gz")
    -
    -
    fn_template = os.path.join("march",
    -
    "{year}-{month:02d}-{day:02d}-{n}.json.gz")
    -
    kwargs = {"year": year, "month": month, "day": day, "n": "*"}
    -
    filenames = glob.glob(fn_template.format(**kwargs))
    -
    -
    for filename in filenames:
    -
    c.executemany('INSERT INTO userinfo VALUES (?,?,?,?,?)', handle_gzip_file(filename))
    -
    -
    conn.commit()
    -
    c.close()
    +

    executemany可以插入多条数据,对于我们的数据来说,一小时的文件大概有五六千个会符合我们上面的安装,也就是有actor又有type才是我们需要记录的数据,我们只需要统计用户的那些事件,而非全部的事件。

    我们需要去遍历文件,然后找到合适的部分,这里只是要找2014-03-012014-03-31的全部事件,而光这些数据的gz文件就有1.26G,同上面那些解压为json文件显得不合适,只能用遍历来处理。

    这里参考了osrc项目中的写法,或者说直接复制过来。

    首先是正规匹配

    -
    date_re = re.compile(r"([0-9]{4})-([0-9]{2})-([0-9]{2})-([0-9]+)\.json.gz")
    +

    不过主要的还是在于glob.glob

    glob是python自己带的一个文件操作相关模块,用它可以查找符合自己目的的文件,就类似于Windows下的文件搜索,支持通配符操作。

    @@ -2313,24 +2338,24 @@ Set up your git name and email, this is important so that your commits can be id

    最后代码可以见

    github.com/gmszone/ml

    更好的方案?

    -

    ###Redis

    +

    Redis

    查询用户事件总数

    -
    import redis
    -
    r = redis.StrictRedis(host='localhost', port=6379, db=0)
    -
    pipe = pipe = r.pipeline()
    -
    pipe.zscore('osrc:user',"gmszone")
    -
    pipe.execute()
    +

    系统返回了227.0,试试别人。

    -
    >>> pipe.zscore('osrc:user',"dfm")
    -
    <redis.client.StrictPipeline object at 0x104fa7f50>
    -
    >>> pipe.execute()
    -
    [425.0]
    -
    >>>
    +

    看看主要是在哪一天提交的

    -
    >>> pipe.hgetall('osrc:user:gmszone:day')
    -
    <redis.client.StrictPipeline object at 0x104fa7f50>
    -
    >>> pipe.execute()
    -
    [{'1': '51', '0': '41', '3': '17', '2': '34', '5': '28', '4': '22', '6': '34'}]
    +

    结果大致如下图所示:

    SMTWTFS
    SMTWTFS
    @@ -2346,36 +2371,36 @@ Set up your git name and email, this is important so that your commits can be id

    蓝色的就是push事件,黄色的是create等等。

    到这里我们算是知道了OSRC的数据库部分是如何工作的。

    -

    ####Redis 查询

    +

    Redis 查询

    主要代码如下所示

    -
    def get_vector(user, pipe=None):
    -
    -
    r = redis.StrictRedis(host='localhost', port=6379, db=0)
    -
    no_pipe = False
    -
    if pipe is None:
    -
    pipe = pipe = r.pipeline()
    -
    no_pipe = True
    -
    -
    user = user.lower()
    -
    pipe.zscore(get_format("user"), user)
    -
    pipe.hgetall(get_format("user:{0}:day".format(user)))
    -
    pipe.zrevrange(get_format("user:{0}:event".format(user)), 0, -1,
    -
    withscores=True)
    -
    pipe.zcard(get_format("user:{0}:contribution".format(user)))
    -
    pipe.zcard(get_format("user:{0}:connection".format(user)))
    -
    pipe.zcard(get_format("user:{0}:repo".format(user)))
    -
    pipe.zcard(get_format("user:{0}:lang".format(user)))
    -
    pipe.zrevrange(get_format("user:{0}:lang".format(user)), 0, -1,
    -
    withscores=True)
    -
    -
    if no_pipe:
    -
    return pipe.execute()
    +

    结果在上一篇中显示出来了,也就是

    [227.0, {'1': '51', '0': '41', '3': '17', '2': '34', '5': '28', '4': '22', '6': '34'}, [('PushEvent', 154.0), ('CreateEvent', 41.0), ('WatchEvent', 18.0), ('GollumEvent', 8.0), ('MemberEvent', 3.0), ('ForkEvent', 2.0), ('ReleaseEvent', 1.0)], 0, 0, 0, 11, [('CSS', 74.0), ('JavaScript', 60.0), ('Ruby', 12.0), ('TeX', 6.0), ('Python', 6.0), ('Java', 5.0), ('C++', 5.0), ('Assembly', 5.0), ('C', 3.0), ('Emacs Lisp', 2.0), ('Arduino', 2.0)]]

    有意思的是在这里生成了和自己相近的人

    ['alesdokshanin', 'hjiawei', 'andrewreedy', 'christj6', '1995eaton']

    osrc最有意思的一部分莫过于flann,当然说的也是系统后台的设计的一个很关键及有意思的部分。

    -

    ##邻近算法与相似用户

    +

    邻近算法与相似用户

    邻近算法是在这个分析过程中一个很有意思的东西。

    邻近算法,或者说K最近邻(kNN,k-NearestNeighbor)分类算法可以说是整个数据挖掘分类技术中最简单的方法了。所谓K最近邻,就是k个最近的邻居的意思,说的是每个样本都可以用她最接近的k个邻居来代表。

    @@ -2401,47 +2426,47 @@ Set up your git name and email, this is important so that your commits can be id
  • 最多的语言
  • osrc中用于解析的代码

    -
    def parse_vector(results):
    -
    points = np.zeros(nvector)
    -
    total = int(results[0])
    -
    -
    points[0] = 1.0 / (total + 1)
    -
    -
    # Week means.
    -
    for k, v in results[1].iteritems():
    -
    points[1 + int(k)] = float(v) / total
    -
    -
    # Event types.
    -
    n = 8
    -
    for k, v in results[2]:
    -
    points[n + evttypes.index(k)] = float(v) / total
    -
    -
    # Number of contributions, connections and languages.
    -
    n += nevts
    -
    points[n] = 1.0 / (float(results[3]) + 1)
    -
    points[n + 1] = 1.0 / (float(results[4]) + 1)
    -
    points[n + 2] = 1.0 / (float(results[5]) + 1)
    -
    points[n + 3] = 1.0 / (float(results[6]) + 1)
    -
    -
    # Top languages.
    -
    n += 4
    -
    for k, v in results[7]:
    -
    if k in langs:
    -
    points[n + langs.index(k)] = float(v) / total
    -
    else:
    -
    # Unknown language.
    -
    points[-1] = float(v) / total
    -
    -
    return points
    +

    这样也就返回我们需要的点数,然后我们可以用get_points来获取这些

    -
    def get_points(usernames):
    -
    r = redis.StrictRedis(host='localhost', port=6379, db=0)
    -
    pipe = r.pipeline()
    -
    -
    results = get_vector(usernames)
    -
    points = np.zeros([len(usernames), nvector])
    -
    points = parse_vector(results)
    -
    return points
    +

    就会得到我们的相应的数据,接着找找和自己邻近的,看看结果。

    [ 0.01298701  0.19736842  0.          0.30263158  0.21052632  0.19736842
         0.          0.09210526  0.          0.22368421  0.01315789  0.          0.
    @@ -2510,97 +2535,97 @@ Set up your git name and email, this is important so that your commits can be id
     
  • https://github.com/cujojs/when
  • 但是显然,他们都太重了。事实上,对于一个库来说,80%的人只需要其中20%的代码。于是,找到了https://github.com/stackp/promisejs,看了看用法,这就是我们需要的功能:

    -
    function late(n) {
    -
    var p = new promise.Promise();
    -
    setTimeout(function() {
    -
    p.done(null, n);
    -
    }, n);
    -
    return p;
    -
    }
    -
    -
    late(100).then(
    -
    function(err, n) {
    -
    return late(n + 200);
    -
    }
    -
    ).then(
    -
    function(err, n) {
    -
    return late(n + 300);
    -
    }
    -
    ).then(
    -
    function(err, n) {
    -
    return late(n + 400);
    -
    }
    -
    ).then(
    -
    function(err, n) {
    -
    alert(n);
    -
    }
    -
    );
    +

    接着打开看看Promise对象,有我们需要的功能,但是又有一些功能超出我的需求。接着把自己不需要的需求去掉,这里函数最后就变成了

    -
    function Promise() {
    -
    this._callbacks = [];
    -
    }
    -
    -
    Promise.prototype.then = function(func, context) {
    -
    var p;
    -
    if (this._isdone) {
    -
    p = func.apply(context, this.result);
    -
    } else {
    -
    p = new Promise();
    -
    this._callbacks.push(function () {
    -
    var res = func.apply(context, arguments);
    -
    if (res && typeof res.then === 'function') {
    -
    res.then(p.done, p);
    -
    }
    -
    });
    -
    }
    -
    return p;
    -
    };
    -
    -
    Promise.prototype.done = function() {
    -
    this.result = arguments;
    -
    this._isdone = true;
    -
    for (var i = 0; i < this._callbacks.length; i++) {
    -
    this._callbacks[i].apply(null, arguments);
    -
    }
    -
    this._callbacks = [];
    -
    };
    -
    -
    var promise = {
    -
    Promise: Promise
    -
    };
    +

    需要注意的是: License,不同的软件有不同的License,如MIT、GPL等等。最好能在遵循协议的情况下,使用别人的代码。

    实现第二个需求

    由于已经有了现有的很多库,所以就可以直接参照(抄)别人写的代码。

    -
    Lettuce.get = function (url, callback) {
    -
    Lettuce.send(url, 'GET', callback);
    -
    };
    -
    -
    Lettuce.load = function (url, callback) {
    -
    Lettuce.send(url, 'GET', callback);
    -
    };
    -
    -
    Lettuce.post = function (url, data, callback) {
    -
    Lettuce.send(url, 'POST', callback, data);
    -
    };
    -
    -
    Lettuce.send = function (url, method, callback, data) {
    -
    data = data || null;
    -
    var request = new XMLHttpRequest();
    -
    if (callback instanceof Function) {
    -
    request.onreadystatechange = function () {
    -
    if (request.readyState === 4 && (request.status === 200 || request.status === 0)) {
    -
    callback(request.responseText);
    -
    }
    -
    };
    -
    }
    -
    request.open(method, url, true);
    -
    if (data instanceof Object) {
    -
    data = JSON.stringify(data);
    -
    request.setRequestHeader('Content-Type', 'application/json');
    -
    }
    -
    request.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
    -
    request.send(data);
    -
    };
    +

    如何以“正确的姿势”阅读开源软件代码

    所有让你直接看最新源码的文章都是在扯淡,你应该从“某个版本”开始阅读代码。

    @@ -2790,7 +2815,7 @@ Set up your git name and email, this is important so that your commits can be id

    代码: https://github.com/phodal/gmap-solr

    技能树

    这个可以从两部分说起:

    -

    ####重构Skill Tree

    +

    重构 Skill Tree

    原来的是

    • Knockout
    • @@ -2802,7 +2827,7 @@ Set up your git name and email, this is important so that your commits can be id Skill Tree
      Skill Tree

    代码: https://github.com/phodal/skillock

    -

    ####技能树Sherlock

    +

    技能树Sherlock