Blog logoJustZht

Blog logo

2019.11.7

 • 

IMG_20191104_234719
去东京参加了 Wacom Inkathon 的 Final,到场其他人的项目基本上都是 production ready 了,自己的半成品自然是没有什么结果 hhhh,但是各个队伍都很友好,交流的很愉快,以及后期也许还有协作的可能。总之在东京的这几天真的非常开心,除了还是很困以外。

Google Sheet + Apps Script + Glide

 • 

背 GRE 单词的时候弄的一套自动化流程,可以批量查词修改 Spreadsheet,配合 GlideApp 生成 PWA 在手机上用,效果可以参考这里

1) 导入 Apps Script

这个脚本基本上就两个功能,一个是触发器,负责在修改文档的时候更新那一行的内容,第二个是一些数值函数,可以直接在 Sheet 的 f(x) 框里面用。所有查询的词都做了 lowercase 和 cache,毕竟 UrlFetchApp 有配额一天只能跑上几千次,要省着点用,而且如果用了 f(x) 表达式的话,每次打开页面都请求爱词霸也挺拖累别人服务器的。

appsscript.json

{
  "timeZone": "Asia/Hong_Kong",
  "dependencies": {
  },
  "webapp": {
    "access": "ANYONE",
    "executeAs": "USER_ACCESSING"
  },
  "exceptionLogging": "STACKDRIVER",
  "oauthScopes": ["https://www.googleapis.com/auth/documents", "https://www.googleapis.com/auth/script.external_request", "https://www.googleapis.com/auth/spreadsheets"]
}

code.gs

function onOpen() {
  var ui = SpreadsheetApp.getUi();
  // Or DocumentApp or FormApp.
  ui.createMenu('Words')
      .addItem('Reset Cache', 'ResetCache')
      .addToUi();
}

function onEditInstalledTrigger(e) {
  var range = e.range;
  var sheet = SpreadsheetApp.getActiveSheet();
  
  Logger.log("row = " + range.getRow() + " column = " + range.getColumn);
  Logger.log("row Number = " + range.getNumRows() + " column Number = " + range.getNumColumns());
  
  for (var i = range.getRow(); i <= range.getRow() + range.getNumRows() - 1; i++) {
    for (var j = range.getColumn(); j <= range.getColumn() + range.getNumColumns() - 1; j++) 
    {
      if (j == 1)
      {
        var currentValue = sheet.getRange(i,j).getValue();
        var wordLowCase = GetWordLowerCase(currentValue);
        var empty = IsEmpty(wordLowCase);
        var cache = IsCached(wordLowCase);
        
        Logger.log("row i = " + i + " column j = " + i + " word = " + wordLowCase);
        var attribute = {
          cache:cache,
          value:wordLowCase,
          empty:empty
        }
        sheet.getRange(i,j).setNote(empty ? "" : JSON.stringify(attribute));
        sheet.getRange(i,j+1).setValue(empty ? "" : "LOADING");
        sheet.getRange(i,j+2).setValue(empty ? "" : "LOADING");
        sheet.getRange(i,j+3).setValue(empty ? "" : "LOADING");
        sheet.getRange(i,j+4).setValue(empty ? "" : "LOADING");
        sheet.getRange(i,j+5).setValue(empty ? "" : "LOADING");
        
        var wordJsonObject = empty ? {} : GetWord(wordLowCase);
        attribute.json = wordJsonObject;
        sheet.getRange(i,j).setNote(empty ? "" : JSON.stringify(attribute));
        Logger.log(JSON.stringify(attribute));
        
        if (wordJsonObject.status == 0)
        {
          var wordMeaning = empty ? "" : wordJsonObject.content.word_mean.join("\r\n");
          sheet.getRange(i,j+1).setValue(wordMeaning);
          
          var worldPronunciationUS = empty ? "" : "/" + wordJsonObject.content.ph_am + "/";
          sheet.getRange(i,j+2).setValue(worldPronunciationUS);
          
          var worldPronunciationURLUS = empty ? "" : wordJsonObject.content.ph_am_mp3;
          sheet.getRange(i,j+3).setValue(worldPronunciationURLUS);
          
          var cachedString = empty ? "" : (cache ? "YES" : "NO");
          sheet.getRange(i,j+4).setValue(cachedString);
      
        }
        
        var updateTime = empty ? "" : new Date().toISOString();
        sheet.getRange(i,j+5).setValue(updateTime);
       
      }
    }
  }
}

function ResetCache() {
  var cache = CacheService.getScriptCache();
  var selection = SpreadsheetApp.getActiveSheet();
  var data = selection.getSelection().getActiveRange().getValues()
  for (var i = 0; i < data.length; i++) {
    var wordLowCase = GetWordLowerCase(data[i][0]);
    Logger.log('Cache remove: ' + wordLowCase);
    cache.remove(wordLowCase);
  }
}

function GetWordLowerCase(word)
{
  return String(word).toLowerCase();
}

function GetWordUrl(word)
{
  var wordLowCase = GetWordLowerCase(word);
  var url = "http://fy.iciba.com/ajax.php?a=fy&f=auto&t=zh&w=".concat(encodeURIComponent(wordLowCase));
  return url;
}

function IsCached(word)
{
  if (IsEmpty(word))
  {
    return false;
  }
  var wordLowCase = GetWordLowerCase(word);
  var cache = CacheService.getScriptCache();
  var cached = cache.get(wordLowCase);
  return cached != null;
}

function IsEmpty(str) {
    return (!str || 0 === str.length);
}

/**
 * Get Word JSON Object.
 *
 * @param {word} the word.
 * @return JSON returned from iciba.
 * @customfunction
 */
function GetWord(word) {
  if (IsEmpty(word))
  {
    Logger.log("word == null");
    return {};
  }
  var wordLowCase = GetWordLowerCase(word);
  var cache = CacheService.getScriptCache();
  var cached = cache.get(wordLowCase);
  if (cached != null) {
    return JSON.parse(cached);
  }
  var url = GetWordUrl(wordLowCase);
  var jsonData = UrlFetchApp.fetch(url);
  var jsonContent = jsonData.getContentText();
  cache.put(wordLowCase, jsonContent, 604800); // cache for random minutes
  var object   = JSON.parse(jsonContent);
  return object;
}
/**
 * @customfunction
 */
function GetWordMeaning(word)
{
  var object = GetWord(word);
  return object.content.word_mean.join("\r\n");
}
/**
 * @customfunction
 */
function GetWordPronunciationUS(word)
{
  var object = GetWord(word);
  return object.content.ph_am;
}
/**
 * @customfunction
 */
function GetWordPronunciationUSURL(word)
{
  var object = GetWord(word);
  return object.content.ph_am_mp3;
}
/**
 * @customfunction
 */
function GetWordJSON(word)
{
  var object = GetWord(word);
  return JSON.stringify(object);
}

然后部署下,允许下文件访问

2) 安装 Trigger

表格 - 工具 - 脚本触发器 - 修改 - 当前项目触发器 - 添加:

  • 选择要运行的功能 OnEditInstalledTrigger
  • 选择活动来源 基于电子表格
  • 选择活动来源 编辑时
3) 输入数据

第一行最好 freeze 住,然后在第二行开始输入英文,Trigger 会自动补全后面几列
Screenshot-2019-10-24-at-12.44.30-AM

4) 同步到 Glide 里

Glide 里新建一个项目绑定这个表格。
Glide 比较好的地方在于,他们的 Detail Page 可以设定 Audio 组件绑定 URL,刚好可以对应爱词霸 API 里的音标地址,基本配置完可以当一个自定义单词本来用了。

Screenshot-2019-10-24-at-12.49.27-AM-2
Screenshot-2019-10-24-at-12.50.28-AM

2019.10.21

 • 

Hackathon 入围第一轮了,接下来下个月会去东京参加 Final。自己的作品原本就只是三四天完成的,因此选题和实现上都是典型的求快的方式,在长期改进方面相比其他队伍就会比较被动,加上自己还有语言考试没有结束,只能抽时间做了。
回家休息了一周,心态懒散了不少。
看了 Hayao Miyazaki's AirshipsTrevor Noah: Afraid of the Dark

2019.10.4

 • 

9.21 的托福成绩出来了,连续每两周一考的最后一次,目前看来三次下来分别是 102/107/108,目前四科加起来的 best score 总算是上 110 了,虽然单次考试没过线,但申请总算是有一个大致的保障了,接下来会去考 GRE。

6CD8BD79-EF75-462F-9A8E-BD98688D61D9

顺带一提,浙大的考点是六边形的开放桌子,会比较影响人发挥,还是科大的封闭桌子好用。


国庆也没有出去玩,也没有看电影,而是窝在住处给将要参加的一个 Online Hackathon 写了三四天的 Unity,和 ARKit 相关。ARKit 3 的 Motion Tracking 其实还是挺不稳定的,有的时候极其精确,有的时候又都连叉开的左右脚都分不清。一开始想做成 Unity Editor 的样式跑在 iPad 上,因此买了 Runtime Editor,效果还行,但对移动设备的支持不太好,加上多个 RenderTarget 也挺吃内存的最后就放弃了,直接用 UGUI 做 World Space 的 3D UI。

iPad 的效果
算下来也是这几个月第一次花了全天的时间在写代码了,GitHub 上也少有地出现了绿格,虽然过几天还有 GRE 考试因此心里还是挺担心因为光顾着代码没时间准备考试,但总的来说还是挺高兴的,有点类似于阅兵一样,算是在检验自己的快速出原型的能力。


之前趁着降价买了 XCOM,而且鬼使神差地 Google Play 和 App Store 上都买了,结果 Easy 模式对于我来说都有点够呛,中期经常全员覆灭,到中后期打完神秘组织后靠着机器坦克慢慢缓过来,又觉得没有挑战了。不过总归来说这个游戏太让人入迷了,因此最后被我被迫删了。
之前貌似又玩了 EVE 手游版的 testflight,但是后来貌似测试服务器关了,因此也删了。总之貌似还在准备语言考试的阶段,因此自己也没办法过的太懒散。

Unity 实例在 iOS 13.1 上无法连接 Profiler

 • 

Workaround:Xcode 项目里 Add Capability 'Access WIFI Information' && 申请地理位置权限
PostBuildCapabilityChanger.cs

using UnityEditor;
using UnityEditor.Callbacks;
using UnityEditor.iOS.Xcode;
using UnityEngine;

namespace FinGameWorks.Scripts.Editor.Modifer
{
    public static class PostBuildCapabilityChanger 
    {
        [PostProcessBuild(999)]
        public static void OnPostprocessBuild(BuildTarget target, string pathToBuiltProject)
        {
            string path = pathToBuiltProject + "/Unity-iPhone.xcodeproj/project.pbxproj";
            Debug.Log(path);
            Debug.Log(PBXProject.GetUnityTargetName());
            ProjectCapabilityManager capabilityManager = new ProjectCapabilityManager(path, "InkCloth.entitlements", PBXProject.GetUnityTargetName());
            capabilityManager.AddAccessWiFiInformation();;
            capabilityManager.WriteToFile();
        }
    }
}

PermissionManager.cs

using System.Collections;
using UnityEngine;

namespace FinGameWorks.Scripts.Manager
{
    public class PermissionManager : Singleton<PermissionManager>
    {
        private LocationService m_LocationService;
        protected override void OnAwake()
        {
            base.OnAwake();
            m_LocationService = new LocationService();
            m_LocationService.Start();
        }
    }
}

2019.8.29

 • 

连着上了两个月课,也有些懈怠了。
上英语课的最大发现倒不是语言上的什么诀窍,而是发现自己在表达看法上的匮乏。有些话题给我我就没话说也没什么想写,因为“感觉一句话就说完了”。换言之,一直以来是刻意地训练自己把看法内化,而突然被要求对这些看法重新评估的时候就已经有预设了,自然感觉没什么好说。这件事倒是没什么好或不好的,只是在没有足够的理论方法却又建立了太多个人观点的尴尬境地里徘徊一会。
因为上课也没有太多时间,很多想法也没有在 Ghost 上写了,转而偶尔在 Day One 里记一下。之前花了很大功夫从 Day One 迁移到 Journey,结果 Journey 的 Chrome App 说关就关,电脑上只能用桌面客户端,虽然是一次性购买但软件质量真的不值几十美元,因此又转而续了 Day One 的订阅。
有了几个新的想法,比如闲暇时有在慢慢写一个 iOS 上基于 Files 的 DocumentPickerUI 的背单词软件。此外还有一些 Unity 项目和 Unity Asset 的计划,但是 Unity 项目的调试周期都太长,目前是没有可能做了。目前看来语言考试的排期已经到了一两个月后,如此看来今年下半年是不太可能有时间去 Unity Shanghai 写喜欢的项目了,其实还挺可惜的。如果年底语言考试完结,估计也不打算去什么公司了,而是直接重写 Epoch,希望明年动身前能差不多做到核心玩法都完成的地步,后面再走一步看一步。
看了好几部电影,包括 Shaft 1 & 2,哪吒和速度与激情,记得之前还重新看了遍星银岛。
密集地修了两次电脑,一次是 MBP 18 的键盘,一次是 MBP 15 的电池召回。两次都是在南京修的,每次去都要顺路去买泡芙,因此和背单词记含义一样现在建立起来了苹果店和泡芙店的奇怪联系。
博客写的越来越少了,各种平台也不怎么说话了,除去上课的原因,自己相比以前也更没话说了。有的时候看以前的推发觉还挺好玩,那种心态倒是挺难得的,一种急切又乐观的状态,但是也因此很难维持下去。自己倒是逐渐在适应沉住气的节奏,因此和都在做好玩的事的大家相比显得就更为无话了,默默围观下大公司雇员,独立开发者群,设计群和游戏开发们的动静。有的时候大家又都在发看法,我想说些什么却似乎也没有发表看法的必要,因为某种意义上来说,不暴露自己的政治倾向也是政治倾向的一部分。

2019.8.10

 • 

做了万全的准备 来杭州首考托福 结果考试因为台风取消了 真是哭笑不得
现在在考虑要怎么回去

lekima

2019.7.12

 • 

六月底拿到了毕业证。之后就一直在上英语课,也算是回到了学生的日常生活。之前翘课了那么多年,这次反而头一回在主动地去机构学习东西,倒也显得有趣。
看了 blank vhs covers were kinda beautiful

2019.6.20

 • 

Macbook 键盘出问题了,因此最近用起来多了很多双击的困扰,比如敲击 hao(好)会经常粘连成 haoo(好喔)。虽然这么一说还挺可爱的,但编程的时候也总是变量名敲错地烦躁,因此约了周末的 Genius Bar 看什么情况,还好一般的任务我可以用 Pixelbook 来代替。最近还在 Pixelbook 上装了 Idea 和 Webstorm,只不过需要手动编辑 desktop 文件把图标路径从 svg 指向 png,其余都大体能用。
个人项目再一次往后拖了,也不知道暑假的时候还有没有空完成。此外在考虑暑假之后到明年六月份是:去上海 Unity 入职 / 重新面一次微软苏州的 iOS 岗 / 留在北京工作,还是在家把 Epoch 做完发布。我到现在也没有一个具体的想法,Unity 的话 offer 应该还算稳妥,好处就是也许能跟着我很喜欢的框架的项目组开发,还是少有的上海本地有独立自主开发权的项目,真做好了也就可以说是 Dogfooding 了;微软苏州如果暑假忙完刷算法应该可以再试试,只不过不确定自己的水平,也还不清楚和下半年的几场考试冲不冲突;在家的话就是可以完全准备语言考试,也有时间宅在家,如果充分投入的话也许明年六月份就能做出来 Epoch,意义和潜在的后续发展可能都挺大的。
毕业证去长沙问了一次,说还没印出来,要排在今年毕业的学生后面印了,也不知道我到底什么时候才能真正毕业。

2019.6.6

 • 

macOS 升级到了 10.15,跑了下 Marzipan 完成度出奇地高。然后发现 Unity 导出 il2cpp 项目不正常,于是又降级了。今年 WWDC 还是有很多新东西的,比如 SwiftUI 和 BackgroundTask。但是令人迷惑的东西也多,比如 iPad 所谓的新多窗口多任务交互感觉更加难用了,以及新 Mac Pro 似乎是很强,但定位也并不是给一般的 pro,而是土豪 pro 们了,因此目前为止苹果产品线上还是没有一个能满足一般的多媒体创意需求(软件开发,游戏开发,建模和渲染之类)的中等价位的台式主机/笔记本产品,也没有 N 卡。
Skyline 的 Dark Mode 支持已经推到 Stable Channel 了,顺便解决了 Android Q block background activity 启动的警告。估计会在 6.10 - 6.16 开一个小的 0.99 刀促销活动来庆祝下。
看了 Minority Report — Dismantling Precrime