Day 1 of the Positive Life

A rectangle of the COVID test kit sample dipping zone, and an emoji with wide-eyed face and two red lines down from the eye

Yes, I got COVID. After 2 years and 3 shots. It's so frustrating.

I might just have too much to write about, so much that no one might ever want to see on Twitter. Maybe a blog series would work then? Writing something down might make me feel a little positive with these positive days, I guess.

I don't really want to bother anyone to chat with me all day randomly. Ugh, social is hard. 继续阅读“Day 1 of the Positive Life”

Customized COVID data comparison with pandas and plotly

It's yet another summer that people just want to travel without fear. Probably it has been "back to normal" for some people, but for me, it has to be backed with some data. There are plenty of websites that publish COVID case data and visualize them by city (even in Google onebox - by the way, Google Travel shows some helpful metrics on hotel reservation rate as well).

However, with a lot of travel destinations in mind, it would be very helpful if there's a dashboard that I can compare COVID cases between different cities and figure out the trend myself. I haven't seen one handy, and I really want to play around the data with some home baked solution. 继续阅读“Customized COVID data comparison with pandas and plotly”

Wrap Context Manager (as nested) in a Python function

To make my code more elegant, I need to wrap a context manager with initialization code as a function in Python. This is definitely possible, but it took me some time to find the most elegant way to do this.

Generally speaking, you will want to enter all contexts when using the decorator @contextlib.contextmanager or @contextlib.asynccontextmanager. When the end user uses with my_function() as a:, everything inside the with block has been inside the nested contexts. When Python gets out of the end user's with block, it should also run all related __exit__s in the wrapper function. See example (requires Python 3.7+ probably and writes test.txt; you can test it on

import asyncio
import contextlib
import aiofiles
import typing

async def my_context_manager() -> typing.ContextManager[aiofiles.threadpool.AsyncFileIO]:
    # init
    filename = 'test.txt'

    async with, 'w+') as file:
        # you can still override or interact with `file` if needed
        yield file

async def main():
    # end user
    myfile: aiofiles.threadpool.AsyncFileIO
    async with my_context_manager() as myfile:
        print(await myfile.write("12\n"))
        print(await myfile.readline())

When I rewrite the code above I feel it so easy and natural. It really took me some time to realize how this works.

Side note of some scheduling tools


Free version includes:

  • multiple spots in one time slot
  • email (ics) confirmation to invitee
  • export as table
  • cancellation email triggered by organizer
  • modify bookings by unique link (in the email).

UX feels like oldest.


Free version includes:

  • multiple spots in one time slot
  • export as table (maybe?)

No invitee email is available in free version. Booking modification can only be protected if invitees log in with a Doodle account. UX is a lot better.


Free version includes:

  • email (ics) confirmation to invitee
  • export as table
  • cancellation email triggered by organizer
  • modify bookings by unique ID (in the email)

Paid version (10 days trial) includes:

  • multiple spots in one time slot
  • webhook (API)

UX is the best.

Strip Assets Files in new Android Gradle

Spending 2 hours on this because 1) I don't have experience with Gradle script (its syntax looks funky) 2) DJI sxxks, and they put 50MB of large assets files to their SDK, which is camera distrotion_correction files that I never need, and 3) Android Gradle library upgrades, which deprecating mergeAssets.

Looking at some GitHub projects's Gradle script, it looks like dependsOn is very popular in defining what order the task should be executed, and it works! 继续阅读“Strip Assets Files in new Android Gradle”

Manual work after WordPress auto update failed

WordPress 版本都到 4.9 了,全球 CMS 占有量也早过三分之一了,自动更新居然还是全量下载,连个增量更新都不做(光做 有啥用拜托?)。虚拟主机下载 WordPress 的包速度又很慢,结果总是超时(虚拟主机控制的那种超时断开),更新失败。


My WordPress auto update always failed because of slow download speed. Just don't know why WordPress doesn't implement a incremental upgrade mechanism (maybe I should search for a ticket?). Whatever, I have to solve it manually, in a smart way.

P.S. 写完这篇之后,看到更新的 API 了,的确是有增量更新的,但他们不用。They actually have incremental upgrade implemented, but they hardly use it.

继续阅读“Manual work after WordPress auto update failed”

jzGradeChecker - 优雅查成绩 Graceful Grade-Checking Experience

链接 Links

  • 网站 Website (包含导出数据查看网页 Includes a webpage for reading the exported file)
  • 演示 Demo (这是扩展的欢迎界面,显示效果与实际扩展工作情况无异 This is the introduction page of the extension, which reuses the same core scripts of the parsing code, working identically to the extension)
  • Chrome Web Store

重要提示 Notice

由于原网站在今年改版,成绩查询服务器已经下线,该扩展的读取成绩功能已经无法工作。但因为扩展自定义了一套 JSON 数据结构,即便服务器不可用,仍可通过离线数据实现信息显示,因此在线演示、导出成绩查看器等仍可查看。

Due to the original website upgrade, this Chrome extension cannot fetch grade data any more (the grade check server is down). However, the demo and the exported data reader still work, because I have implemented a customized JSON data structure, which make the code functional by reading the JSON data even if the remote server is unavailable. 继续阅读“jzGradeChecker - 优雅查成绩 Graceful Grade-Checking Experience”

魔方机器人(网页与 Python 控制程序) Rubik's Cube Solving Robot

介绍 Introduction


This is a course project for "Scientific Research Project Guidance and Training" course of my major. We chose to build a Rubik's cube solving robot, which can identify a third-order cube's pattern and solve it.

该机器人拥有六个自由度,对六个面进行旋转,并有四个摄像头从四个角进行颜色的识别。此外,机器人采用树莓派作为上位机进行总体控制,采用 Arduino 作为下位机进行步进电机的控制。

The robot has six degrees of freedom, rotating the six sides. It identifies the cube by four cameras from the four corners. A Raspberry Pi is used as an upper controller for the general control, and an Arduino board as a lower controller for the motor control.

项目在最终结题答辩时获得非常高的评价,同一课程中共有 9 组项目。之后,项目在 2017 年全国青年科普创作实验暨作品大赛中获得北京赛区未来教育组三等奖

The project gets an excellent feedback at the final presentation among 9 projects of the course. Afterwards we also won a third prize (Beijing Area/FutureEDU) in 2017 China Youth Science Popularization Experiment and Work Contest.

继续阅读“魔方机器人(网页与 Python 控制程序) Rubik's Cube Solving Robot”

Make Service Fault Transparent

This article is an English one, because I really need to work on the language. Sorry if it is not easy to understand.

A Summary to What's Happening Recently

Recently in my campus, IT service is very unstable.

  • In March, many people posted on forums that they tried to top up campus Internet account by WeChat, but more money (maybe 100x) than they paid were topped up.
    • Later WeChat top-up service were disabled. Because most people were not aware of the existing offline top-up-by-card service, many of them became arrearage.
    • Several days later, campus Internet's charging system was disabled, which means you can use it for free. Later the charging system was resumed, but only charging at the monthly fee (not counting flux fee).
    • An unnoticeable statement was published then, indicating that it was caused by a bug from the software company.
  • On March 20th, campus card users who used their cards to drink hot water or eat breakfast, found their card locked. (Those lazy guys were not affected at all)
    • In the morning nobody knows whether the issue was being solved, until at around 11 (lunchtime) my school's instructor sent an announcement that "there will be unlock service in canteens, please keep order and don't panic at the scene". At canteens announcements by canteens' administrator is put up. Unlocking was quick and easy, but most people still went to canteens where Alipay is accepted.
    • Later that afternoon public statement by card administrator was out: It was a service fault (on BITUnion some said that it's a bug hidden for 14 years). IT staffs explained on BITUnion that they tried to work out solutions and mitigate the issue before they drafted public statements.
  • In these months campus Internet is unstable: During peak hours it became very slow or even unavailable. Maybe it's around 2%'s downtime (in a 24-hour aspect), looking not that much, but users surely could experience that.
    • The causes seem very complex. In my view, new DNS servers, old cache servers, new firewall systems, new upstream link providers and upstream link issue all can cause problems. And of course those new facilities all need to be fine-tuned, which takes time.
    • Currently no authentic statement is published. But in the IT service monthly report (which most people are not aware of), it said "Issue fully fixed, during peak hours upstream links can work in full bandwidth now". One of the reasons they mentioned was "DDoS attack causing network core server CPU instant usage up to 99% (usually ~20%)".
    • However, as student representatives meeting will be held, many representatives will raise the heated Internet issue onto the meeting. But I believe most of they will never get the point why this is happening.

继续阅读“Make Service Fault Transparent”