分類
网站

使用 UpUp 讓網站離線可用

UpUp 是個很小(2.5 KB)的 Javascript 庫,但卻可以方便的實現網站的離線訪問。我之前有一個小項目,PHP 的後端和頁面,另外藉助 Apeche Cordova 用 HTML 實現了安卓的客戶端以供離線使用。最近用 Django 重新寫了,藉助 UpUp 的離線頁面,我不用爲了離線使用而再去生成一個安卓應用。

一般的網站,要使用 UpUp 是非常簡單的,只需要在網站的頂層文件夾引入對應的庫即可,但是 Django 的靜態文件一般是放在 static 下的,要想在項目根目錄下提供它們,可以這樣設置。

#項目的 url.py 文件
from django.urls import path
...
from . import views

urlpatterns = [
    ...
    path('upup.min.js', cache_page(60 * 60 * 48)(TemplateView.as_view(template_name="site/upup.min.js", 
  content_type='application/javascript', )), name='upup.min.js'),
    path('upup.sw.min.js', cache_page(60 * 60 * 48)(TemplateView.as_view(template_name="site/upup.sw.min.js", 
  content_type='application/javascript', )), name='upup.sw.min.js'),
]

然後將 upup.min.js 和 upup.sw.min.js 放在項目的模板目錄裏,比如 mysite/site/templates/site/ 中。

最後在 HTML 頁面中引用並設置需要離線的文件即可。

...
<script type="text/javascript" src="{% url 'upup.min.js' %}"></script>
<script type="text/javascript" src="{% static 'site/js/jquery-3.6.0.min.js' %}"></script>
<script>
UpUp.start({
  'content-url': '/site/',
  'assets': ['/static/site/js/jquery-3.6.0.min.js',
   '/static/site/js/site.js', '/static/site/css/site.css']

});
</script>

離線站點調試的過程中遇到的另外一個問題是,離線站點必須是 HTTPS 類型的加密頁面,但是本地配置 HTTPS 又略嫌繁瑣,其實只要再 Chrome 裏添加例外(chrome://flags/#enable-site-per-process)即可。火狐我還不知道要怎麼添加,最近安卓火狐的一系列更新都不盡人意,實在是有些令人擔憂。

分類
說說

210521

非常遺憾,本站似乎無法從國內直接打開了。

#天威寬帶
$ curl https://ft.shaman.eu.org/
curl: (28) Failed to connect to ft.shaman.eu.org port 443: Connection timed out

#移動網絡(2G 和 4G)
$ curl https://ft.shaman.eu.org/
curl: (7) Couldn't connect to server

通過測速網站的結果可以看到,國內的 DNS 服務可以正確解析出本站的 Cloudflare IP,但是無法打開頁面。國外則正常。

分類
软件

心臟異常檢測 Heart Anomaly Detection

藉助手機的麥克風來錄製心跳,然後使用 MananAgarwal 訓練的 Heartbeat-Classifier 模型來判斷心跳是否異常。經過測試,我和 Emanon 的心跳都是正常。我也有嘗試使用其他項目的實驗數據來測試這個模型,對於「二尖瓣迴流」和「主動脈狹窄」的異常心跳都成功的識別了出來。不過它畢竟只是一個人工智能模型,如果有不舒服,還是應該儘早去看醫生。

安裝 Heart Anomaly Detection

這是一個 python3 的 tensorflow 項目。項目本身並不大(9 MB)但依賴大概有 600MB ,所以推薦使用虛擬環境來安裝

#安裝依賴
pip install tensorflow keras librosa 
#下載項目
git clone https://github.com/MananAgarwal/Heartbeat-Classifier.git
#測試運行
cd Heartbeat-Classifier
python testing.py heartbeat-to-classify.wav
#稍等片刻應該會成功輸出
Normal heartbeat
confidence: 0.9638266

使用手機錄製心跳

由於我不是蘋果手機用戶,所以無法推薦和測試適合的錄音應用。但是該模型的訓練數據中有用到蘋果手機(用iStethoscope Pro)錄製的心跳音頻,所以蘋果手機應該也是沒問題的。我的安卓手機使用的是 Audio Recorder,一個開源免費又小巧的錄音軟件(5.6 MB)。設置裏只需要把編碼格式改爲「.wav」,其他的保持不變即可滿足我們的需要。

找到手機的麥克風位置,一般都在手機的底部。找一個相對安靜的地方,點擊開始錄音(爲了避免誤觸屏幕,可以按一下電源鍵把屏幕關閉),把手機的麥克風的位置貼近心臟的位置,按住15秒左右,然後打開手機,停止錄音。我的建議是手機直接接觸皮膚來錄音,可能會獲得更好的音質。但是我給 Emanon 錄音的時候是隔着 T 恤的,倒也沒有影響的測試結果。

錄好的聲音文件默認保存在 /sdcard/Android/data/com.github.axet.audiorecorder/files/recordings 中,可以通過 USB 線或 Material Files 文件管理器將文件傳輸到電腦上。

最後使用上面的測試命令進行檢測,即可得到預測的心率狀態:Normal(正常)或 Abnormal(不正常),以及預測的信心值 confidence。

關於這個預測模型

該模型由博拉理工学院(印度皮拉尼)的 Manan Agarwal 和 Ankita Chakravarty 根據 The Classifying Heart Sounds Challenge 2011的數據集訓練而來。模型的訓練準確度爲 89.73,測試準確率爲 84.04。下面的文字摘自他們的論文說明:

我們的目標是提供一個可靠,快速且低成本的系統,讓未經培訓的一線衛生工作者或任何具有互聯網訪問權限的人都可以使用,以幫助確定是否應該將受試者推薦給專家診斷,尤其是在訪問臨床醫生和專家比較困難的地區。這也將有助於早期診斷心血管疾病,並大大降低這些死亡的潛在危險因素。

分類
其它

210515

【廣東省公安廳,廣東省通信管理局】「拒絕跨境賭博主題宣傳周」提示:辛苦賺來血汗錢,跨境一賭全不見,一入賭博深似海,再想回頭難上難。

【廣東省民政廳】提示:公衆要謹防被非法社會組織利用,上當受騙;可通過「中國社會組織動態」微信公衆號查驗真僞,並向屬地民政局舉報相關線索。

【省三防辦、省應急管理廳】提醒您:今天是第 13 個「全國防災減災日」。我省已進入強對流天氣多發季節,要注意防禦雷擊、雷暴大風和短時強降雨引發的次生災害。雷雨大風期間,塔吊、龍門吊、腳手架、井架、升降機、高空吊籃等一律停止作業,深基坑、地下管網等在建工程要做好防範措施;居民儘量留在堅固安全房屋內避險,遠離戶外廣告牌、棚架、鐵皮屋、板房等,切勿在樹下、電杆下、塔吊下躲避。(廣東預警信息發布中心 2021 年 5 月 12 日發布)

【**政務短信】【溫馨提示】 尊敬的**區**街道居民,**街道 5 月 15 日繼續接種新冠疫苗。接種時間,**中心:9:00-18:00;社康中心:14:00-20:00。敬請您前往接種前,關注「**疾控」微信公衆號,點擊「打疫苗」,「個人新冠疫苗預約」完成自助建檔和預約;亦可現場排隊接種。接種新冠疫苗,保你我健康!

【**中行】尊敬的客戶*,國家法定數字貨幣—「數字人民幣」已在**率先試點。我們按要求邀請特定客戶分批參與嘗鮮體驗。現邀請您於 5 月 16 日之前登陸中行手機銀行首頁,在「更多」—「支付」模塊中點擊「數字人民幣」圖標(如未顯示圖標請升級手機銀行至最新版),註冊開通即可參與「數字人民幣春之禮」活動,在**指定商超(*、*、*、*、*等)體驗使用數字人民幣,每單最高享 50 元優惠。體驗名額有限,先到先得。詳詢各網點,退訂回復TD。

分類
网站

使用 Plausible 統計用戶事件

中文繁簡轉換工具用上 Bootstrap 後漂亮多了,使用 JavaScript 來請求表格減少了頁面的跳轉,用起來也比較方便。於是產生一個新需求就是想統計下「轉換」按鈕被點擊了多少次。關於自定義事件目標的具體介紹,可以參考 Plausible 的官方文檔Custom event goals,我這裏只是羅列下具體步驟。

前端 html 中的修改

確保 Plausible 配置有下面的第二行

<script async defer data-domain="<yourdomain.com>" src="https://plausible.yourdomain.com/js/plausible.js"></script>
<script>window.plausible = window.plausible || function() { (window.plausible.q = window.plausible.q || []).push(arguments) }</script>

在需要統計的地方執行這行 JavaScript

plausible('my_event');

Plausible 後臺的設置

打開 Plausible 後臺,在左上角的域名處選擇 Site Settings。在 Goals 區域點擊 + Add goal。然後 Goal trigger 選擇 Custom event,Event name 要輸入 JavaScritp 請求的參數,這裏就是「my_event」。最後點擊 Add goal 就完成了。

分類
程序

把媒體中地理位置標籤從 WGS-84 轉換成 GCJ-02

手機拍攝的照片地理位置默認是 WGS-84 系統,但是谷歌地圖使用的卻是 GCJ-02 系統,這就會出現偏移。爲了展示正確的位置,可以通過把照片的地理位置標籤轉換成 GCJ-02 來實現。需要用到 Phil Harvey開發的 ExifTool 和 Avatar sshuair 開發的 coord-convert。前者下載exiftool.exe 到本地即可,後者可以通過 pip 安裝。

# -*- coding: utf-8 -*-

#pip install coord-convert

#Usage:
#Drage a single photo or directory with photos to this script
# OR
#python wgs2gcj.py D:\my.JPG
#Results will be wrote to gps.log at the same dir with this script

from coord_convert.transform import wgs2gcj
import subprocess,json,sys,os,time,datetime
#You may set your exiftool file here
exiftool_path = "D:\\Program Files\\portable\\exiftool.exe"

work_dir = os.getcwd()
def log42(logFile,logText):
	ts = int(time.time())
	dt = datetime.datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S')
	
	log_file = os.path.join(work_dir,logFile+".log")
	with open(log_file, "a") as myfile:
		myfile.write(dt+" "+logText+"\n")
		print(logText)
		
		
if (exiftool_path == ""):
	exiftool_path = os.path.join(work_dir,'exiftool.exe')
if not os.path.exists(exiftool_path):
	log42("gps","Error: exiftool.exe not found neither in script directory nor in the script setting \"exiftool_path\"")
	exit()
	
file_paths_input = sys.argv[1:]
file_paths = []

for file_path_input in file_paths_input:
	if os.path.isfile(file_path_input):
		file_paths.append(file_path_input)
	elif os.path.isdir(file_path_input):
		for root,ds,fs in os.walk(file_path_input):
			for ff in fs:
				full_path = os.path.join(root,ff)
				file_paths.append(full_path)

for f in file_paths:
	output = subprocess.check_output('"'+exiftool_path+'" -struct -j -a -n "-gps*" '+f, shell=True)
	data = json.loads(output)
	if 'GPSMapDatum' in data[0]:
		log42("gps",data[0]['SourceFile']+':'+str(data[0]['GPSMapDatum'])+' '+str(data[0]['GPSLatitude'])+' '+str(data[0]['GPSLongitude']))
		if(data[0]['GPSMapDatum']=='WGS-84'):
			gcj_lon, gcj_lat = wgs2gcj(data[0]['GPSLongitude'], data[0]['GPSLatitude'])
			log42("gps",str(gcj_lat)+' '+str(gcj_lon))
			if (gcj_lat==data[0]['GPSLatitude']):
				log42("gps",'not in China')
			else:
				output = subprocess.check_output('"'+exiftool_path+'" -overwrite_original -exif:gpsmapdatum=GCJ-02 -exif:gpslatitude='+str(gcj_lat)+' -exif:gpslongitude='+str(gcj_lon)+' '+f, shell=True)
				log42("gps",output.decode())
		else:			
			log42("gps","not a wgs84 location")
	else:
		log42("gps","no gps data found")
		
log42("gps","task finished")

另請參考:使用ExifTool處理文件元數據wgs-84-gcj-02-經緯度在線轉換

分類
RIP

210508

今天早上去公園,連續遇到兩隻茸毛都沒長全的小鳥,死在草地上。爲了防止環衛人員把小鳥屍體當其他垃圾清理走,我把它們放到了附近的僅存的灌木叢裏。

最近整個公園整個城市都在搞綠化,附近的灌木叢悉數被連根拔起,換上了園藝草坪。草坪上不時長出會開花的野草,還需要園藝人員一顆一顆拔掉。

大樹無比幸運地雖保住了性命,但枝椏卻未能倖免。他們被修剪成乾淨利落的形狀,有些甚至只留下幾條粗壯的主幹。以防將來有一日,枯枝落下,砸傷路邊昂貴的汽車。

但現在是春夏之交。比砍樹更糟糕的就是這個時候砍樹。