This commit is contained in:
louiscklaw
2025-01-31 19:29:24 +08:00
parent 843c590c8b
commit abff74fd77
81 changed files with 7754 additions and 0 deletions

BIN
can.f/cart-page/2022-12-09_16-41.png (Stored with Git LFS) Normal file

Binary file not shown.

View File

@@ -0,0 +1,15 @@
#!/usr/bin/env bash
rm -rf delivery.zip
mkdir -p _temp
rm -rf delivery/*
cp -r src _temp
set -ex
7za a -tzip delivery.zip src/
rm -rf _temp

BIN
can.f/cart-page/src/assets/bin.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
can.f/cart-page/src/assets/minus.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
can.f/cart-page/src/assets/plus.png (Stored with Git LFS) Normal file

Binary file not shown.

View File

@@ -0,0 +1,142 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://kit.fontawesome.com/7f7422272b.js" crossorigin="anonymous"></script>
<link rel="stylesheet" href="styles.css">
</head>
<body class="prod-page">
<div class="container">
<div class="top-label">
My Cart
</div>
<div class="prod-item-container-first">
<div class="prod">
<div class="prod-img" style="background-image: url('./product1.png');"></div>
<div style="flex-grow: 2; padding-left: 2rem">
<div class="prod-name">
Jake Duffle Bag
<div class="description">
Hamilton Brown
</div>
</div>
<div class="price">
HKD 800
</div>
<div style="padding-top: 1rem">
<div class="button-group">
<div class="qty-button-group">
<div class="minus-button"></div>
<div style="margin-left: 0.5rem">
<input class="qty" type="text" value="1" />
</div>
<div class="plus-button">
</div>
</div>
<div>
<div class="bin-button"></div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="prod-item-container">
<div class="prod">
<div class="prod-img" style="background-image: url('./product2.png');"></div>
<div style="flex-grow: 2; padding-left: 2rem">
<div class="prod-name">
Jake Duffle Bag
<div class="description">
Hamilton Brown
</div>
</div>
<div class="price">
HKD 800
</div>
<div style="padding-top: 1rem">
<div class="button-group">
<div class="qty-button-group">
<div class="minus-button"></div>
<div style="margin-left: 0.5rem">
<input class="qty" type="text" value="1" />
</div>
<div class="plus-button">
</div>
</div>
<div>
<div class="bin-button"></div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="prod-item-container">
<div class="prod">
<div class="prod-img" style="background-image: url('./product3.png');"></div>
<div style="flex-grow: 2; padding-left: 2rem">
<div class="prod-name">
Jake Duffle Bag
<div class="description">
Hamilton Brown
</div>
</div>
<div class="price">
HKD 800
</div>
<div style="padding-top: 1rem">
<div class="button-group">
<div class="qty-button-group">
<div class="minus-button"></div>
<div style="margin-left: 0.5rem">
<input class="qty" type="text" value="1" />
</div>
<div class="plus-button">
</div>
</div>
<div>
<div class="bin-button"></div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="check-out-button-container">
<button class="check-out-button">CheckOut</button>
</div>
</div>
<div class="bottom-bar-container">
<div class="bottom-bar">
<div style="padding-top: 3rem">
<i class="fa-solid fa-house fa-xl"></i>
</div>
<div style="padding-top: 3rem">
<i class="fa-regular fa-heart fa-xl"></i>
</div>
<div style="padding-top: 3rem">
<i class="fa-solid fa-cart-shopping fa-xl"></i>
</div>
<div style="padding-top: 3rem">
<i class="fa-regular fa-user fa-xl"></i>
</div>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,12 @@
{
"name": "can.f",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}

BIN
can.f/cart-page/src/product1.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
can.f/cart-page/src/product2.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
can.f/cart-page/src/product3.png (Stored with Git LFS) Normal file

Binary file not shown.

View File

@@ -0,0 +1,178 @@
.prod-page {
padding: 0;
margin: 0;
box-sizing: border-box;
height: 100vh;
}
.container {
background: #EAEAEA;
height: 100%;
padding: 1rem 1.5rem 1rem 1.5rem
}
.button-group {
display: flex;
flex-direction: row;
justify-content: space-between;
}
.qty-button-group {
display: flex;
flex-direction: row;
justify-content: space-between;
}
.top-label {
font-family: 'Cantarell';
font-style: normal;
font-weight: 700;
font-size: 32px;
line-height: 46px;
padding-top: 2rem;
padding-bottom: 2rem;
}
.prod-item-container-first{
background-color:white;
background: #FFFFFF;
border: 1px solid rgba(218, 218, 218, 0.5);
box-shadow: -1px 4px 4px 2px rgba(0, 0, 0, 0.15);
border-radius: 15px;
}
.prod-item-container{
background-color:white;
background: #FFFFFF;
border: 1px solid rgba(218, 218, 218, 0.5);
box-shadow: -1px 4px 4px 2px rgba(0, 0, 0, 0.15);
border-radius: 15px;
margin-top: 2rem
}
.prod {
height: 100px;
display: flex;
flex-direction: row;
justify-content: space-between;
justify-content: center;
align-items: center;
padding: 1rem
}
.prod-img {
width: 35%;
height: 100%;
/* background-color: gold; */
background-size: contain;
background-position:center;
background-repeat: no-repeat;
}
.prod-name {
font-family: 'Cantarell';
font-style: normal;
font-weight: 500;
font-size: 18px;
line-height: 26px;
}
.description {
font-size: 13px;
}
.price {
font-family: 'Cantarell';
font-style: normal;
font-weight: 700;
font-size: 14px;
line-height: 20px;
text-decoration-line: underline;
color: #0D0C0C;
}
.qty {
width: 45px;
height: 20px;
background: #DADADA;
border-radius: 34px;
border-width: 0;
text-align: center;
font-family: 'Bakbak One';
font-style: normal;
font-weight: 800;
font-size: 10px;
line-height: 14px;
color: #000000;
}
.minus-button {
margin-left: 0.5rem;
width: 20px;
height: 20px;
background-image: url('./assets/minus.png');
background-size: contain;
background-position:center;
background-repeat: no-repeat;
}
.plus-button {
margin-left: 0.5rem;
width: 20px;
height: 20px;
background-image: url('./assets/plus.png');
background-size: contain;
background-position:center;
background-repeat: no-repeat;
}
.bin-button {
width: 20px;
height: 20px;
background-image: url('./assets/bin.png');
background-size: contain;
background-position:center;
background-repeat: no-repeat;
}
.check-out-button-container{
padding-top: 2rem
}
.check-out-button {
width:100%;
background: #000000;
border-radius: 26px;
height:50px;
font-family: 'Cantarell';
font-style: normal;
font-weight: 500;
font-size: 20px;
line-height: 28px;
color: #FFFFFF;
}
.bottom-bar-container {
position: fixed;
bottom:0;
width: 100%;
}
.bottom-bar {
background: #000000;
display: flex;
flex-direction: row;
justify-content: space-evenly;
height: 122px;
border-radius: 15px 15px 0px 0px;
color: #D9D4C9;
}
body {
padding: 0;
margin: 0;
box-sizing: border-box;
}

7
can.f/gitUpdate.bat Normal file
View File

@@ -0,0 +1,7 @@
git status .
@pause
git add .
git commit -m"update can.f,"
start git push

5
can.f/meta.md Normal file
View File

@@ -0,0 +1,5 @@
---
tags: [html, css]
---
# can.f

BIN
catmk2/answer.png (Stored with Git LFS) Normal file

Binary file not shown.

7
catmk2/gitUpdate.bat Normal file
View File

@@ -0,0 +1,7 @@
git status .
@pause
git add .
git commit -m"update catmk2,"
start git push

BIN
catmk2/list.xlsx Normal file

Binary file not shown.

97
catmk2/meta.md Normal file
View File

@@ -0,0 +1,97 @@
---
tags: google-apps-script, rude
---
# catmk2
https://developers.google.com/apps-script/guides/services/quotas#current_limitations
PNL X Louis Coding Developing
## 想點整 ?
我講講想點整先
1. 通知報名者已獲取錄及付款
目的: 通知報名者
- 已獲取錄及
- 付款
做法:
- 頁面 '學員進度'
- 想設置 手動 觸發 (未知係咩動作)
- 欄 '通知日期' 中,該列 "變成" 觸發日期
- 欄 '繳費進度' 自己轉為 已通知
- 自動 SEND EMAIL to 同列的 '電郵地址'
回郵地址?
電郵主旨 : 取錄通知 - '報讀課程<根據 excel 表內容而定>'
> --- email 開始 (此行註釋用,不會出現在 email)
>
> '中文姓名' 您好 ,
>
> 您感興趣的 '報讀課程<根據 excel 表內容而定>', '課程班號<根據 excel 表內容而定>' 即將開辦,煩請 閣下到以下連結:
>
> '付款連結<根據 excel 表內容而定>'
>
> 在進入頁面後,按 【PAY WITH FPS】
>
> 獲得轉數快 QR CODE 後,請在 10 分鐘內繳付,否則需要重新處理頁面。
>
> 成功付款後,請保留單據紀錄並回覆本電郵。
>
> 多謝合作
>
> --- email 完 (此行註釋用,不會出現在 email)
## 1. 每一班另開新頁面
- 表單中的課程班號會因應時期,有所加增,如報滿左人就會刪除,有新班就會加選項
- 班號我地設計到不會重覆
- 頁面 '學員進度' 每次有新的 '課程班號' 出現時
- 自動開啟新頁面 命名為 '課程班號' (如果可以 SET就只抽取當左 KI2308 \*舉例 呢個就得)
- 新頁面開啟欄位 '中文姓名' '英文姓名' '手提電話' '電郵地址' 並抽取相關資料
- 之後
- 每有 相同 '課程班號' 的新列時,就會加入此頁面
- 全新 '課程班號' 時, 就會開新頁
### limitation
https://developers.google.com/apps-script/guides/services/quotas#current_limitations
### tutorial
ttps://youtu.be/1CYo1ud5NNk
基本做完 task1可以的話約個時間開個 call 我交低埋小小野比你(主要係點用)
我拍左段 walk through, 因為有個人資料所以我唔方便放係 youtube
ttps://drive.google.com/file/d/1mgNuoYIK7j1i3XQPFFyd1vKGe4x9noTq/view?usp=drive_link
請在最前面加 h
## QnA
A: 你呢個我諗最簡單係跟返你個意思做,當兩個 task 做
1. 通知報名者已獲取錄及付款
1. 每一班另開新頁面
用 MS office + excel + outlook + vba
你另外應該有個公司 email 嘛 (@companyname.com),用果個出 email 做回郵地址
每一個 task HKD500,
小修小改可以
會 troubleshoot 到係你部機做到為止

13
catmk2/package.json Normal file
View File

@@ -0,0 +1,13 @@
{
"name": "catmk2",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"gitUpdate":"git add . && git commit -m\"update catmk2,\""
},
"keywords": [],
"author": "",
"license": "ISC"
}

BIN
catmk2/task1.png (Stored with Git LFS) Normal file

Binary file not shown.

4
catmk2/task1/.clasp.json Normal file
View File

@@ -0,0 +1,4 @@
{
"scriptId": "1E3I99AvUTQ7ydwCVsuD5-OpbCJyW4LhVbo78xDQJuWWFLNGOLRzRkJ1b",
"rootDir": "/workspace/carousell-comission-playlist/catmk2/task1"
}

View File

@@ -0,0 +1,2 @@
tests
**/node_modules/**

10
catmk2/task1/Code.js Normal file
View File

@@ -0,0 +1,10 @@
function onOpen() {
const ui = SpreadsheetApp.getUi();
ui.createMenu("Automator Menu " + VER)
// .addItem("send 繳費通知 (未通知 -> 已通知)", "processPaymentNotice")
.addItem("debug-Say Hello", "sayHelloworld")
// .addItem("debug-helloWorldEmail", "helloWorldEmail")
// .addItem("debug-helloWorldWriteCell", "helloWorldWriteCell")
// .addItem("debug-processPaymentNotice", "processPaymentNotice")
.addToUi();
}

View File

@@ -0,0 +1,6 @@
{
"timeZone": "Asia/Hong_Kong",
"dependencies": {},
"exceptionLogging": "STACKDRIVER",
"runtimeVersion": "V8"
}

30
catmk2/task1/config.js Normal file
View File

@@ -0,0 +1,30 @@
const VER = "1.0.3";
const SHEET_ID = "1yHIRyYetq6SC3BZtGNVDzCfIJvuwcROnmdqyk9Jmyss";
// 學員進度
const SHEET_STUDENT_PROGRESS = "學員進度";
const COL_STUDENT_PROGRESS_CHINESE_NAME = "A"; //中文姓名
const COL_STUDENT_PROGRESS_ENGLISH_NAME = "B"; // 英文姓名
const COL_STUDENT_PROGRESS_COURSE_OFFERED = "C"; // 報讀課程
const COL_STUDENT_PROGRESS_COURSE_CODE = "D"; // 課程班號
const COL_STUDENT_PROGRESS_GENDER = "E"; // 性別
const COL_STUDENT_PROGRESS_PHONE_NUMBER = "F"; // 手提電話
const COL_STUDENT_PROGRESS_EMAIL_ADDRESS = "G"; // 電郵地址
const COL_STUDENT_PROGRESS_ATTENDANCE_PROGRESS = "H"; // 上堂進度
const COL_STUDENT_PROGRESS_PAYMENT_PROGRESS = "I"; // 繳費進度
const COL_STUDENT_PROGRESS_PAYMENT_LINK = "K"; // 付款連結
const COL_STUDENT_PROGRESS_NOTIFICATION_DATE = "J"; // 通知日期 (yyyy-mm-dd)
// const COL_STUDENT_PROGRESS_PAYMENT_PROOF_PROVIDED = "M"; // 已提供付款證明
const COL_STUDENT_PROGRESS_CERTIFICATE_REISSUE = "L"; // 證書補發
const COL_STUDENT_PROGRESS_CERTIFICATE_FEE_RECEIVED = "M"; // 己收補證書費用
const COL_STUDENT_PROGRESS_REMARKS = "O"; // 備註
const COL_STUDENT_PROGRESS_RESULT = "Y"; // RESULT
const CONST_NOT_NOTIFIED = "未通知";
const CONST_NOTIFIED_ALREADY = "已通知";
const EMAIL_QUOTA_USED_UP="對不起email 配額已用完"
const ROW_START = 2;
console.log("config initialized");

3301
catmk2/task1/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

23
catmk2/task1/package.json Normal file
View File

@@ -0,0 +1,23 @@
{
"name": "task1",
"version": "1.0.0",
"description": "",
"main": "Code.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"cP": "clasp push",
"gitUpdate": "git add . && git commit -m\"update,\"",
"watch:cP": "nodemon -L --delay 1 --exec \"npm run cP\"",
"watch:format": "npx nodemon -L --exec \"npx prettier --write . \""
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"browser-sync": "^2.29.3",
"nodemon": "^3.0.1"
},
"dependencies": {
"clasp": "^1.0.0"
}
}

1
catmk2/task1/slides_src/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
*.html

BIN
catmk2/task1/slides_src/assets/email-screenshot.png (Stored with Git LFS) Normal file

Binary file not shown.

View File

@@ -0,0 +1 @@
<svg width="98" height="96" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z" fill="#24292f"/></svg>

After

Width:  |  Height:  |  Size: 963 B

BIN
catmk2/task1/slides_src/assets/resend.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
catmk2/task1/slides_src/assets/run_script.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
catmk2/task1/slides_src/assets/send_email_done.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
catmk2/task1/slides_src/assets/wanted_row.png (Stored with Git LFS) Normal file

Binary file not shown.

View File

@@ -0,0 +1,6 @@
#!/usr/bin/env bash
set -ex
npx marp -w index.md
npx marp --pdf --allow-local-files ./index.md

View File

@@ -0,0 +1,58 @@
---
marp: true
title: Helloworld
description: Hosting Marp slide deck on the web
theme: uncover
paginate: true
_paginate: false
header: '**google apps script send email**'
footer: '![height:20px](./assets/github-mark.svg) https://louiscklaw.github.io'
---
<!-- headingDivider: 2 -->
# google apps script
send email
## 目的: 通知報名者
1. 已獲取錄
1. 付款
## 做法:
- 頁面 '學員進度'
- 欄 '通知日期' 中,該列 "變成" 觸發日期
- 欄 '繳費進度' 自己轉為 "已通知"
- 自動 SEND EMAIL 去 同列的 '電郵地址'
## requirement/需要資料:
1. 係學員進度呢個頁面度
1. 首先 "繳費進度" 要係未通知
1. 跟住 "電郵地址" 要係一個有效嘅地址
1. 加埋 "付款連結" 要係一條 link ()
![height:250px](./assets/wanted_row.png)
## how to run/點行呢?
1. click "Automator Menu"
1. click "send 繳費通知 (未通知 -> 己通知)"
![height:250px](./assets/run_script.png)
## done/行完?
**send 左 email 的話:**
1. 繳費進度 由 "未通知" -> "已通知"
1. 通知日期 會填上 "日期"
![width:900px](./assets/send_email_done.png)
## email/電郵?
![height:500px](./assets/email-screenshot.png)
## resend/s重發 email?
1. 將繳費進度由 "己通知" 轉番去 "未通知",
1. 然後再行個 script 一次 (slide 5)
![height:300px](./assets/resend.png)
## QnA/問問題?
😃

BIN
catmk2/task1/slides_src/main.png (Stored with Git LFS) Normal file

Binary file not shown.

2887
catmk2/task1/slides_src/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,18 @@
{
"name": "catmk2-presentation",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"gitUpdate": "git add . && git commit -m \"update catmk2-presentation,\" && git push",
"build": "rm -rf *.html **/*.html && yarn marp -I . -o .",
"watch": "npx nodemon -e \"*.md\" --exec yarn build"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"@marp-team/marp-cli": "^2.0.4"
}
}

Binary file not shown.

View File

@@ -0,0 +1,84 @@
const email_title = (course_title) => `${course_title} 取錄 及 付款通知`;
const email_content = (
chinese_name,
course_name,
course_code,
payment_link,
chinese_today_string
) => {
try {
var output = {
state: "init",
debug: { chinese_name, course_name, course_code, chinese_today_string },
error: {},
};
return `
<div>
${chinese_name ? chinese_name + " 您好," : "您好,"}<br />
<br />
多謝閣下報名參加&nbsp;<br />
<br />
${course_name ? "課程:&nbsp;" + course_name : ""}<br />
${course_code ? "班號:&nbsp;" + course_code : ""}<br />
<br />
很高興通知您,課程即將開辦,請閣下於盡快完成付款手續,以確認報名。<br />
<br />
<a href="${payment_link}" target="_blank">按此到 付款連結</a><br />
<br />
另煩請保留&nbsp;<underline>付款截圖</underline>&nbsp;或&nbsp;<underline>證明</underline>,貼上附件後回覆本電郵。<br />
<br />
<br />
如付款過程中遇到任何困難,煩請聯絡 9134 7967 或 <a href="">回覆</a>本電郵即可。<br />
於課程開始&nbsp;7&nbsp;天前仍未付款者,本司有權將課程名額轉讓至候補名單<br />
<br />
最後,隨信附上二維碼付款指引教學以供參考。謝謝 😊 <br />
<br />
<br />
<br />
樂歷課程報名處<br />
${chinese_today_string || ""}<br />
<br />
<br />
<br />
</div>
`.trim();
} catch (error) {
output = { ...output, error };
console.log("notification_email.js errors");
console.log(error);
}
};
const email_content1 = (chinese_name, course_name, course_code) => {
return `
<中文姓名> 你好,
多謝閣下報名參加
${course_name}
${course_code}
很高興通知您,課程即將開辦,請閣下於盡快完成付款收續,以確認報名。
繳費方式 :
請按下方連結下載轉數快/ FPS 二維碼並使用各大銀行的手機現財App 内掃描二維碼會直接付款。
<付款連結>
請保留付款截圖或證明後,貼上附件後回覆本電郵。
如付款有任何困難,請聯絡 9134 7967 或 回覆本電郵即可
如課程前7天前仍未付款者本司有權將課程名額轉讓至候補名單
最後,附上二維碼付款指引教學,以供參考。謝謝 😊
<附件 - 智豐收二維碼付款指引.pdf>
樂歷課程報名處
<今日日期>
`.trim();
};

View File

@@ -0,0 +1,12 @@
{
"name": "template",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}

View File

@@ -0,0 +1,20 @@
function appendResult(row, comment_to_write) {
var output = { state: "init", debug: { comment_to_write }, error: "" };
try {
var sheet = SpreadsheetApp.openById(SHEET_ID).getSheetByName(
SHEET_STUDENT_PROGRESS
);
var result_cell = getCell(sheet, row, COL_STUDENT_PROGRESS_RESULT);
var result_value = readCell(result_cell);
if (result_value.trim() == "") {
writeCell(result_cell, comment_to_write);
} else {
writeCell(result_cell, [result_value, comment_to_write].join("\n"));
}
} catch (error) {
output = { ...output, error };
console.log(output);
}
}

View File

@@ -0,0 +1,12 @@
function checkEmailQuotaAvailable() {
var output = { state: "init", debug: {}, error: {} };
try {
var emailQuotaRemaining = MailApp.getRemainingDailyQuota();
return emailQuotaRemaining > 0;
} catch (error) {
output = {...output , error}
console.log('checkEmailQuotaAvailable error')
console.log(output);
}
}

View File

@@ -0,0 +1,57 @@
function checkLastRow(sheet, current_row) {
// return true if considered last row, false if not
// check email column only
output = {state:'init', debug:{sheet, current_row}, error:{}}
try {
var current_row_cell = getCell(
sheet,
current_row,
COL_STUDENT_PROGRESS_EMAIL_ADDRESS
);
var current_row_cell_1 = getCell(
sheet,
current_row + 1,
COL_STUDENT_PROGRESS_EMAIL_ADDRESS
);
var current_row_cell_2 = getCell(
sheet,
current_row + 2,
COL_STUDENT_PROGRESS_EMAIL_ADDRESS
);
var current_row_cell_3 = getCell(
sheet,
current_row + 3,
COL_STUDENT_PROGRESS_EMAIL_ADDRESS
);
var current_row_cell_4 = getCell(
sheet,
current_row + 4,
COL_STUDENT_PROGRESS_EMAIL_ADDRESS
);
var current_row_cell_5 = getCell(
sheet,
current_row + 5,
COL_STUDENT_PROGRESS_EMAIL_ADDRESS
);
var check_is_empty = [
isCellEmpty(current_row_cell),
isCellEmpty(current_row_cell_1),
isCellEmpty(current_row_cell_2),
isCellEmpty(current_row_cell_3),
isCellEmpty(current_row_cell_4),
isCellEmpty(current_row_cell_5),
];
return check_is_empty.indexOf(false) < 0;
} catch (error) {
output ={...output, error}
console.log("checkLastRow: error");
console.log(output)
return false;
}
}

View File

@@ -0,0 +1,10 @@
function checkNotNotifiedForPayment(cell_value) {
var output = { state: "init", debug: {}, error: "" };
try {
return cell_value == CONST_NOT_NOTIFIED;
} catch (error) {
console.log('checkNotNotifiedForPayment error')
output = { ...output, error };
console.log(output);
}
}

View File

@@ -0,0 +1,27 @@
function checkPaymentLinkAvailable(current_row) {
var output = { state: "init", debug: { current_row }, error: {} };
try {
var sheet = SpreadsheetApp.openById(SHEET_ID).getSheetByName(
SHEET_STUDENT_PROGRESS
);
var payment_link_cell = getCell(
sheet,
current_row,
COL_STUDENT_PROGRESS_PAYMENT_LINK
);
var payment_link = readCell(payment_link_cell);
console.log({ payment_link });
if (payment_link.search(/^https?:\/\/.+/) > -1) {
return true;
}
return false;
} catch (error) {
output = { ...output, error };
console.log('checkPaymentLinkAvailable error')
console.log(output);
return false;
}
}

View File

@@ -0,0 +1,10 @@
function getCell(sheet, row, column) {
output = {state:'init', debug:{}, error:{}}
try {
var cell = sheet.getRange(column + row);
return cell;
} catch (error) {
output = {...output, error}
console.log(output);
}
}

View File

@@ -0,0 +1,3 @@
function getChineseDayString() {
return Utilities.formatDate(new Date(), "GMT+8", "yyyy 年 MM 月 dd 日");
}

View File

@@ -0,0 +1,27 @@
function getLastRow(sheet) {
var output = { state: "init", debug: {sheet}, error: "" };
try {
var last_row = -1;
var row_scan = 99999;
for (let i = 1; i < row_scan; i++) {
if (checkLastRow(sheet, i)) {
// print last row number
last_row = i - 1;
output = { ...output, debug: { ...output.debug, last_row } };
console.log("last row is " + last_row.toString());
break;
} else {
// keep going
}
}
if (last_row == -1) {
throw new Error('cannot find the last row')
}
return last_row;
} catch (error) {
console.log('getLastRow error')
output = { ...output, error };
console.log(output);
}
}

View File

@@ -0,0 +1,14 @@
function getSheetStudentProgress() {
output = {state:'init', debug:{}, error:{}}
try {
var sheet = SpreadsheetApp.openById(SHEET_ID).getSheetByName(
SHEET_STUDENT_PROGRESS
);
return sheet;
} catch (error) {
output = {...output, error}
console.log("getSheetStudentProgress error")
console.log(output);
}
}

View File

@@ -0,0 +1,12 @@
function isCellEmpty(cell) {
var output = { state: "init", debug: {}, error: {} };
try {
// console.log("isCellEmpty:" + cell.getValue());
var temp = cell.getValue().toString().trim();
return temp == "";
} catch (error) {
console.log("isCellEmpty error");
output = { ...output, error };
console.log(output);
}
}

View File

@@ -0,0 +1,51 @@
function processPaymentNotice() {
var output = { state: "init", debug: {}, error: "" };
try {
var sheet = getSheetStudentProgress();
var last_row = getLastRow(sheet);
output = { ...output, debug: { ...output.debug, last_row } };
for (var i = ROW_START; i < last_row + 1; i++) {
var email_cell = getCell(sheet, i, COL_STUDENT_PROGRESS_EMAIL_ADDRESS);
var payment_progress_cell = getCell(
sheet,
i,
COL_STUDENT_PROGRESS_PAYMENT_PROGRESS
);
var payment_progress = readCell(payment_progress_cell);
if (checkNotNotifiedForPayment(payment_progress)) {
resetResult(i);
appendResult(
i,
`not notified(${CONST_NOT_NOTIFIED}), proceed send payment notification email`
);
var quota_available = checkEmailQuotaAvailable();
var payment_link_available = checkPaymentLinkAvailable(i);
if (quota_available && payment_link_available) {
try {
sendPaymentNoticeEmail(i);
updateRowToNotificationSent(i);
} catch (error) {
Browser.msgBox("error during sending email");
}
} else {
if (quota_available < 1) {
Browser.msgBox(EMAIL_QUOTA_USED_UP);
}
if (!payment_link_available) {
appendResult(i, `payment link not exist, skipping`);
}
}
} else {
resetResult(i);
appendResult(i, `not "${CONST_NOT_NOTIFIED}" skipping`);
// var student_email = readCell(email_cell);
}
}
} catch (error) {
output = { ...output, error };
console.log(output);
}
}

View File

@@ -0,0 +1,10 @@
function readCell(cell) {
var output = { state: "init", debug: {}, error: {} };
try {
return cell.getValue() || "";
} catch (error) {
output = {...output, err: error}
console.log(error);
}
}

View File

@@ -0,0 +1,16 @@
function resetResult(row) {
var output = { state: "init", debug: {}, error: "" };
try {
var sheet = SpreadsheetApp.openById(SHEET_ID).getSheetByName(
SHEET_STUDENT_PROGRESS
);
var result_cell = getCell(sheet, row, COL_STUDENT_PROGRESS_RESULT);
return writeCell(result_cell, null);
} catch (error) {
console.log("resetResult error");
output = { ...output, error };
console.log(output);
console.log(error);
}
}

View File

@@ -0,0 +1,3 @@
function sayHelloworld() {
console.log("say helloworld");
}

View File

@@ -0,0 +1,11 @@
function sendEmail(options) {
var output = { state: "init", debug: {}, error: {} };
try {
MailApp.sendEmail(options);
output = { ...output, state: "done" };
} catch (error) {
output = { ...output, error };
console.log("sendEmail error");
console.log(output);
}
}

View File

@@ -0,0 +1,121 @@
function sendPaymentNoticeEmail(row) {
var output = { state: "init", debug: {}, error: {} };
var sheet = getSheetStudentProgress();
// var filename = "payment_guide.pdf";
var fps_tutorial_pdf_file_id = "1iD4CL8X-Nr7vfmj3UI3OzrJaKl1Gbg8tGAi6T6EmEJ0";
var fps_tutorial_pdf_file = DriveApp.getFileById(fps_tutorial_pdf_file_id);
// NOTE: student email?
var student_email_address_cell = getCell(
sheet,
row,
COL_STUDENT_PROGRESS_EMAIL_ADDRESS
);
var student_email_address = readCell(student_email_address_cell);
var student_chinese_name_cell = getCell(
sheet,
row,
COL_STUDENT_PROGRESS_CHINESE_NAME
);
var student_chinese_name = readCell(student_chinese_name_cell);
// NOTE: courase_name
var course_offered_cell = getCell(
sheet,
row,
COL_STUDENT_PROGRESS_COURSE_OFFERED
);
var course_offered = readCell(course_offered_cell);
if (course_offered == "") {
output = {
...output,
debug: {
...output.debug,
remarks: {
course_offered,
comment: "found empty",
},
},
};
}
// NOTE: course_code
var course_code_cell = getCell(sheet, row, COL_STUDENT_PROGRESS_COURSE_CODE);
var course_code = readCell(course_code_cell);
if (course_code == "") {
output = {
...output,
debug: {
...output.debug,
remarks: {
course_code,
comment: "found empty",
},
},
};
}
// NOTE: payment_link
var payment_link_cell = getCell(
sheet,
row,
COL_STUDENT_PROGRESS_PAYMENT_LINK
);
var payment_link = readCell(payment_link_cell);
if (payment_link == "") {
output = {
...output,
debug: {
...output.debug,
remarks: {
payment_link,
comment: "found empty",
},
},
};
}
// NOTE: courase_name
// NOTE: courase_name
try {
var subject = email_title(course_offered);
// var body = email_content("中文名", "chinese 中文科", "CHI001");
var htmlBody = email_content(
student_chinese_name,
course_offered,
course_code,
payment_link,
getChineseDayString()
);
var recipient = student_email_address;
if (recipient=='') throw new Error('email address not valid');
var sender = "testhelloworld04@gmail.com";
// https://developers.google.com/apps-script/reference/mail/mail-app
var options = {
bcc: sender,
replyTo: sender,
to: recipient,
subject: subject,
// body: body,
htmlBody: htmlBody,
attachments: [fps_tutorial_pdf_file.getAs(MimeType.PDF)],
};
sendEmail(options);
updateNotificationDate(row)
appendResult(row, "send email done");
output = { ...output, state: "send email done" };
} catch (error) {
output = { ...output, error };
console.log("sendEmail error");
console.log(error);
throw error;
}
}

View File

@@ -0,0 +1,15 @@
function updateNotificationDate(row) {
output = {state:"init", debug:{}, error:{}}
try {
var student_progress_sheet = SpreadsheetApp.openById(SHEET_ID).getSheetByName(
SHEET_STUDENT_PROGRESS
);
var notification_date_cell = getCell(student_progress_sheet, row, COL_STUDENT_PROGRESS_NOTIFICATION_DATE)
writeCell(notification_date_cell, getChineseDayString().replace(/ /g,''))
} catch (error) {
output = {...output, error}
console.log('updateNotificationDate error')
console.log(output)
}
}

View File

@@ -0,0 +1,17 @@
function updateRowToNotificationSent(row) {
var output = { state: "init", debug: { row }, error: {} };
try {
var sheet = getSheetStudentProgress();
var payment_progress_cell = getCell(
sheet,
row,
COL_STUDENT_PROGRESS_PAYMENT_PROGRESS
);
writeCell(payment_progress_cell, CONST_NOTIFIED_ALREADY);
return;
} catch (error) {
output = { ...output, error };
console.log(error);
}
}

View File

@@ -0,0 +1,11 @@
function writeCell(cell, content) {
var output = { state: "init", debug: {}, error: "" };
try {
cell.setValue(content);
} catch (error) {
output = {...output, error}
console.log("writeCell error");
console.log(output);
}
}

BIN
catmk2/task2.png (Stored with Git LFS) Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 584 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 344 KiB

View File

@@ -0,0 +1,18 @@
Hi Louis, 目前我們的官網 pnl.hk 遇到Error 1000 的問題
我們為了處理google workspace的驗證, 曾經按Cloudflare 的指示修改了HKDNR 內的 “名稱伺服器”,但修改後 pnl.hk 便出現 Error 1000.
為解決Error 1000問題我們已嘗試按 Cloudflare 的指引修改了A Record 及 AAAA Record, 但無果。目前我已將一切還原但官網仍顯示Error 1000
請問你知道如何解決嗎?謝謝
environment:
HKDNR (providing DNS)
cloudflare (CDN)
google workspace (providing Email)
wordpress (www hosting)

4
catmk2/update.bat Normal file
View File

@@ -0,0 +1,4 @@
git pull
git add .
git commit -m"update catmk2,"
start git push

BIN
catmk2/yMNQBdw4Cl.png (Stored with Git LFS) Normal file

Binary file not shown.

View File

@@ -0,0 +1,7 @@
git status .
@pause
git add .
git commit -m"update chillilover,"
start git push

3
chillilover/meta.md Normal file
View File

@@ -0,0 +1,3 @@
---
tags: yolo, object-detection, computer-vision
---

Binary file not shown.

After

Width:  |  Height:  |  Size: 244 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,3 @@
![](./52175ecb-6117-4373-8a90-f08912f77a24.jpeg)
![](./f8c489a9-7c8c-4893-94a0-be20ed7d898f.jpeg)
![](./f65e6f61-dcff-4c1a-80ee-088b9d81d3c4.jpeg)

Binary file not shown.

11
choco_boo/NOTES.md Normal file
View File

@@ -0,0 +1,11 @@
92505026
唔係想去攞高包或者拎咩成績㗎,我哋都冇乜參加比賽嘅經驗,佢好有興趣,所以想搵個導師教佢,去等佢學到自己有興趣同埋開心嘅課程
( python ? blockly ? )
Next Tue 20/Aug
Ok ar ,暫定 1030
## ref:
https://kidspython.com/#/

BIN
choco_boo/chrome_WcZEMvLRnZ.png (Stored with Git LFS) Normal file

Binary file not shown.

8
choco_boo/gitUpdate.bat Normal file
View File

@@ -0,0 +1,8 @@
git status .
echo "press enter to continue"
@pause
git add .
git commit -m"update choco_boo,"
start git push

View File

@@ -0,0 +1,51 @@
## 1. What is Python?
## 2. Why Python for Kids?
## 3. Getting Started with Python
### 3.1. Installing Python
### 3.2. Python IDE
### 4. Python Syntax
## 5. First Python Program Hello, World!
## 6. Python Variables
## 7. Python Datatypes
### 7.1 Int
### 7.2 Float
### 7.3 String
### 7.4 Boolean
### 7.5 List
### 7.6 Tuple
### 7.7 Dict
### 7.8 Set
## 8. Python Operators
## 9. Control Flow
### 9.1. Conditional Statements
### 9.2. Loops
## 10. Functions and Modules in Python
### 10.1 Python Functions
### 10.2 Python Modules
## 11. OOPS in Python
## 12. Fun Activities Python Projects for Kids

View File

@@ -0,0 +1,229 @@
# python tutorial list
- Python Tutorial
- Python HOME
- Python Intro
- Python Get Started
- Python Syntax
- Python Comments
## Python Variables
- Python Variables
- Variable Names
- Assign Multiple Values
- Output Variables
- Global Variables
- Variable Exercises
- Python Data Types
- Python Numbers
- Python Casting
## Python Strings
- Slicing Strings
- Modify Strings
- Concatenate Strings
- Format Strings
- Escape Characters
- String Methods
- String Exercises
## Python Booleans
- Python Operators
- Python Lists
## Python Lists
- Access List Items
- Change List Items
- Add List Items
- Remove List Items
- Loop Lists
- List Comprehension
- Sort Lists
- Copy Lists
- Join Lists
- List Methods
- List Exercises
## Python Tuples
- Access Tuples
- Update Tuples
- Unpack Tuples
- Loop Tuples
- Join Tuples
- Tuple Methods
- Tuple Exercises
## Python Sets
- Access Set Items
- Add Set Items
- Remove Set Items
- Loop Sets
- Join Sets
- Set Methods
- Set Exercises
## Python Dictionaries
- Access Items
- Change Items
- Add Items
- Remove Items
- Loop Dictionaries
- Copy Dictionaries
- Nested Dictionaries
- Dictionary Methods
- Dictionary Exercise
## Python If...Else
- Python While Loops
- Python For Loops
## Python Functions
- Python Lambda
- Python Arrays
- Python Classes/Objects
- Python Inheritance
- Python Iterators
- Python Polymorphism
- Python Scope
- Python Modules
- Python Dates
- Python Math
- Python JSON
- Python RegEx
- Python PIP
- Python Try...Except
- Python User Input
- Python String Formatting
## File Handling
- Python File Handling
- Python Read Files
- Python Write/Create Files
- Python Delete Files
## Python Modules
- NumPy Tutorial
- Pandas Tutorial
- SciPy Tutorial
- Django Tutorial
## Python Matplotlib
- Matplotlib Intro
- Matplotlib Get Started
- Matplotlib Pyplot
- Matplotlib Plotting
- Matplotlib Markers
- Matplotlib Line
- Matplotlib Labels
- Matplotlib Grid
- Matplotlib Subplot
- Matplotlib Scatter
- Matplotlib Bars
- Matplotlib Histograms
- Matplotlib Pie Charts
## Machine Learning
- Getting Started
- Mean Median Mode
- Standard Deviation
- Percentile
- Data Distribution
- Normal Data Distribution
- Scatter Plot
- Linear Regression
- Polynomial Regression
- Multiple Regression
- Scale
- Train/Test
- Decision Tree
## Confusion Matrix
- Hierarchical Clustering
- Logistic Regression
- Grid Search
- Categorical Data
- K-means
- Bootstrap Aggregation
- Cross Validation
- AUC - ROC Curve
- K-nearest neighbors
## Python MySQL
- MySQL Get Started
- MySQL Create Database
- MySQL Create Table
- MySQL Insert
- MySQL Select
- MySQL Where
- MySQL Order By
- MySQL Delete
- MySQL Drop Table
- MySQL Update
- MySQL Limit
- MySQL Join
## Python MongoDB
- MongoDB Get Started
- MongoDB Create DB
- MongoDB Collection
- MongoDB Insert
- MongoDB Find
- MongoDB Query
- MongoDB Sort
- MongoDB Delete
- MongoDB Drop Collection
- MongoDB Update
- MongoDB Limit
## Python Reference
- Python Overview
- Python Built-in Functions
- Python String Methods
- Python List Methods
- Python Dictionary Methods
- Python Tuple Methods
- Python Set Methods
- Python File Methods
- Python Keywords
- Python Exceptions
- Python Glossary
- Module Reference
- Random Module
- Requests Module
- Statistics Module
- Math Module
- cMath Module
- Python How To
- Remove List Duplicates
- Reverse a String
- Add Two Numbers
## Python Examples
- Python Examples
- Python Compiler
- Python Exercises
- Python Quiz
- Python Server
- Python Bootcamp
- Python Certificate