存储到数据库中

SQLite3

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

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

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

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

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

  1. (u'gmszone', u'ForkEvent', u'RESUME', u'TeX', u'https://github.com/gmszone/RESUME')
  2. (u'gmszone', u'WatchEvent', u'iot-dashboard', u'JavaScript', u'https://github.com/gmszone/iot-dashboard')
  3. (u'gmszone', u'PushEvent', u'wechat-wordpress', u'Ruby', u'https://github.com/gmszone/wechat-wordpress')
  4. (u'gmszone', u'WatchEvent', u'iot', u'JavaScript', u'https://github.com/gmszone/iot')
  5. (u'gmszone', u'CreateEvent', u'iot-doc', u'None', u'https://github.com/gmszone/iot-doc')
  6. (u'gmszone', u'CreateEvent', u'iot-doc', u'None', u'https://github.com/gmszone/iot-doc')
  7. (u'gmszone', u'PushEvent', u'iot-doc', u'TeX', u'https://github.com/gmszone/iot-doc')
  8. (u'gmszone', u'PushEvent', u'iot-doc', u'TeX', u'https://github.com/gmszone/iot-doc')
  9. (u'gmszone', u'PushEvent', u'iot-doc', u'TeX', u'https://github.com/gmszone/iot-doc')
  10. 109

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

值得注意的是。

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

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

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

  1. brew install sqlite3

或者是

  1. sudo port install sqlite3

或者是Ubuntu的

  1. sudo apt-get install sqlite3

openSUSE自然就是

  1. sudo zypper install sqlite3

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

数据导入

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

  1. def handle_gzip_file(filename):
  2. userinfo = []
  3. with gzip.GzipFile(filename) as f:
  4. events = [line.decode("utf-8", errors="ignore") for line in f]
  5. for n, line in enumerate(events):
  6. try:
  7. event = json.loads(line)
  8. except:
  9. continue
  10. actor = event["actor"]
  11. attrs = event.get("actor_attributes", {})
  12. if actor is None or attrs.get("type") != "User":
  13. continue
  14. key = actor.lower()
  15. repo = event.get("repository", {})
  16. info = str(repo.get("owner")), str(repo.get("language")), str(event["type"]), str(repo.get("name")), str(
  17. repo.get("url"))
  18. userinfo.append(info)
  19. return userinfo
  20. def build_db_with_gzip():
  21. init_db()
  22. conn = sqlite3.connect('userdata.db')
  23. c = conn.cursor()
  24. year = 2014
  25. month = 3
  26. for day in range(1,31):
  27. date_re = re.compile(r"([0-9]{4})-([0-9]{2})-([0-9]{2})-([0-9]+)\.json.gz")
  28. fn_template = os.path.join("march",
  29. "{year}-{month:02d}-{day:02d}-{n}.json.gz")
  30. kwargs = {"year": year, "month": month, "day": day, "n": "*"}
  31. filenames = glob.glob(fn_template.format(**kwargs))
  32. for filename in filenames:
  33. c.executemany('INSERT INTO userinfo VALUES (?,?,?,?,?)', handle_gzip_file(filename))
  34. conn.commit()
  35. c.close()

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

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

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

首先是正规匹配

  1. 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

更好的方案?

Redis

查询用户事件总数

  1. import redis
  2. r = redis.StrictRedis(host='localhost', port=6379, db=0)
  3. pipe = pipe = r.pipeline()
  4. pipe.zscore('osrc:user',"gmszone")
  5. pipe.execute()

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

  1. >>> pipe.zscore('osrc:user',"dfm")
  2. <redis.client.StrictPipeline object at 0x104fa7f50>
  3. >>> pipe.execute()
  4. [425.0]
  5. >>>

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

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

结果大致如下图所示:

SMTWTFS SMTWTFS

看看主要的事件是?

  1. >>> pipe.zrevrange("osrc:user:gmszone:event".format("gmszone"), 0, -1,withscores=True)
  2. <redis.client.StrictPipeline object at 0x104fa7f50>
  3. >>> pipe.execute()
  4. [[('PushEvent', 154.0), ('CreateEvent', 41.0), ('WatchEvent', 18.0), ('GollumEvent', 8.0), ('MemberEvent', 3.0), ('ForkEvent', 2.0), ('ReleaseEvent', 1.0)]]
  5. >>>

Main Event Main Event

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

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

Redis 查询

主要代码如下所示

  1. def get_vector(user, pipe=None):
  2. r = redis.StrictRedis(host='localhost', port=6379, db=0)
  3. no_pipe = False
  4. if pipe is None:
  5. pipe = pipe = r.pipeline()
  6. no_pipe = True
  7. user = user.lower()
  8. pipe.zscore(get_format("user"), user)
  9. pipe.hgetall(get_format("user:{0}:day".format(user)))
  10. pipe.zrevrange(get_format("user:{0}:event".format(user)), 0, -1,
  11. withscores=True)
  12. pipe.zcard(get_format("user:{0}:contribution".format(user)))
  13. pipe.zcard(get_format("user:{0}:connection".format(user)))
  14. pipe.zcard(get_format("user:{0}:repo".format(user)))
  15. pipe.zcard(get_format("user:{0}:lang".format(user)))
  16. pipe.zrevrange(get_format("user:{0}:lang".format(user)), 0, -1,
  17. withscores=True)
  18. if no_pipe:
  19. return pipe.execute()

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

  1. [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)]]

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

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

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