Skip to content

Instantly share code, notes, and snippets.

@brainysmurf
Last active October 27, 2019 04:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save brainysmurf/bc44fba06c505faa731a to your computer and use it in GitHub Desktop.
Save brainysmurf/bc44fba06c505faa731a to your computer and use it in GitHub Desktop.
Copy and paste into a Google Doc, which will turn it into a journaling activity. Requires some setup, for the document itself needs to be formatted with Header1 and Header2 in order to look good.

“Gamified Journal with Notifications" a learning tool for students. Public sample document is available here

  • Turns a Google Document into a very simple Journal
  • Reports how long students spend writing, how many words were written
  • Formats each entry with a header with today's date
  • Notify the teacher each time student adds an entry

Why:

  • Self-paced "gamified" journal writing activity
  • Authentic typing and writing practice
  • Teacher can provide timely annotations, via notification feature

Making adjustments:

  • You can change the format that the header, subheader, and body text are in by changing the font/size/style using the built-in Google Docs features of paragraph styles: See this page, heading “Customize titles, subtitles, headings and text style”.

Sample Output:

/*
CSS.html
*/
<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/themes/smoothness/jquery-ui.css">
<style>
.noDisplay {
display: none;
}
label, legend, input, div {
font-family: Calibri;
font-size: 14pt;
}
</style>
/*
JavaScript.html
*/
<!-- Different tags to ensure dependencies -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min.js"></script>
<!-- We can assume jquery/jqueryui is all good -->
<script>
<!-- Use jQuery's autocomplete to build possible list -->
<!-- https://jqueryui.com/autocomplete/ -->
$( "#emails" ).autocomplete({
source: JSON.parse( $('#emailContainer').getData('contacts') )
});
</script>
/*
Main.gs
*/
// We use a library here called "Moment"
// It's a dependency, so the below won't work until you have done this:
// Go to Resources -> Libraries and enter this product key: MHMchiX6c1bwSqGM1PZiW_PxhMjh3Sh48
var moment = Moment.load();
// Define the paragraph styles that will be used
// You access them via Google Apps Scripts through class objects such as these
// And then pass it to functions like Paragarph.setHeading
var HEADER1 = DocumentApp.ParagraphHeading.HEADING1;
var HEADER2 = DocumentApp.ParagraphHeading.HEADING2;
var NORMAL = DocumentApp.ParagraphHeading.NORMAL;
var TIMEKEY = 'timeStart';
// This include function is used in the Nofication.html file
// This allows me to include files easily, like importing
function include(filename) {
return HtmlService.createHtmlOutputFromFile(filename)
.setSandboxMode(HtmlService.SandboxMode.IFRAME)
.getContent();
}
// showAlert is used sparingly in the main code and is useful for debugging
function showAlert(title, prompt, buttons) {
var ui = DocumentApp.getUi();
var result = ui.alert(
title,
prompt,
buttons);
// Process the user's response.
if (result == ui.Button.YES) {
// User clicked "Yes".
return true;
} else {
// User clicked "No" or X in the title bar.
return false;
}
}
// showPrompt displays the prompt and processes the user's response
function showPrompt(title, prompt, buttons) {
var ui = DocumentApp.getUi(); // Same variations.
var result = ui.prompt(title, prompt, buttons);
// Process the user's response.
var button = result.getSelectedButton();
var text = result.getResponseText();
if (button == ui.Button.OK) {
return text;
} else if (button == ui.Button.CANCEL) {
return null
}
return ""
}
// This function runs automatically when the file is opened
function onOpen() {
var ui = DocumentApp.getUi();
// TODO: Figure out how to get the owner (probably using File service)
ui.createMenu('Learning Journal')
.addItem('Start…', 'start')
.addItem('Finished!', 'finish')
.addSeparator()
.addItem('Notifications', 'notify')
.addToUi();
if (showAlert(
"Are you ready to start the timer?",
'After clicking "Yes", begin writing. When you are done writing, you can click on "Learning Journal" and "Finished" to get a report of how long you wrote, and how many words you have written.',
DocumentApp.getUi().ButtonSet.YES_NO)
) {
start()
}
}
function test_notify() {
notify('hello', 'hi');
}
function notify() {
var agent = PropertiesService.getScriptProperties().getProperty('notify_email');
if (!agent) {
var prompt = "No one is currently notified. Add an email below.";
} else {
var prompt = "Right now " + agent + " receives an email. To change, type below";
}
var result = showPrompt(
"Who to notify after clicking 'Finished'?",
prompt,
DocumentApp.getUi().ButtonSet.OK_CANCEL)
if (result) {
//TODO: Validate
var scriptProperties = PropertiesService.getScriptProperties();
scriptProperties.setProperty('notify_email', result)
}
}
// Not used:
function tryThis() {
var file = DriveApp.getFileById(DocumentApp.getActiveDocument().getId());
Logger.log(file);
Logger.log(file.getOwner().getEmail());
}
//
// These functions are convenience functions that will allow us to
// to add text, very useful
//
function insertHROnTop() {
var doc = DocumentApp.getActiveDocument();
var body = doc.getBody();
body.insertParagraph(0, '\n');
body.insertHorizontalRule(0);
body.insertParagraph(0, '\n');
}
function insertTextOnTop(textToInsert, heading, select) {
var doc = DocumentApp.getActiveDocument();
var text = doc.getBody();
var par = text.insertParagraph(0, textToInsert);
par.setHeading(heading);
if (select) {
var rangeBuilder = doc.newRange();
rangeBuilder.addElement(par);
doc.setSelection(rangeBuilder.build());
}
}
// Calculates how much time has passed, using the moment library
function testCalcTimeElasped() {
var userProperties = PropertiesService.getUserProperties();
var then = moment(userProperties.getProperty('timeStart'));
var now = moment(moment().format());
var result = calcTimeElapsed(then, now);
Logger.log(result);
}
function calcTimeElapsed(now, then) {
Logger.log(then.format("HH:mm:ss"));
var duration = moment.duration(then.diff(now));
return Math.round(duration.asMinutes());
}
// Count how many words are in s
// Kinda rudementary
function countWords(s){
s = s.replace(/(^\s*)|(\s*$)/gi,"");//exclude start and end white-space
s = s.replace(/[ ]{2,}/gi," ");//2 or more space to 1
s = s.replace(/\n /,"\n"); // exclude newline with a start spacing
return s.split(' ').length;
}
// Function that collects every typed word starting at the beginning of the document
// and loops until it reaches a horizontal rule
// Returns the count and the words themselves
function words() {
var doc = DocumentApp.getActiveDocument();
var body = doc.getBody();
var finished = false;
var userEntered = "";
for (var i = 0; i < body.getNumChildren(); i++) {
var child = body.getChild(i);
if (!finished) {
userEntered += " " + child.getText();
}
if (child.getNumChildren() > 0) {
for (var c = 0; c < child.getNumChildren(); c++) {
var embeddedChild = child.getChild(c);
if (embeddedChild.getType() == DocumentApp.ElementType.HORIZONTAL_RULE) {
finished = true;
}
}
}
}
return {count: countWords(userEntered), text: userEntered};
}
function test() {
start();
finish();
}
// Starts the timer and presents sample text
function start() {
var userProperties = PropertiesService.getUserProperties();
userProperties.setProperties({'timeStart': moment().format() });
insertHROnTop();
insertTextOnTop("Start writing!", NORMAL, true);
}
// Looks at the script property "notify_email"
// And sends email
function emailAgents(body) {
var user = Session.getActiveUser();
var doc = DocumentApp.getActiveDocument();
var body = doc.getUrl() + '\n\n' + body;
var subject = '[' + doc.getName() + '] New Entry by ' + user.getEmail()
var agent = PropertiesService.getScriptProperties().getProperty('notify_email');
if (agent) {
MailApp.sendEmail(agent, subject, body);
}
Logger.log(agent);
}
// This function gets called when user clicks finished
// It collects the script property timeStart and calls the other functions appropriately
function finish() {
var userProperties = PropertiesService.getUserProperties();
var timeStart = userProperties.getProperty('timeStart');
if (timeStart != "") {
var now = moment(moment().format());
var then = moment(timeStart);
var result = calcTimeElapsed(then, now);
var wds = words();
var title = "(You spent " + result + " minutes to write " + wds.count + " words!)";
insertTextOnTop(title, HEADER2, false);
insertTextOnTop(moment().format('MMMM Do YYYY'), HEADER1, false);
// clear it to indicate usage
userProperties.setProperties({'timeStart': ""});
emailAgents(wds.text);
} else {
showAlert("Oops!", "You have to click on 'Start' in the 'Learning Journal' menu first!", DocumentApp.getUi().ButtonSet.OK);
}
}
/*
Notification.html
*/
<!DOCTYPE html>
<?!= include('CSS'); ?>
<div class="title">Notify your teacher(s) every time you finish a new entry!</div>
<div id="emailContainer" data-contacts="" class="ui-widget">
<label for="emails">Emails: </label>
<input id="emails">
</div>
<?!= include('JavaScript'); ?>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment