,) to ('http://127.0.0.1:5000/login',)
AT LINE: login.twill:7
Note: submit is using submit button: name="login", value="登入"
AT LINE: login.twill:9
==> at http://127.0.0.1:5000/login
--
1 of 1 files SUCCEEDED.
一个成功的测试诞生了。
##Fake Server
实践了一下怎么用sinon去fake server,还没用respondWith,于是写一下。
这里需要用到sinon框架来测试。
当我们fetch的时候,我们就可以返回我们想要fake的结果。
var data = {"id":1,"name":"Rice","type":"Good","price":12,"quantity":1,"description":"Made in China"};
beforeEach(function() {
this.server = sinon.fakeServer.create();
this.rices = new Rices();
this.server.respondWith(
"GET",
"http://localhost:8080/all/rice",
[
200,
{"Content-Type": "application/json"},
JSON.stringify(data)
]
);
});
于是在afterEach的时候,我们需要恢复这个server。
afterEach(function() {
this.server.restore();
});
接着写一个jasmine测试来测试
describe("Collection Test", function() {
it("should get data from the url", function() {
this.rices.fetch();
this.server.respond();
var result = JSON.parse(JSON.stringify(this.rices.models[0]));
expect(result["id"])
.toEqual(1);
expect(result["price"])
.toEqual(12);
expect(result)
.toEqual(data);
});
});
#重构
或许你应该知道了,重构是怎样的,你也知道重构能带来什么。在我刚开始学重构和设计模式的时候,我需要去找一些好的示例,以便于我更好的学习。有时候不得不创造一些更好的场景,来实现这些功能。
有一天,我发现当我需要我一次又一次地重复讲述某些内容,于是我就计划着把这些应该掌握的技能放到Github上,也就有了[Artisan Stack](https://github.com/artisanstack) 计划。
每个程序员都不可避免地是一个Coder,一个没有掌握好技能的Coder,算不上是手工艺人,但是是手工人。
艺,需要有创造性的方法。
#[前端技能训练: 重构一](http://www.phodal.com/blog/frontend-improve-refactor-javascript-code/)
##为什么重构?
> 为了更好的代码。
在经历了一年多的工作之后,我平时的主要工作就是修Bug。刚开始的时候觉得无聊,后来才发现修Bug需要更好的技术。有时候你可能要面对着一坨一坨的代码,有时候你可能要花几天的时间去阅读代码。而,你重写那几十代码可能只会花上你不到一天的时间。但是如果你没办法理解当时为什么这么做,你的修改只会带来更多的bug。修Bug,更多的是维护代码。还是前人总结的那句话对:
> 写代码容易,读代码难。
假设我们写这些代码只要半天,而别人读起来要一天。为什么不试着用一天的时候去写这些代码,让别人花半天或者更少的时间来理解。
如果你的代码已经上线,虽然是一坨坨的。但是不要轻易尝试,``没有测试的重构``。
从前端开始的原因在于,写得一坨坨且最不容易测试的代码都在前端。
让我们来看看我们的第一个训练,相当有挑战性。
##重构uMarkdown
代码及setup请见github: [js-refactor](https://github.com/artisanstack/js-refactor)
###代码说明
``uMarkdown``是一个用于将Markdown转化为HTML的库。代码看上去就像一个很典型的过程代码:
```javascript
/* code */
while ((stra = micromarkdown.regexobject.code.exec(str)) !== null) {
str = str.replace(stra[0], '\n' + micromarkdown.htmlEncode(stra[1]).replace(/\n/gm, '
').replace(/\ /gm, ' ') + '\n');
}
/* headlines */
while ((stra = micromarkdown.regexobject.headline.exec(str)) !== null) {
count = stra[1].length;
str = str.replace(stra[0], '' + stra[2] + '' + '\n');
}
/* mail */
while ((stra = micromarkdown.regexobject.mail.exec(str)) !== null) {
str = str.replace(stra[0], '' + stra[1] + '');
}
```
选这个做重构的开始,不仅仅是因为之前在写[EchoesWorks](https://github.com/phodal/echoesworks)的时候进行了很多的重构。而且它更适合于,``重构到设计模式``的理论。让我们在重构完之后,给作者进行pull request吧。
Markdown的解析过程,有点类似于``Pipe and Filters``模式(架构模式)。
Filter即我们在代码中看到的正规表达式集:
```javascript
regexobject: {
headline: /^(\#{1,6})([^\#\n]+)$/m,
code: /\s\`\`\`\n?([^`]+)\`\`\`/g
```
他会匹配对应的Markdown类型,随后进行替换和处理。而``str```,就是管理口的输入和输出。
接着,我们就可以对其进行简单的重构。
###重构
(ps: 推荐用WebStrom来做重构,自带重构功能)
作为一个示例,我们先提出codeHandler方法,即将上面的
```javascript
/* code */
while ((stra = micromarkdown.regexobject.code.exec(str)) !== null) {
str = str.replace(stra[0], '\n' + micromarkdown.htmlEncode(stra[1]).replace(/\n/gm, '
').replace(/\ /gm, ' ') + '\n');
}
```
提取方法成
```javascript
codeFilter: function (str, stra) {
return str.replace(stra[0], '\n' + micromarkdown.htmlEncode(stra[1]).replace(/\n/gm, '
').replace(/\ /gm, ' ') + '\n');
},
```
while语句就成了
```javascript
while ((stra = regexobject.code.exec(str)) !== null) {
str = this.codeFilter(str, stra);
}
```
然后,运行所有的测试。
```
grunt test
```
同理我们就可以``mail``、``headline``等方法进行重构。接着就会变成类似于下面的代码,
```javascript
/* code */
while ((execStr = regExpObject.code.exec(str)) !== null) {
str = codeHandler(str, execStr);
}
/* headlines */
while ((execStr = regExpObject.headline.exec(str)) !== null) {
str = headlineHandler(str, execStr);
}
/* lists */
while ((execStr = regExpObject.lists.exec(str)) !== null) {
str = listHandler(str, execStr);
}
/* tables */
while ((execStr = regExpObject.tables.exec(str)) !== null) {
str = tableHandler(str, execStr, strict);
}
```
然后你也看到了,上面有一堆重复的代码,接着让我们用JavaScript的``奇技浮巧``,即apply方法,把上面的重复代码变成。
```javascript
['code', 'headline', 'lists', 'tables', 'links', 'mail', 'url', 'smlinks', 'hr'].forEach(function (type) {
while ((stra = regexobject[type].exec(str)) !== null) {
str = that[(type + 'Handler')].apply(that, [stra, str, strict]);
}
});
```
进行测试,blabla,都是过的。
```javascript
Markdown
✓ should parse h1~h3
✓ should parse link
✓ should special link
✓ should parse font style
✓ should parse code
✓ should parse ul list
✓ should parse ul table
✓ should return correctly class name
```
快来试试吧, [https://github.com/artisanstack/js-refactor](https://github.com/artisanstack/js-refactor)
是时候讨论这个Refactor利器了,最初看到这个重构的过程是从ThoughtWorks郑大晔校开始的,只是之前对于Java的另外一个编辑器Eclipse的坏感。。这些在目前已经不是很重要了,试试这个公司里面应用广泛的编辑器。
##Interllij Idea重构
开发的流程大致就是这样子的,测试先行算是推荐的。
编写测试->功能代码->修改测试->重构
上次在和buddy聊天的时候,才知道测试在功能简单的时候是后行的,在功能复杂不知道怎么手手的时候是先行的。
开始之前请原谅我对于Java语言的一些无知,然后,看一下我写的Main函数:
```java
package com.phodal.learing;
public class Main {
public static void main(String[] args) {
int c=new Cal().add(1,2);
int d=new Cal2().sub(2,1);
System.out.println("Hello,s");
System.out.println(c);
System.out.println(d);
}
}
```
代码写得还好(自我感觉),先不管Cal和Cal2两个类。大部分都能看懂,除了c,d不知道他们表达的是什么意思,于是。
###Rename
**快捷键:Shift+F6**
**作用:重命名**
- 把光标丢到int c中的c,按下shift+f6,输入result_add
- 把光标移到int d中的d,按下shift+f6,输入result_sub
于是就有
```java
package com.phodal.learing;
public class Main {
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);
}
}
```
###Extract Method
**快捷键:alt+command+m**
**作用:扩展方法**
- 选中System.out.println(result_add);
- 按下alt+command+m
- 在弹出的窗口中输入mprint
于是有了
```java
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**
**作用:内联方法**
- 选中main中的mprint
- alt+command+n
- 选中Inline all invocations and remove the method(2 occurrences) 点确定
然后我们等于什么也没有做了~~:
```java
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类:
```java
public class Cal2 extends Cal {
public int sub(int a,int b){
return a-b;
}
}
```
以及Cal2的父类Cal
```java
public class Cal {
public int add(int a,int b){
return a+b;
}
}
```
最后的结果,就是将Cal2类中的sub方法,提到父类:
```java
public class Cal {
public int add(int a,int b){
return a+b;
}
public int sub(int a,int b){
return a-b;
}
}
```
而我们所要做的就是鼠标右键
###重构之以查询取代临时变量
快捷键
Mac: 木有
Windows/Linux: 木有
或者: ``Shift``+``alt``+``command``+``T`` 再选择 ``Replace Temp with Query``
鼠标: **Refactor** | ``Replace Temp with Query``
####重构之前
过多的临时变量会让我们写出更长的函数,函数不应该太多,以便使功能单一。这也是重构的另外的目的所在,只有函数专注于其功能,才会更容易读懂。
以书中的代码为例
```java
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``很愉快地拿鼠标点上面的重构

便会返回
```java
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. 选中
_quantity * _itemPrice
2. 对其进行``Extrace Method``
3. 选择``basePrice``再``Inline Method``
####Intellij IDEA重构
在Intellij IDEA的文档中对此是这样的例子
```java
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
便会有下面的结果:
```javas
import java.lang.String;
public class replaceTemp {
public void method() {
String str = "str";
System.out.println(aString(str));
}
private String aString(String str) {
return returnString().concat(str);
}
}
```
#Github连击
##100天
我也是蛮拼的,虽然我想的只是在Github上连击100~200天,然而到了今天也算不错。

``在停地造轮子的过程中,也不停地造车子。``
在那篇连续冲击365天的文章出现之前,我们公司的大大([https://github.com/dreamhead](https://github.com/dreamhead))也曾经在公司内部说过,天天commit什么的。当然这不是我的动力,在连击140天之前
- 给过google的``ngx_speed``、``node-coap``等项目创建过pull request
- 也有``free-programming-books``、``free-programming-books-zh_CN``这样的项目。
- 当然还有一个连击20天。
对比了一下365天连击的commit,我发现我在total上整整多了近0.5倍。

同时这似乎也意味着,我每天的commit数与之相比多了很多。
在连击20的时候,有这样的问题: *为了commit而commit代码*,最后就放弃了。
而现在是``为了填坑而commit``,为自己挖了太多的想法。
###40天的提升
当时我需要去印度接受毕业生培训,大概有5周左右,想着总不能空手而归。于是在国庆结束后有了第一次commit,当时旅游归来,想着自己在不同的地方有不同的照片,于是这个repo的名字是 [onmap](https://github.com/phodal/onmap)——将自己的照片显示在地图上的拍摄地点(手机是Lumia 920)。然而,中间因为修改账号的原因,丢失了commit。
再从印度说起,当时主要维护三个repo:
- 物联网的CoAP协议
- [一步步设计物联网系统](https://github.com/phodal/designiot)的电子书
- 一个Node.js + JS的网站
说说最后一个,最后一个是练习的项目。因为当时培训比较无聊,业余时间比较多,英语不好,加上听不懂印度人的话。晚上基本上是在住的地方默默地写代码,所以当时的目标有这么几个:
- TDD
- 测试覆盖率
- 代码整洁
这也就是为什么那个repo有这样的一行:

做到98%的覆盖率也算蛮拼的,当然还有Code Climate也达到了4.0,也有了112个commits。因此也带来了一些提高:
- 提高了代码的质量(code climate比jslint更注重重复代码等等一些bad smell)。
- 对于Mock、Stub、FakesServer等用法有更好的掌握
- 可以持续地交付软件(版本管理、自动测试、CI、部署等等)
###100天的挑战
(ps:从印度回来之后,由于女朋友在泰国实习,有了更多的时间可以看书、写代码)
有意思的是越到中间的一些时间,commits的次数上去了,除了一些简单的pull request,还有一些新的轮子出现了。

这是上一星期的commits,这也就意味着,在一星期里面,我需要在8个repo里切换。而现在我又有了一个新的idea,这时就发现了一堆的问题:
- 今天工作在这个repo上,突然发现那个repo上有issue,需要去修复,于是就放下了当前的代码。
- 在不同的repo间切换容易分散精力
- 很容易就发现有太多的功能可以实现,但是时间是有限的。
- 没有足够的空闲时间,除了周末。
- 希望去寻找那些有兴趣的人,然而却发现原来没有那么多时间去找人。
###140天的希冀
在经历了100天之后,似乎整个人都轻松了,毕竟目标是100~200天。似乎到现在,也不会有什么特殊的情怀,除了一些希冀。
当然,对于一个开源项目的作者来说,最好有下面的情况:
- 很多人知道了这个项目
- 很多人用它的项目。
- 在某些可以用这个项目快速解决问题的地方提到了这个项目
- 提了bug、issue、问题。
- 提了bug,并解决了。(ps:这是最理想的情况)
##200天的Showcase
今天是我连续泡在Github上的第200天,也是蛮高兴的,终于到达了:

故事的背影是: 去年国庆完后要去印度接受毕业生培训——就是那个神奇的国度。但是在去之前已经在项目待了九个多月,项目上的挑战越来越少,在印度的时间又算是比较多。便给自己设定了一个长期的goal,即100~200天的longest streak。
或许之前你看到过一篇文章[让我们连击](https://github.com/phodal/github-roam/blob/master/chapters/12-streak-your-github.md),那时已然140天,只是还是浑浑噩噩。到了今天,渐渐有了一个更清晰地思路。
先让我们来一下ShowCase,然后再然后,下一篇我们再继续。
###一些项目简述
上面说到的培训一开始是用Java写的一个网站,有自动测试、CI、CD等等。由于是内部组队培训,代码不能公开等等因素,加之做得无聊。顺手,拿Node.js +RESTify 做了Server,Backbone + RequireJS + jQuery 做了前台的逻辑。于是在那个日子里,也在维护一些旧的repo,如[iot-coap](https://github.com/phodal/iot-coap)、[iot](https://github.com/phodal/iot),前者是我拿到WebStorm开源License的Repo,后者则是毕业设计。
对于这样一个项目也需要有测试、自动化测试、CI等等。CI用的是Travics-CI。总体的技术构架如下:
####技术栈
前台:
- Backbone
- RequireJS
- Underscore
- Mustache
- Pure CSS
后台:
- RESTify
测试:
- Jasmine
- Chai
- Sinon
- Mocha
- Jasmine-jQuery
一直写到五星期的培训结束, 只是没有自动部署。想想就觉得可以用github-page的项目多好~~。
过程中还有一些有意思的小项目,如:
###google map solr polygon 搜索
[google map solr polygon 搜索](http://www.phodal.com/blog/google-map-width-solr-use-polygon-search/)

代码: [https://github.com/phodal/gmap-solr](https://github.com/phodal/gmap-solr)
###技能树
这个可以从两部分说起:
####重构Skill Tree
原来的是
- Knockout
- RequireJS
- jQuery
- Gulp

代码: [https://github.com/phodal/skillock](https://github.com/phodal/skillock)
####技能树Sherlock
- D3.js
- Dagre-D3.js
- jquery.tooltipster.js
- jQuery
- Lettuce
- Knockout.js
- Require.js

代码: [https://github.com/phodal/sherlock](https://github.com/phodal/sherlock)
####Django Ionic ElasticSearch 地图搜索

- ElasticSearch
- Django
- Ionic
- OpenLayers 3
代码: [https://github.com/phodal/django-elasticsearch](https://github.com/phodal/django-elasticsearch)
####简历生成器

- React
- jsPDF
- jQuery
- RequireJS
- Showdown
代码: [https://github.com/phodal/resume](https://github.com/phodal/resume)
####Nginx 大数据学习

- ElasticSearch
- Hadoop
- Pig
代码: [https://github.com/phodal/learning-data/tree/master/nginx](https://github.com/phodal/learning-data/tree/master/nginx)
####其他
虽然技术栈上主要集中在Python、JavaScript,当然还有一些Ruby、Pig、Shell、Java的代码,只是我还是习惯用Python和JavaScript。一些用到觉得不错的框架:
- Ionic: 开始Hybird移动应用。
- Django: Python Web开发利器。
- Flask: Python Web开发小刀。
- RequireJS: 管理js依赖。
- Backbone: Model + View + Router。
- Angluar: ...。
- Knockout: MVV*。
- React: 据说会火。
- Cordova: Hybird应用基础。
还应该有:
- ElasticSearch
- Solr
- Hadoop
- Pig
- MongoDB
- Redis
##365天
给你一年的时间,你会怎样去提高你的水平???

正值这难得的sick leave(万恶的空气),码文一篇来记念一个过去的366天里。尽管想的是在今年里写一个可持续的开源框架,但是到底这依赖于一个好的idea。在我的[Github 孵化器](http://github.com/phodal/ideas) 页面上似乎也没有一个特别让我满意的想法,虽然上面有各种不样有意思的ideas。多数都是在过去的一年是完成的,然而有一些也是还没有做到的。
尽管一直在Github上连击看上去似乎是没有多大必要的,但是人总得有点追求。如果正是漫无目的,却又想着提高技术的同时,为什么不去试试?毕竟技术非常好、不需要太多练习的人只是少数,似乎这样的人是不存在的。大多数的人都是经过练习之后,才会达到别人口中的“技术好”。
这让我想起了充斥着各种气味的知乎上的一些问题,在一些智商被完虐的话题里,无一不是因为那些人学得比别人早——哪来的天才?所谓的天才,应该是未来的智能生命一般,一出生什么都知道。如果并非如此,那只是说明他练习到位了。
练习不到位便意味着,即使你练习的时候是一万小时的两倍,那也是无济于事的。如果你学得比别人晚,在**很长的一段时间里**(可能直到进棺材)输给别人是必然的——落后就要挨打。就好像我等毕业于一所二本垫底的学校里,如果在过去我一直保持着和别人(各种重点)一样的学习速度,那么我只能一直是Loser。
需要注意的是,对你来说考上二本很难,并不是因为你比别人笨。教育资源分配不均的问题,在某种程度上导致了新的阶级制度的出现。如[我的首页](https://www.phodal.com/)说的那样: **THE ONLY FAIR IS NOT FAIR**——唯一公平的是它是不公平的。我们可以做的还有很多——**CREATE & SHARE**。真正的不幸是,因为营养不良导致的教育问题。
于是在想明白了很多事的时候起,便有了Re-Practise这样的计划,而365天只是中间的一个产物。
###编程的基础能力
虽说算法很重要,但是编码才是基础能力。算法与编程在某种程度上是不同的领域,算法编程是在编程上面的一级。算法写得再好,如果别人很难直接拿来复用,在别人眼里就是shit。想出能work的代码一件简单的事,学会对其重构,使之变得更易读就是一件有意义的事。
于是,在某一时刻在Github上创建了一个组织,叫[Artisan Stack](https://github.com/artisanstack)。当时想的是在Github寻找一些JavaScript项目,对其代码进行重构。但是到底是影响力不够哈,参与的人数比较少。
####重构
如果你懂得如何写出高可读的代码,那么我想你是不需要这个的,但是这意味着你花了更多的时候在思考上了。当谈论重构的时候,让我想起了TDD(测试驱动开发)。即使不是TDD,那么如果你写着测试,那也是可以重构的。(之前写过一些利用Intellij IDEA重构的文章:[提炼函数](https://www.phodal.com/blog/intellij-idea-refactor-extract-method/)、[以查询取代临时变量](https://www.phodal.com/blog/intellij-idea-refactor-replace-temp-with-query/)、[重构与Intellij Idea初探](https://www.phodal.com/blog/thoughtworks-refactor-and-intellij-idea/)、[内联函数](https://www.phodal.com/blog/intellij-idea-refactor-inline-method/))
在各种各样的文章里,我们看到过一些相关的内容,最好的参考莫过于《重构》一书。最基础不过的原则便是函数名,取名字很难,取别人能读懂的名字更难。其他的便有诸如长函数、过大的类、重复代码等等。在我有限的面试别人的经历里,这些问题都是最常见的。
####测试
而如果没有测试,其他都是扯淡。写好测试很难,写个测试算是一件容易的事。只是有些容易我们会为了测试而测试。
在我写[EchoesWorks](https://github.com/echoesworks/echoesworks)和[Lan](https://github.com/phodal/lan)的过程中,我尽量去保证足够高的测试覆盖率。


从测试开始的TDD,会保证方法是可测的。从功能到测试则可以提供工作次效率,但是只会让测试成为测试,而不是代码的一部分。
测试是代码的最后一公里。所以,尽可能的为你的Github上的项目添加测试。
####编码的过程
初到TW时,Pair时候总会有人教我如何开始编码,这应该也是一项基础的能力。结合日常,重新演绎一下这个过程:
1. 有一个可衡量、可实现、过程可测的目标
2. Tasking (即对要实现的目标过程进行分解)
3. 一步步实现 (如TDD)
4. 实现目标
放到当前的场景就是:
1. 我想在Github上连击365天。对应于每一个时候段的目标都应该是可以衡量、测试的——即每天都会有Contributions。
2. 分解就是一个痛苦的过程。理想情况下,我们应该会有每天提交,但是这取决于你的repo的数量,如果没有新的idea出现,那么这个就变成为了Contributions而Commit。
3. 一步步实现
在我们实际工作中也是如此,接到一个任务,然后分解,一步步完成。不过实现会稍微复杂一些,因为事务总会有抢占和优先级的。
###技术与框架设计
在上上一篇博客中《[After 500: 写了第500篇博客,然后呢?](https://www.phodal.com/blog/after-500-blogposts-analytics-after-tech/)》也深刻地讨论了下这个问题,技术向来都是后发者优势。对于技术人员来说,也是如此,后发者占据很大的优势。
如果我们只是单纯地把我们的关注点仅仅放置于技术上,那么我们就不具有任何的优势。而依赖于我们的编程经验,我们可以在特定的时候创造一些框架。而架构的设计本身就是一件有意思的事,大抵是因为程序员都喜欢创造。(ps:之前曾经写过这样一篇文章,《[对不起,我并不热爱编程,我只喜欢创造](https://www.phodal.com/blog/sorry-i-don't-like-programming/)》)
**创造是一种知识的再掌握过程。**
回顾一下写echoesworks的过程,一开始我需要的是一个网页版的PPT,当然这类的东西已经有很多了,如impress.js、bespoke.js等等。分析一下所需要的功能:markdown解析器、键盘事件处理、Ajax、进度条显示、图片处理、Slide。我们可以在Github上找到各式各样的模块,我们所要做的就是将之结合在一样。在那之前,我试着用类似的原理写(组合)了[Lettuce](https://github.com/phodal/lettuce)。
组合相比于创造过程是一个更有挑战性的过程,我们需要在这过程去设计胶水来粘合这些代码,并在最终可以让他工作。这好比是我们在平时接触到的任务划分,每个人负责相应的模块,最后整合。
想似的我在写[lan](https://github.com/phodal/lan)的时候,也是类似的,但是不同的是我已经设计了一个清晰的架构图。

而在我们实现的编码过程也是如此,使用不同的框架,并且让他们能工作。如早期玩的[moqi.mobi](https://github.com/echoesworks/moqi.mobi),基于Backbone、RequireJS、Underscore、Mustache、Pure CSS。在随后的时间里,用React替换了View层,就有了[backbone-react](https://github.com/phodal/backbone-react)的练习。
技术同人一样,需要不断地往高一级前进。我们只需要不断地Re-Practise。
###领域与练习
说业务好像不太适合程序员的口味,那就领域吧。不同行业的人,如百度、阿里、腾讯,他们的领域核心是不一样的。
而领域本身也是相似的,这可以解释为什么互联网公司都喜欢互相挖人,而一般都不会去华为、中兴等非互联网领域挖人。出了这个领域,你可能连个毕业生都不如。领域、业务同技术一样是不断强化知识的一个过程。Ritchie先实现了BCPL语言,而后设计了C语言,而BCPL语言一开始是基于CPL语言。
领域本身也在不断进化。
这也是下一个值得提高的地方。
###其他
是时候写这个小结了。从不会写代码,到写代码是从0到1的过程,但是要从1到60都不是一件容易的事。无论是刷Github也好(不要是自动提交),或者是换工作也好,我们都在不断地练习。
而练习是要分成不同的几个步骤,不仅仅局限于技术:
1. 编码
2. 架构
3. 设计
4. 。。。
#如何在Github"寻找灵感(fork)"
> 重造轮子是重新创造一个已有的或是已被其他人优化的基本方法。
最近萌发了一个想法写游戏引擎,之前想着做一个JavaScript前端框架。看看,这个思路是怎么来的。
##[Lettuce](https://github.com/phodal/lettuce)构建过程
> Lettuce是一个简约的移动开发框架。
故事的出发点是这样的:``写了很多代码,用的都是框架,最后不知道收获什么了``?事实也是如此,当自己做了一些项目之后,发现最后什么也没有收获到。于是,就想着做一个框架。
###需求
有这样的几个前提
- 为什么我只需要jQuery里的选择器、Ajax要引入那么重的库呢?
- 为什么我只需要一个Template,却想着用Mustache
- 为什么我需要一个Router,却要用Backbone呢?
- 为什么我需要的是一个isObject函数,却要用到整个Underscore?
我想要的只是一个简单的功能,而我不想引入一个庞大的库。换句话说,我只需要不同库里面的一小部分功能,而不是一个库。
实际上想要的是:
> 构建一个库,里面从不同的库里面抽取出不同的函数。
###计划
这时候我参考了一本电子书《Build JavaScript FrameWork》,加上一些平时的需求,于是很快的就知道自己需要什么样的功能:
- Promise 支持
- Class类(ps:没有一个好的类使用的方式)
- Template 一个简单的模板引擎
- Router 用来控制页面的路由
- Ajax 基本的Ajax Get/Post请求
在做一些实际的项目中,还遇到了这样的一些功能支持:
- Effect 简单的一些页面效果
- AMD支持
而我们有一个前提是要保持这个库尽可能的小、同时我们还需要有测试。
###实现第一个需求
简单说说是如何实现一个简单的需求。
####生成框架
因为Yeoman可以生成一个简单的轮廓,所以我们可以用它来生成这个项目的骨架。
- Gulp
- Jasmine
####寻找
在Github上搜索了一个看到了下面的几个结果:
- [https://github.com/then/promise](https://github.com/then/promise)
- [https://github.com/reactphp/promise](https://github.com/reactphp/promise)
- [https://github.com/kriskowal/q](https://github.com/kriskowal/q)
- [https://github.com/petkaantonov/bluebird](https://github.com/petkaantonov/bluebird)
- [https://github.com/cujojs/when](https://github.com/cujojs/when)
但是显然,他们都太重了。事实上,对于一个库来说,80%的人只需要其中20%的代码。于是,找到了[https://github.com/stackp/promisejs](https://github.com/stackp/promisejs),看了看用法,这就是我们需要的功能:
```javascript
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对象,有我们需要的功能,但是又有一些功能超出我的需求。接着把自己不需要的需求去掉,这里函数最后就变成了
```javascript
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等等。最好能在遵循协议的情况下,使用别人的代码。
###实现第二个需求
由于,现有的一些Ajax库都比较,最后只好参照着别人的代码自己实现。
```javascript
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);
};
```
#Github项目分析一
##生成图表
如何分析用户的数据是一个有趣的问题,特别是当我们有大量的数据的时候。除了``matlab``,我们还可以用``numpy``+``matplotlib``
数据可以在这边寻找到
[https://github.com/gmszone/ml](https://github.com/gmszone/ml)
最后效果图

要解析的json文件位于``data/2014-01-01-0.json``,大小6.6M,显然我们可能需要用每次只读一行的策略,这足以解释为什么诸如sublime打开的时候很慢,而现在我们只需要里面的json数据中的创建时间。。
==,这个文件代表什么?
**2014年1月1日零时到一时,用户在github上的操作,这里的用户指的是很多。。一共有4814条数据,从commit、create到issues都有。**
###数据解析
```python
import json
for line in open(jsonfile):
line = f.readline()
```
然后再解析json
```python
import dateutil.parser
lin = json.loads(line)
date = dateutil.parser.parse(lin["created_at"])
```
这里用到了``dateutil``,因为新鲜出炉的数据是string需要转换为``dateutil``,再到数据放到数组里头。最后有就有了``parse_data``
```python
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
```
下面这句代码就是将上面的解析为
```python
minuteswithcount = [(x, dataarray.count(x)) for x in set(dataarray)]
```
这样的数组以便于解析
```python
[(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
```bash
sudo pip install matplotlib
```
然后引入这个库
import matplotlib.pyplot as plt
如上面的那个结果,只需要
plt.figure(figsize=(8,4))
plt.plot(x, y,label = files)
plt.legend()
plt.show()
最后代码可见
```python
#!/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")
```
##每周分析
继上篇之后,我们就可以分析用户的每周提交情况,以得出用户的真正的工具效率,每个程序员的工作时间可能是不一样的,如

这是我的每周情况,显然如果把星期六移到前面的话,随着工作时间的增长,在github上的使用在下降,作为一个
a fulltime hacker who works best in the evening (around 8 pm).
不过这个是osrc的分析结果。
###python github 每周情况分析
看一张分析后的结果

结果正好与我的情况相反?似乎图上是这么说的,但是数据上是这样的情况。
data
├── 2014-01-01-0.json
├── 2014-02-01-0.json
├── 2014-02-02-0.json
├── 2014-02-03-0.json
├── 2014-02-04-0.json
├── 2014-02-05-0.json
├── 2014-02-06-0.json
├── 2014-02-07-0.json
├── 2014-02-08-0.json
├── 2014-02-09-0.json
├── 2014-02-10-0.json
├── 2014-02-11-0.json
├── 2014-02-12-0.json
├── 2014-02-13-0.json
├── 2014-02-14-0.json
├── 2014-02-15-0.json
├── 2014-02-16-0.json
├── 2014-02-17-0.json
├── 2014-02-18-0.json
├── 2014-02-19-0.json
└── 2014-02-20-0.json
我们获取是每天晚上0点时的情况,至于为什么是0点,我想这里的数据量可能会比较少。除去1月1号的情况,就是上面的结果,在只有一周的情况时,总会以为因为在国内那时是假期,但是总觉得不是很靠谱,国内的程序员虽然很多,会在github上活跃的可能没有那么多,直至列出每一周的数据时。
6570, 7420, 11274, 12073, 12160, 12378, 12897,
8474, 7984, 12933, 13504, 13763, 13544, 12940,
7119, 7346, 13412, 14008, 12555
###Python 数据分析
重写了一个新的方法用于计算提交数,直至后面才意识到其实我们可以算行数就够了,但是方法上有点hack
```python
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图表
让我们的matplotlib来做这些图表的工作
```python
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()
```
蓝色的是第一周,绿色的是第二周,蓝色的是第三周就有了上面的结果。
我们还需要优化方法,以及多线程的支持。
#Github项目分析二
让我们分析之前的程序,然后再想办法做出优化。网上看到一篇文章[http://www.huyng.com/posts/python-performance-analysis/](http://www.huyng.com/posts/python-performance-analysis/)讲的就是分析这部分内容的。
##Time Python分析
分析程序的运行时间
```bash
$time python handle.py
```
结果便是,但是对于我们的分析没有一点意义
```
real 0m43.411s
user 0m39.226s
sys 0m0.618s
```
###line_profiler python
```bash
sudo ARCHFLAGS="-Wno-error=unused-command-line-argument-hard-error-in-future" easy_install line_profiler
```
然后在我们的``parse_data.py``的``handle_json``前面加上``@profile``
```python
@profile
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
```
Line_profiler带了一个分析脚本``kernprof.py``,so
```bash
kernprof.py -l -v handle.py
```
我们便会得到下面的结果
```
Wrote profile results to handle.py.lprof
Timer unit: 1e-06 s
File: parse_data.py
Function: handle_json at line 15
Total time: 127.332 s
Line # Hits Time Per Hit % Time Line Contents
==============================================================
15 @profile
16 def handle_json(jsonfile):
17 19 636 33.5 0.0 f = open(jsonfile, "r")
18 19 21 1.1 0.0 dataarray = []
19 19 16 0.8 0.0 datacount = 0
20
21 212373 730344 3.4 0.6 for line in open(jsonfile):
22 212354 2826826 13.3 2.2 line = f.readline()
23 212354 13848171 65.2 10.9 lin = json.loads(line)
24 212354 109427317 515.3 85.9 date = dateutil.parser.parse(lin["created_at"])
25 212354 238112 1.1 0.2 datacount += 1
26 212354 260227 1.2 0.2 dataarray.append(date.minute)
27
28 19 349 18.4 0.0 f.close()
29 19 20 1.1 0.0 return datacount, dataarray
```
于是我们就发现我们的瓶颈就是从读取``created_at``,即创建时间。。。以及解析json,反而不是我们关心的IO,果然``readline``很强大。
###memory_profiler
首先我们需要install memory_profiler:
```bash
$ pip install -U memory_profiler
$ pip install psutil
```
如上,我们只需要在``handle_json``前面加上``@profile``
```bash
python -m memory_profiler handle.py
```
于是
```
Filename: parse_data.py
Line # Mem usage Increment Line Contents
================================================
13 39.930 MiB 0.000 MiB @profile
14 def handle_json(jsonfile):
15 39.930 MiB 0.000 MiB f = open(jsonfile, "r")
16 39.930 MiB 0.000 MiB dataarray = []
17 39.930 MiB 0.000 MiB datacount = 0
18
19 40.055 MiB 0.125 MiB for line in open(jsonfile):
20 40.055 MiB 0.000 MiB line = f.readline()
21 40.066 MiB 0.012 MiB lin = json.loads(line)
22 40.055 MiB -0.012 MiB date = dateutil.parser.parse(lin["created_at"])
23 40.055 MiB 0.000 MiB datacount += 1
24 40.055 MiB 0.000 MiB dataarray.append(date.minute)
25
26 f.close()
27 return datacount, dataarray
```
###objgraph python
安装objgraph
```bash
pip install objgraph
```
我们需要调用他
```python
import pdb;
```
以及在需要调度的地方加上
```python
pdb.set_trace()
```
接着会进入``command``模式
```python
(pdb) import objgraph
(pdb) objgraph.show_most_common_types()
```
然后我们可以找到。。
```
function 8259
dict 2137
tuple 1949
wrapper_descriptor 1625
list 1586
weakref 1145
builtin_function_or_method 1117
method_descriptor 948
getset_descriptor 708
type 705
```
也可以用他生成图形,貌似这里是用``dot``生成的,加上``python-xdot``
很明显的我们需要一个数据库。
如果我们每次都要花同样的时间去做一件事,去扫那些数据的话,那么这是最好的打发时间的方法。
##python SQLite3 查询数据
我们创建了一个名为``userdata.db``的数据库文件,然后创建了一个表,里面有owner,language,eventtype,name url
```python
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)''')
```
接着我们就可以查询数据,这里从结果讲起。
```python
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``的时候,也就是我自己就会有如下的结果
```bash
(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了。
值得注意的是。
```bash
-rw-r--r-- 1 fdhuang staff 905M Apr 12 14:59 userdata.db
```
这个数据库文件有**905M**,不过查询结果相当让人满意,至少相对于原来的结果来说。
Python自带了对SQLite3的支持,然而我们还需要安装SQLite3
```bash
brew install sqlite3
```
或者是
```bash
sudo port install sqlite3
```
或者是Ubuntu的
```bash
sudo apt-get install sqlite3
```
openSUSE自然就是
```bash
sudo zypper install sqlite3
```
不过,用yast2也很不错,不是么。。
###数据导入
需要注意的是这里是需要python2.7,起源于对gzip的上下文管理器的支持问题
```python
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-01``到``2014-03-31``的全部事件,而光这些数据的gz文件就有1.26G,同上面那些解压为json文件显得不合适,只能用遍历来处理。
这里参考了osrc项目中的写法,或者说直接复制过来。
首先是正规匹配
```python
date_re = re.compile(r"([0-9]{4})-([0-9]{2})-([0-9]{2})-([0-9]+)\.json.gz")
```
不过主要的还是在于``glob.glob``
> glob是python自己带的一个文件操作相关模块,用它可以查找符合自己目的的文件,就类似于Windows下的文件搜索,支持通配符操作。
这里也就用上了``gzip.GzipFile``又一个不错的东西。
最后代码可以见
[github.com/gmszone/ml](http://github.com/gmszone/ml)
更好的方案?
##Redis
查询用户事件总数
```python
import redis
r = redis.StrictRedis(host='localhost', port=6379, db=0)
pipe = pipe = r.pipeline()
pipe.zscore('osrc:user',"gmszone")
pipe.execute()
```
系统返回了``227.0``,试试别人。
```bash
>>> pipe.zscore('osrc:user',"dfm")
>>> pipe.execute()
[425.0]
>>>
```
看看主要是在哪一天提交的
```python
>>> pipe.hgetall('osrc:user:gmszone:day')
>>> pipe.execute()
[{'1': '51', '0': '41', '3': '17', '2': '34', '5': '28', '4': '22', '6': '34'}]
```
结果大致如下图所示:

看看主要的事件是?
>>> pipe.zrevrange("osrc:user:gmszone:event".format("gmszone"), 0, -1,withscores=True)
>>> pipe.execute()
[[('PushEvent', 154.0), ('CreateEvent', 41.0), ('WatchEvent', 18.0), ('GollumEvent', 8.0), ('MemberEvent', 3.0), ('ForkEvent', 2.0), ('ReleaseEvent', 1.0)]]
>>>

蓝色的就是push事件,黄色的是create等等。
到这里我们算是知道了OSRC的数据库部分是如何工作的。
###Redis 查询
主要代码如下所示
```python
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个邻居来代表。
换句话说,我们需要一些样本来当作我们的分析资料,这里东西用到的就是我们之前的。
```
[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)]]
```
在代码中是构建了一个points.h5的文件来分析每个用户的points,之后再记录到hdf5文件中。
```
[ 0.00438596 0.18061674 0.2246696 0.14977974 0.07488987 0.0969163
0.12334802 0.14977974 0. 0.18061674 0. 0. 0.
0.00881057 0. 0. 0.03524229 0. 0.
0.01321586 0. 0. 0. 0.6784141 0.
0.07929515 0.00440529 1. 1. 1. 0.08333333
0.26431718 0.02202643 0.05286344 0.02643172 0. 0.01321586
0.02202643 0. 0. 0. 0. 0. 0.
0. 0. 0.00881057 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0.00881057]
```
这里分析到用户的大部分行为,再找到与其行为相近的用户,主要的行为有下面这些:
- 每星期的情况
- 事件的类型
- 贡献的数量,连接以及语言
- 最多的语言
osrc中用于解析的代码
```python
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``来获取这些
```python
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.
0. 0. 0. 0.01315789 0. 0.
0.01315789 0. 0. 0. 0.73684211 0. 0.
0. 1. 1. 1. 0.2 0.42105263
0.09210526 0. 0. 0. 0. 0.23684211
0. 0. 0.03947368 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. ]
```
真看不出来两者有什么相似的地方 。。。。