A | B | C | D | E | F |
์ ํ | ํ์๋ฒํธ | ์ด๋ฆ | ํฌ์ธํธ | ์ค๋ช
| ์ํ |
โ๏ธ | 1 | ๊น์ฒ ์ | 10 | ์ถ์ | ๋๊ธฐ์ค |
โ๏ธ | 2 | ์ด์ํฌ | 10 | ์ถ์ | ๋๊ธฐ์ค |
โ | 3 | ๋ฐ๋ฏผ์ | 10 | ์ถ์ | ๋๊ธฐ์ค |
/**
* ๊ทธ๋ผ์ด๋ API ์ค์
* โ ๏ธ ์ค์ API ํค์ ํด๋์ค ID๋ก ๋ณ๊ฒฝํ์ธ์!
*/
const GROWND_API_KEY = 'sk_live_your_api_key_here'; // ๋ฐ๊ธ๋ฐ์ API ํค
const GROWND_CLASS_ID = 'your_class_id_here'; // ํด๋์ค ID
const GROWND_API_URL = 'https://growndcard.com/api/v1';
/**
* ๋ฉ๋ด์ ์ปค์คํ
๋ฒํผ ์ถ๊ฐ
*/
function onOpen() {
const ui = SpreadsheetApp.getUi();
ui.createMenu('๐ฏ ๊ทธ๋ผ์ด๋')
.addItem('์ ํํ ํ์์๊ฒ ํฌ์ธํธ ๋ถ์ฌ', 'awardPointsToSelected')
.addItem('์ ์ฒด ํ์์๊ฒ ํฌ์ธํธ ๋ถ์ฌ', 'awardPointsToAll')
.addSeparator()
.addItem('์ค์ ', 'showSettings')
.addToUi();
}
/**
* ์ ํํ ํ์์๊ฒ ํฌ์ธํธ ๋ถ์ฌ
*/
function awardPointsToSelected() {
const sheet = SpreadsheetApp.getActiveSheet();
const ui = SpreadsheetApp.getUi();
// ํ์ธ ๋ํ์์
const response = ui.alert(
'ํฌ์ธํธ ๋ถ์ฌ ํ์ธ',
'์ ํ๋ ํ์๋ค์๊ฒ ํฌ์ธํธ๋ฅผ ๋ถ์ฌํ์๊ฒ ์ต๋๊น?',
ui.ButtonSet.YES_NO
);
if (response !== ui.Button.YES) {
return;
}
// ๋ฐ์ดํฐ ๋ฒ์ ๊ฐ์ ธ์ค๊ธฐ (2ํ๋ถํฐ ์์, ํค๋ ์ ์ธ)
const dataRange = sheet.getRange(2, 1, sheet.getLastRow() - 1, 6);
const data = dataRange.getValues();
let successCount = 0;
let failCount = 0;
// ๊ฐ ํ ์ฒ๋ฆฌ
for (let i = 0; i < data.length; i++) {
const [checked, studentCode, name, points, description, status] = data[i];
// ์ฒดํฌ๋ฐ์ค๊ฐ ์ ํ๋ ํ๋ง ์ฒ๋ฆฌ
if (checked === true) {
// ์ํ ์
๋ฐ์ดํธ: ์ฒ๋ฆฌ ์ค
sheet.getRange(i + 2, 6).setValue('โณ ์ฒ๋ฆฌ ์ค...');
SpreadsheetApp.flush(); // ์ฆ์ ํ๋ฉด ์
๋ฐ์ดํธ
try {
const result = awardPointToStudent(studentCode, points, description);
if (result.success) {
sheet.getRange(i + 2, 6).setValue(`โ
${points}P ๋ถ์ฌ ์๋ฃ`);
successCount++;
} else {
sheet.getRange(i + 2, 6).setValue(`โ ${result.error}`);
failCount++;
}
} catch (error) {
sheet.getRange(i + 2, 6).setValue(`โ ${error.message}`);
failCount++;
}
SpreadsheetApp.flush();
}
}
// ๊ฒฐ๊ณผ ์๋ฆผ
ui.alert(
'ํฌ์ธํธ ๋ถ์ฌ ์๋ฃ',
`์ฑ๊ณต: ${successCount}๋ช
\n์คํจ: ${failCount}๋ช
`,
ui.ButtonSet.OK
);
}
/**
* ์ ์ฒด ํ์์๊ฒ ํฌ์ธํธ ๋ถ์ฌ
*/
function awardPointsToAll() {
const sheet = SpreadsheetApp.getActiveSheet();
const ui = SpreadsheetApp.getUi();
// ํ์ธ ๋ํ์์
const response = ui.alert(
'์ ์ฒด ํฌ์ธํธ ๋ถ์ฌ ํ์ธ',
'์ ์ฒด ํ์์๊ฒ ํฌ์ธํธ๋ฅผ ๋ถ์ฌํ์๊ฒ ์ต๋๊น?',
ui.ButtonSet.YES_NO
);
if (response !== ui.Button.YES) {
return;
}
const dataRange = sheet.getRange(2, 2, sheet.getLastRow() - 1, 5); // ์ฒดํฌ๋ฐ์ค ์ ์ธ
const data = dataRange.getValues();
let successCount = 0;
let failCount = 0;
for (let i = 0; i < data.length; i++) {
const [studentCode, name, points, description, status] = data[i];
if (studentCode && points) {
sheet.getRange(i + 2, 6).setValue('โณ ์ฒ๋ฆฌ ์ค...');
SpreadsheetApp.flush();
try {
const result = awardPointToStudent(studentCode, points, description);
if (result.success) {
sheet.getRange(i + 2, 6).setValue(`โ
${points}P ๋ถ์ฌ ์๋ฃ`);
successCount++;
} else {
sheet.getRange(i + 2, 6).setValue(`โ ${result.error}`);
failCount++;
}
} catch (error) {
sheet.getRange(i + 2, 6).setValue(`โ ${error.message}`);
failCount++;
}
SpreadsheetApp.flush();
}
}
ui.alert(
'ํฌ์ธํธ ๋ถ์ฌ ์๋ฃ',
`์ฑ๊ณต: ${successCount}๋ช
\n์คํจ: ${failCount}๋ช
`,
ui.ButtonSet.OK
);
}
/**
* ๊ฐ๋ณ ํ์์๊ฒ ํฌ์ธํธ ๋ถ์ฌ (๋ด๋ถ ํจ์)
*/
function awardPointToStudent(studentCode, points, description) {
const url = `${GROWND_API_URL}/classes/${GROWND_CLASS_ID}/students/${studentCode}/points`;
const options = {
method: 'post',
contentType: 'application/json',
headers: {
'X-API-Key': GROWND_API_KEY
},
payload: JSON.stringify({
type: 'reward',
points: points,
description: description || '์คํ๋ ๋์ํธ์์ ๋ถ์ฌ',
source: 'google_sheets'
}),
muteHttpExceptions: true
};
try {
const response = UrlFetchApp.fetch(url, options);
const statusCode = response.getResponseCode();
const contentText = response.getContentText();
// HTML ์๋ต ์ฒดํฌ (์๋ฌ ํ์ด์ง ๋ฐฉ์ง)
if (contentText.startsWith('<')) {
console.error('HTML ์๋ต:', contentText.substring(0, 200));
return {
success: false,
error: `API ์๋ต ์ค๋ฅ (์ํ ${statusCode}). URL์ ํ์ธํ์ธ์.`
};
}
const result = JSON.parse(contentText);
return result.success
? { success: true }
: { success: false, error: result.error.message };
} catch (error) {
console.error('API ํธ์ถ ์๋ฌ:', error);
return { success: false, error: error.message };
}
}
/**
* ์ค์ ๋ํ์์
*/
function showSettings() {
const ui = SpreadsheetApp.getUi();
ui.alert(
'โ๏ธ API ์ค์ ',
`ํ์ฌ ์ค์ :\n\n` +
`API ํค: ${GROWND_API_KEY.substring(0, 15)}...\n` +
`ํด๋์ค ID: ${GROWND_CLASS_ID}\n\n` +
`์ค์ ์ ๋ณ๊ฒฝํ๋ ค๋ฉด Apps Script ์ฝ๋๋ฅผ ์์ ํ์ธ์.`,
ui.ButtonSet.OK
);
}const GROWND_API_KEY = 'sk_live_xxx...'; // ๋ฐ๊ธ๋ฐ์ ์ค์ API ํค
const GROWND_CLASS_ID = 'NP0hetJ3wyQKFtRnFeftmPiy8Dl3_2'; // ์ค์ ํด๋์ค IDA | B | C | D | E | F |
์ ํ | ํ์๋ฒํธ | ์ด๋ฆ | ํฌ์ธํธ | ์ค๋ช
| ์ํ |
โ๏ธ | 1 | ๊น์ฒ ์ | 5 | 2025-01-15 ์ถ์ | |
โ๏ธ | 2 | ์ด์ํฌ | 5 | 2025-01-15 ์ถ์ | |
โ | 3 | ๋ฐ๋ฏผ์ | 0 | ๊ฒฐ์ |
A | B | C | D | E | F |
์ ํ | ํ์๋ฒํธ | ์ด๋ฆ | ํฌ์ธํธ | ์ค๋ช
| ์ํ |
โ๏ธ | 1 | ๊น์ฒ ์ | 20 | ์ํ ํด์ฆ A๋ฑ๊ธ | |
โ๏ธ | 2 | ์ด์ํฌ | 15 | ์ํ ํด์ฆ B๋ฑ๊ธ | |
โ๏ธ | 3 | ๋ฐ๋ฏผ์ | 10 | ์ํ ํด์ฆ C๋ฑ๊ธ |