GitDigger 0.1.0 开发日志

发表于2017年07月19日

2018-07-08

pipenv 在 windows 上没法用,由于用户名是中文的,项目路径也包含中文,只要涉及到路径操作的都会报编码错误。记得之前有改过 pipenv 的源码,但更新到最新版后问题并没有解决。

代码片段需要支持搜索,有点麻烦,现阶段不打算折腾搜索服务,可以推迟开发。据说 PostgreSQL 支持全文搜索,要是用它的特性的话,GitDigger 的数据库就只能是 PostgreSQL 了。

2018-07-01

代码注释抓取工具已经完成,接下来需要让它支持上传结果,还要添加一个接口来接受这些结果,而接口又需要做验证,只允许注册用户上传。虽然用密钥来验证用户比较安全,但要花时间写一个密钥生成功能,让用户自己在页面里生成密钥,现在不想花这么多时间,所以,先暂时用明文形式传递用户名和密码。

接口接收到代码片段列表后,需要对项目中现有的代码片段进行更新/删除操作,文件路径和 commit_id 相同的代码片段不需要处理,因为内容无变化。

2018-06-24

数据可以命名为 Snippet,因为这个板块主要是展示代码片段和 FIXME 注释。既然是代码片段,那可以参考 GitHub Gist 页面的设计。

GitHub Gist

FIXME 板块需要展示的内容有:来源项目、文件路径、复杂度评级、注释、代码片段。由于还没有数据,为方便测试就先弄了个样例数据,经过一番调整,效果如下图所示。

Snippet

接下来准备添加 FIXME 注释抓取功能,遍历项目里的文件,抓取合适的 FIXME 注释,然后添加进数据库。

2018-06-23

准备添加个板块,用于展示各个项目里被 FIXME 标记的代码片段,然而该用什么名字?Fixme?Wanted?Reqeust?Wanted 有“通缉”的意思,这些被曝光的代码片段可以当作被通缉了,看上去还比较适合。

FIXME 需要有个在项目内唯一的编号,就像项目中的 Issue 编号一样,该如何设计表结构来实现?

找到了个答案,但看上去很复杂:
sql - How can you auto-increment a non primary ID column scoped to another table? - Stack Overflow

稍微想了一下,可以给 Repository 表加个 last_wanted_number 字段,记录最后一个 Wanted 编号,每增加一个 Wanted 就让 last_wanted_number 自增。

Watned 列表里会展示代码片段,需要有行号和代码着色,这些用 pygments 可以实现,指定 linenostart 可自定义起始行号。

代码高亮可以写成过滤器。

2018-06-18

主页的设计有些让人纠结,本打算用 GitHub 的 Marketplace 页面那样的无分割线风格,用标题和大点的间距分割区块,然而实际应用的效果并不好。

GitHub Marketplace

主要问题在于项目列表和故事列表之间的分界不明显,区块标题和故事标题一样醒目,好好的简洁大方的风格被表现成简陋风格。感觉这种多类型数据聚合展示的页面更适合使用卡片风格,只需设置灰色背景,然后将不同的内容放到不同卡片里,就能产生很好的分界效果。

故事列表和开发者列表都有用户头像,要是再让项目列表显示头像就会显得很单调了,所以,项目列表不显示头像,改用统一的项目图标。为了让项目列表和其它区块有明显的区别且能够从视觉上产生分界效果,可以把项目弄成卡片式。

区块标题太显眼也不行,可以采用 GitHub 用户主页里的设计,字体稍微大点,但不加粗。

GitHub user page

经过一番调整,效果如下图所示,但感觉故事列表里的中文标题还是太粗了,微软雅黑的中文粗体效果并不好。

GitDigger explore page

2018-06-17

为 User 表添加了几个字段,然而迁移数据库后,字段的值都是 NULL,如何让它在迁移时设置字段默认值?

解决方法是设置 server_default,例如:

op.add_column('user', sa.Column('followers_count', sa.Integer(), default=0, nullable=True, server_default='0'))

添加了个用户信息定时更新任务,每 6 小时更新一次,时间太长,想早点看到数据的话只能手动执行任务了,网上找到的答案是调用 python manage.py shell,然后写 python 代码执行命令,例如:

$ python manage.py shell
>>> from app.workers import user_worker
>>> result = user_worker.update_profile.apply()

2018-06-16

开始添加主页,在用户未登录时路由是根目录,登录后,根目录用于呈现控制面板,首页的路由为 /explore。

2018-05-31

需要让 redis 和 celery 进程能够后台运行,据说可以用 supervisor。

2018-05-30

certbot 用起来蛮方便的,它能自动检测当前服务器的域名,然后自动修改 nginx 配置以启用 HTTPS 连接,期间只需要填写邮箱地址,按几次回车,等它自己完成。

2018-05-26

想部署 GitDigger 在这个服务器上,然而服务器上的 Ubuntu 版本太低,很多软件包源都是 404,依赖包都不好安装,于是就买了个新服务器,准备把个人网站也迁移到新服务器上。

这次想试试 Debian 系统,然而又遇到了一些问题:

2018-05-14

准备写用户主页,不过,写个页面还要自己写代码收集数据,真麻烦。

调整了故事列表项的样式,取消卡片式风格。

故事列表项

设计文档里应该只描述需要什么内容,而设计过程、参考案例、效果图还是写在开发日志里好些,以下是之前的故事列表项的设计过程。

故事列表项的参考对象有如下几个:

Google Plus

Google Plus:头部一行显示该内容所在的圈子,底部一行动态显示随机评论,可以作为参考。考虑到一般的问题(Issue)评论内容都比较长,要么就是长串文字描述,要么就是文字 + 代码片段,不太适合在底部展示,需要做些调整。

Product Hunt

Product Hunt:标签呈现方式比较独特,可以作为参考。

Designer News

Designer News:链接有下划线,时间有点状下划线,视觉特征明显,容易分辨,细节设计蛮好的。

Flarum

Flarum:头像右上角有小徽标,可以作为参考。

Quora

Quora:底部只有投票是按钮形式,其它都是文本形式。

综合上面的参考对象,初步确定了大致效果,如下图所示,头部区域包含作者头像、名称、仓库名、操作、时间,头像右下角小徽标表示当前故事类型及状态;中间内容区域包含标题和摘要;底部区域包括投票按钮、评论数量、主题列表。

GitDigger

2018-02-21

关掉了服务器,一直开着也浪费钱,等以后有空时再在移动到 lc-soft.io 的服务器上。

2017-11-02

由于时间问题,暂时停止此项目的开发。除项目和个人页面外其它页面/功能都可以不做,毕竟这两个页面用处比较大,以后看能不能花些时间设计一下。

2017-08-21

pip install misaka 报错:

misaka\hoedown/buffer.h(9) : fatal error C1083: Cannot open include file: 'stdint.h': No such file or directory

Visual C++ for Python 没有带 stdint.h 文件,解决方法:C99 stdint.h header and MS Visual Studio,手动下载 stdint.h 文件复制到 C:\Users\<你的用户名>\AppData\Local\Programs\Common\Microsoft\Visual C++ for Python\9.0\VC\include 目录下。

2017-08-13

迁移数据库时想重命名,可以用 rename_table()

资讯的多主题搜索功能可以参考这个问答:Flask-SQLAlchemy query many-to-many tagging with multiple requred tags

2017-08-12

查询资讯列表时还要判断每条资讯是否被用户投过票,这个看上去比较复杂,以前学的 SQL 知识在现在还只记得 SELECT、FROM、WHERE、ORDER BY 这类简单的语句,找了相关资料,可以用 CASE 和 LEFT JOIN 语句。折腾了一下,SQL 代码算是完成了,接下来需要将它转换为适用于 SQLAlchemy 的 Python 代码。

SQL

Python 代码如下,然而 Issue 模型里的 user 属性却无法访问。

user_id = current_user.id if current_user.is_authenticated else 0
terms = Voter.target_id==Issue.id and Voter.target_type=='issue'
case = db.case([(Voter.user_id==user_id, True)], else_=False)
query = db.session.query(Issue, case.label('has_voted'))
feeds = query.outerjoin(Voter, terms).order_by(Issue.score.desc()).all()

原来查询到的列表的元素类型是元组,需要这样才能访问到 user 属性:

for feed, has_voted in feeds:
    print feed.user
    print has_voted

然而又有问题,投过票的 Issue,会有会重复记录。看来在 SQL 上花太多时间会影响进度,还是优先把其它页面都弄出来吧。

尝试手写 SQL 去除重复记录,加了 Voter.user_id==user_id 条件后,结果正常。然而写成 SQLAlchemy 版的却没效果,打印出的 SQL 内容中并未追加新的条件。最后找到了这篇内容:sqlalchemy - join child table with 2 conditions,原来是需要调用 and_() 方法追加条件,修改后的代码如下:

user_id = current_user.id if current_user.is_authenticated else 0
terms = db.and_(Voter.target_id==Issue.id, Voter.user_id==user_id)
case = db.case([(Voter.user_id==user_id, True)], else_=False)
query = db.session.query(Issue, case.label('has_voted'))
query = query.order_by(Issue.score.desc(), Issue.created_at.desc())
query = query.outerjoin(Voter, terms)
feeds = query.all()

2017-08-08

刚开始还以为只需要启动 Celery 一个进程就够了,然而启动后预设的定时任务并没有执行,后来才发现启动的只是职程,需要发信号给这个进程它才会执行任务,也就是再开一个进程:

celery beat --app app.celery

2017-08-06

感觉 ExtractTextPlugin 和 expose-loader 有冲突,编辑保存 js 后,生成的 vendor.js 是正常的,app.js 里可以调用 $,如果再编辑一下 scss,生成的 vendor.js 中的 jQuery 并没有导出到全局。解决办法嘛,再新建个 common.js 文件专门引入 css 文件,与其它 js 文件独立开来,这样就不会影响到 vendor.js 的内容了。

2017-08-03

有些比较耗时的任务和定时任务可以异步执行,看来需要添加任务队列了,还要搞个管理后台页面,用来查看当前在跑哪些任务。

这打包总有问题,把 jQuery、Bootstrap 的 js 打包到 vendor.js 里,有时全局可以用 jQuery, 有时又不行。在 vendor.js 里加一些测试代码,有时生成出来的 vendor.js 里根本找不到这些测试代码。

2017-08-02

Bootstrap 的下拉框不支持 hover 时显示,好吧,就这样吧。

Dropdowns are toggleable, contextual overlays for displaying lists of links and more. They’re made interactive with the included Bootstrap dropdown JavaScript plugin. They’re toggled by clicking, not by hovering; this is an intentional design decision.

2017-07-29

改用了 PostgreSQL 数据库。

PostgreSQL

完善了 GitHub 账号登录,GitHub 账号快速注册功能暂时不做。

作为 GitHub 应用被安装后,只需要提供一个 webhook 给 GitHub 调用就能接收到各种事件数据,还可以在网页端配置需要哪些类型的事件,方便了很多。之前看文档还以为要调用项目的 webhooks 去发请求为项目创建 webhook。

2017-07-25

自己写了个装饰器,然而 Flask 报错说视图函数覆盖了现有的 endpoint 函数:

View function mapping is overwriting an existing endpoint function: repos.wrapper

看上去函数在加上装饰器后 Flask.route() 得到的视图函数的名称都是 wrapper ,网上搜索了装饰器相关的文章,据说可以用 functools.wraps 保留函数元信息。

2017-07-24

每为仓库添加一个接口都要写重复的代码,例如:

@repos.route('/<string:username>/<string:name>/settings', methods=['GET'])
def settings(username, name):
    repo = get_repo(username, name)
    if repo is None:
        return abort(404)
    return render_template('repositories/settings/index.html', repo=repo)

@repos.route('/<string:username>/<string:name>', methods=['GET'])
def show(username, name):
    repo = get_repo(username, name)
    if repo is None:
        return abort(404)
    return render_template('repositories/show.html', repo=repo)

感觉好麻烦,要是能写个装饰器简化操作就好了。

@repo_route('/settings', methods=['GET'])
def settings(repo):
    pass

@repo_route('/', methods=['GET'])
def show(repo):
    pass

如何设置 Flask 的蓝图(Blueprint)的默认模板查找位置?看文档说可以设置 template_folder 参数,然而总报错说找不到模板。

添加仓库的设置页面,导航菜单的选项连接是用 url_for() 函数获取的,然而要写全参数才行:

url_for('repos.settings', username=repo.owner.username, name=repo.name)

要是能像 Rails 那样写成 url_for_repo_settings(repo) 就好了。或者自己预先定义好这些变量,然后当参数传进模板里?

感觉上面几个功能可以写成一个模块,命名为 repo_helper.py,然后代码就可以简化成这样:

<a href="{{ url_for_repo }}">Home</a>
<a href="{{ url_for_repo_settings }}">Settings</a>
@repo_hepler.route('/settings', methods=['GET'])
def settings(repo):
    return repo_hepler.render('settings/index.html')

@repo_hepler.route('/', methods=['GET'])
def show(repo):
    return repo_hepler.render('show.html')

@repo_hepler.route('/hooks/<string:hookid>', methods=['GET'])
def hooks(repo, hookid):
    return repo_hepler.render('hooks/show.html')

自动为模板添加 repourl_for_repo_settingsurl_for_repo 参数,不用再手动传递,方便了很多。

访问 hello/hello 这两个路径的效果居然不一样,访问 hello 时浏览器会自动跳转到 hello/,然而 Flask 并未找到与 hello/ 绑定的函数,返回 404,有什么办法让 Flask 自动重定向,将地址末尾的斜杠去掉?

2017-07-23

完成了:

修改表结构后,SQLAlchemy 升级失败,报错:

sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) near "ALTER": syntax error [SQL: u'ALTER TABLE repository ALTER COLUMN name SET NOT NULL']

懒得折腾了,直接删库重建。

2017-07-22

完成了:

2017-07-19

买入了 gitdigger.com 和 gitdigger.io 两个域名,准备搞个网站,设计文档还在完善中,到时候再贴出来。欢迎各位 Python 大佬提供技术支持。

对此文章有疑问?你可以点击此链接反馈你的问题