update,
This commit is contained in:
BIN
can.f/cart-page/2022-12-09_16-41.png
(Stored with Git LFS)
Normal file
BIN
can.f/cart-page/2022-12-09_16-41.png
(Stored with Git LFS)
Normal file
Binary file not shown.
15
can.f/cart-page/build_delivery.sh
Normal file
15
can.f/cart-page/build_delivery.sh
Normal 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
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
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
BIN
can.f/cart-page/src/assets/plus.png
(Stored with Git LFS)
Normal file
Binary file not shown.
142
can.f/cart-page/src/index.html
Normal file
142
can.f/cart-page/src/index.html
Normal 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>
|
12
can.f/cart-page/src/package.json
Normal file
12
can.f/cart-page/src/package.json
Normal 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
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
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
BIN
can.f/cart-page/src/product3.png
(Stored with Git LFS)
Normal file
Binary file not shown.
178
can.f/cart-page/src/styles.css
Normal file
178
can.f/cart-page/src/styles.css
Normal 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
7
can.f/gitUpdate.bat
Normal 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
5
can.f/meta.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
tags: [html, css]
|
||||
---
|
||||
|
||||
# can.f
|
BIN
catmk2/answer.png
(Stored with Git LFS)
Normal file
BIN
catmk2/answer.png
(Stored with Git LFS)
Normal file
Binary file not shown.
7
catmk2/gitUpdate.bat
Normal file
7
catmk2/gitUpdate.bat
Normal file
@@ -0,0 +1,7 @@
|
||||
git status .
|
||||
|
||||
@pause
|
||||
|
||||
git add .
|
||||
git commit -m"update catmk2,"
|
||||
start git push
|
BIN
catmk2/list.xlsx
Normal file
BIN
catmk2/list.xlsx
Normal file
Binary file not shown.
97
catmk2/meta.md
Normal file
97
catmk2/meta.md
Normal 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
13
catmk2/package.json
Normal 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
BIN
catmk2/task1.png
(Stored with Git LFS)
Normal file
Binary file not shown.
4
catmk2/task1/.clasp.json
Normal file
4
catmk2/task1/.clasp.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"scriptId": "1E3I99AvUTQ7ydwCVsuD5-OpbCJyW4LhVbo78xDQJuWWFLNGOLRzRkJ1b",
|
||||
"rootDir": "/workspace/carousell-comission-playlist/catmk2/task1"
|
||||
}
|
2
catmk2/task1/.claspignore
Normal file
2
catmk2/task1/.claspignore
Normal file
@@ -0,0 +1,2 @@
|
||||
tests
|
||||
**/node_modules/**
|
10
catmk2/task1/Code.js
Normal file
10
catmk2/task1/Code.js
Normal 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();
|
||||
}
|
6
catmk2/task1/appsscript.json
Normal file
6
catmk2/task1/appsscript.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"timeZone": "Asia/Hong_Kong",
|
||||
"dependencies": {},
|
||||
"exceptionLogging": "STACKDRIVER",
|
||||
"runtimeVersion": "V8"
|
||||
}
|
30
catmk2/task1/config.js
Normal file
30
catmk2/task1/config.js
Normal 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
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
23
catmk2/task1/package.json
Normal 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
1
catmk2/task1/slides_src/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
*.html
|
BIN
catmk2/task1/slides_src/assets/email-screenshot.png
(Stored with Git LFS)
Normal file
BIN
catmk2/task1/slides_src/assets/email-screenshot.png
(Stored with Git LFS)
Normal file
Binary file not shown.
1
catmk2/task1/slides_src/assets/github-mark.svg
Normal file
1
catmk2/task1/slides_src/assets/github-mark.svg
Normal 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
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
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
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
BIN
catmk2/task1/slides_src/assets/wanted_row.png
(Stored with Git LFS)
Normal file
Binary file not shown.
6
catmk2/task1/slides_src/build.sh
Normal file
6
catmk2/task1/slides_src/build.sh
Normal file
@@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -ex
|
||||
|
||||
npx marp -w index.md
|
||||
npx marp --pdf --allow-local-files ./index.md
|
58
catmk2/task1/slides_src/index.md
Normal file
58
catmk2/task1/slides_src/index.md
Normal 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: ' https://louiscklaw.github.io'
|
||||
---
|
||||
|
||||
<!-- headingDivider: 2 -->
|
||||
|
||||
# google apps script
|
||||
send email
|
||||
|
||||
## 目的: 通知報名者
|
||||
|
||||
1. 已獲取錄
|
||||
1. 付款
|
||||
|
||||
## 做法:
|
||||
- 頁面 '學員進度'
|
||||
- 欄 '通知日期' 中,該列 "變成" 觸發日期
|
||||
- 欄 '繳費進度' 自己轉為 "已通知"
|
||||
- 自動 SEND EMAIL 去 同列的 '電郵地址'
|
||||
|
||||
## requirement/需要資料:
|
||||
1. 係學員進度呢個頁面度
|
||||
1. 首先 "繳費進度" 要係未通知
|
||||
1. 跟住 "電郵地址" 要係一個有效嘅地址
|
||||
1. 加埋 "付款連結" 要係一條 link ()
|
||||
|
||||

|
||||
|
||||
## how to run/點行呢?
|
||||
1. click "Automator Menu"
|
||||
1. click "send 繳費通知 (未通知 -> 己通知)"
|
||||

|
||||
|
||||
## done/行完?
|
||||
|
||||
**send 左 email 的話:**
|
||||
1. 繳費進度 由 "未通知" -> "已通知"
|
||||
1. 通知日期 會填上 "日期"
|
||||

|
||||
|
||||
## email/電郵?
|
||||

|
||||
|
||||
## resend/s重發 email?
|
||||
1. 將繳費進度由 "己通知" 轉番去 "未通知",
|
||||
1. 然後再行個 script 一次 (slide 5)
|
||||

|
||||
|
||||
## QnA/問問題?
|
||||
|
||||
😃
|
BIN
catmk2/task1/slides_src/main.png
(Stored with Git LFS)
Normal file
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
2887
catmk2/task1/slides_src/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
18
catmk2/task1/slides_src/package.json
Normal file
18
catmk2/task1/slides_src/package.json
Normal 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"
|
||||
}
|
||||
}
|
BIN
catmk2/task1/slides_src/slides.pdf
Normal file
BIN
catmk2/task1/slides_src/slides.pdf
Normal file
Binary file not shown.
84
catmk2/task1/template/notification_email.js
Normal file
84
catmk2/task1/template/notification_email.js
Normal 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 />
|
||||
多謝閣下報名參加 <br />
|
||||
<br />
|
||||
${course_name ? "課程: " + course_name : ""}<br />
|
||||
${course_code ? "班號: " + course_code : ""}<br />
|
||||
<br />
|
||||
很高興通知您,課程即將開辦,請閣下於盡快完成付款手續,以確認報名。<br />
|
||||
<br />
|
||||
<a href="${payment_link}" target="_blank">按此到 付款連結</a><br />
|
||||
<br />
|
||||
另煩請保留 <underline>付款截圖</underline> 或 <underline>證明</underline>,貼上附件後回覆本電郵。<br />
|
||||
<br />
|
||||
<br />
|
||||
如付款過程中遇到任何困難,煩請聯絡 9134 7967 或 <a href="">回覆</a>本電郵即可。<br />
|
||||
於課程開始 7 天前仍未付款者,本司有權將課程名額轉讓至候補名單<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();
|
||||
};
|
12
catmk2/task1/template/package.json
Normal file
12
catmk2/task1/template/package.json
Normal 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"
|
||||
}
|
20
catmk2/task1/utils/appendResult.js
Normal file
20
catmk2/task1/utils/appendResult.js
Normal 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);
|
||||
}
|
||||
}
|
12
catmk2/task1/utils/checkEmailQuotaAvailable.js
Normal file
12
catmk2/task1/utils/checkEmailQuotaAvailable.js
Normal 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);
|
||||
}
|
||||
}
|
57
catmk2/task1/utils/checkLastRow.js
Normal file
57
catmk2/task1/utils/checkLastRow.js
Normal 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;
|
||||
}
|
||||
}
|
10
catmk2/task1/utils/checkNotNotifiedForPayment.js
Normal file
10
catmk2/task1/utils/checkNotNotifiedForPayment.js
Normal 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);
|
||||
}
|
||||
}
|
27
catmk2/task1/utils/checkPaymentLinkAvailable.js
Normal file
27
catmk2/task1/utils/checkPaymentLinkAvailable.js
Normal 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;
|
||||
}
|
||||
}
|
10
catmk2/task1/utils/getCell.js
Normal file
10
catmk2/task1/utils/getCell.js
Normal 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);
|
||||
}
|
||||
}
|
3
catmk2/task1/utils/getChineseDayString.js
Normal file
3
catmk2/task1/utils/getChineseDayString.js
Normal file
@@ -0,0 +1,3 @@
|
||||
function getChineseDayString() {
|
||||
return Utilities.formatDate(new Date(), "GMT+8", "yyyy 年 MM 月 dd 日");
|
||||
}
|
27
catmk2/task1/utils/getLastRow.js
Normal file
27
catmk2/task1/utils/getLastRow.js
Normal 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);
|
||||
}
|
||||
}
|
14
catmk2/task1/utils/getSheetStudentProgress.js
Normal file
14
catmk2/task1/utils/getSheetStudentProgress.js
Normal 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);
|
||||
}
|
||||
}
|
12
catmk2/task1/utils/isCellEmpty.js
Normal file
12
catmk2/task1/utils/isCellEmpty.js
Normal 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);
|
||||
}
|
||||
}
|
51
catmk2/task1/utils/processPaymentNotice.js
Normal file
51
catmk2/task1/utils/processPaymentNotice.js
Normal 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);
|
||||
}
|
||||
}
|
10
catmk2/task1/utils/readCell.js
Normal file
10
catmk2/task1/utils/readCell.js
Normal 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);
|
||||
}
|
||||
}
|
16
catmk2/task1/utils/resetResult.js
Normal file
16
catmk2/task1/utils/resetResult.js
Normal 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);
|
||||
}
|
||||
}
|
3
catmk2/task1/utils/sayHelloworld.js
Normal file
3
catmk2/task1/utils/sayHelloworld.js
Normal file
@@ -0,0 +1,3 @@
|
||||
function sayHelloworld() {
|
||||
console.log("say helloworld");
|
||||
}
|
11
catmk2/task1/utils/sendEmail.js
Normal file
11
catmk2/task1/utils/sendEmail.js
Normal 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);
|
||||
}
|
||||
}
|
121
catmk2/task1/utils/sendPaymentNoticeEmail.js
Normal file
121
catmk2/task1/utils/sendPaymentNoticeEmail.js
Normal 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;
|
||||
}
|
||||
}
|
15
catmk2/task1/utils/updateNotificationDate.js
Normal file
15
catmk2/task1/utils/updateNotificationDate.js
Normal 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)
|
||||
}
|
||||
}
|
17
catmk2/task1/utils/updateRowToNotificationSent.js
Normal file
17
catmk2/task1/utils/updateRowToNotificationSent.js
Normal 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);
|
||||
}
|
||||
}
|
11
catmk2/task1/utils/writeCell.js
Normal file
11
catmk2/task1/utils/writeCell.js
Normal 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
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 |
18
catmk2/troubleshoot1/notes.md
Normal file
18
catmk2/troubleshoot1/notes.md
Normal 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
4
catmk2/update.bat
Normal 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
BIN
catmk2/yMNQBdw4Cl.png
(Stored with Git LFS)
Normal file
Binary file not shown.
7
chillilover/gitUpdate.bat
Normal file
7
chillilover/gitUpdate.bat
Normal file
@@ -0,0 +1,7 @@
|
||||
git status .
|
||||
|
||||
@pause
|
||||
|
||||
git add .
|
||||
git commit -m"update chillilover,"
|
||||
start git push
|
3
chillilover/meta.md
Normal file
3
chillilover/meta.md
Normal file
@@ -0,0 +1,3 @@
|
||||
---
|
||||
tags: yolo, object-detection, computer-vision
|
||||
---
|
BIN
chillilover/task1/52175ecb-6117-4373-8a90-f08912f77a24.jpeg
Normal file
BIN
chillilover/task1/52175ecb-6117-4373-8a90-f08912f77a24.jpeg
Normal file
Binary file not shown.
After Width: | Height: | Size: 244 KiB |
BIN
chillilover/task1/f65e6f61-dcff-4c1a-80ee-088b9d81d3c4.jpeg
Normal file
BIN
chillilover/task1/f65e6f61-dcff-4c1a-80ee-088b9d81d3c4.jpeg
Normal file
Binary file not shown.
After Width: | Height: | Size: 126 KiB |
BIN
chillilover/task1/f8c489a9-7c8c-4893-94a0-be20ed7d898f.jpeg
Normal file
BIN
chillilover/task1/f8c489a9-7c8c-4893-94a0-be20ed7d898f.jpeg
Normal file
Binary file not shown.
After Width: | Height: | Size: 102 KiB |
BIN
chillilover/task1/notes/PowerPoint Presentation.pdf
Normal file
BIN
chillilover/task1/notes/PowerPoint Presentation.pdf
Normal file
Binary file not shown.
BIN
chillilover/task1/notes/PowerPoint Presentation2.pdf
Normal file
BIN
chillilover/task1/notes/PowerPoint Presentation2.pdf
Normal file
Binary file not shown.
BIN
chillilover/task1/notes/PowerPoint Presentation3.pdf
Normal file
BIN
chillilover/task1/notes/PowerPoint Presentation3.pdf
Normal file
Binary file not shown.
3
chillilover/task1/question.md
Normal file
3
chillilover/task1/question.md
Normal file
@@ -0,0 +1,3 @@
|
||||

|
||||

|
||||

|
BIN
choco_boo/JavaScript-for-Kids.pdf
Normal file
BIN
choco_boo/JavaScript-for-Kids.pdf
Normal file
Binary file not shown.
11
choco_boo/NOTES.md
Normal file
11
choco_boo/NOTES.md
Normal 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
BIN
choco_boo/chrome_WcZEMvLRnZ.png
(Stored with Git LFS)
Normal file
Binary file not shown.
8
choco_boo/gitUpdate.bat
Normal file
8
choco_boo/gitUpdate.bat
Normal file
@@ -0,0 +1,8 @@
|
||||
git status .
|
||||
|
||||
echo "press enter to continue"
|
||||
@pause
|
||||
|
||||
git add .
|
||||
git commit -m"update choco_boo,"
|
||||
start git push
|
51
choco_boo/tutorial-python-1.md
Normal file
51
choco_boo/tutorial-python-1.md
Normal 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
|
229
choco_boo/tutorial-python-2.md
Normal file
229
choco_boo/tutorial-python-2.md
Normal 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
|
Reference in New Issue
Block a user