Skip to content

Commit

Permalink
adding clasp files
Browse files Browse the repository at this point in the history
  • Loading branch information
bloombar committed Oct 9, 2023
1 parent 2ff8512 commit 883b3df
Show file tree
Hide file tree
Showing 5 changed files with 156 additions and 3 deletions.
1 change: 1 addition & 0 deletions automations/.clasp.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"scriptId":"1d-At2A2QTQAobdXicLsAai-_P7OrYovTa4qYOWbtV-3CjJaE8XztRgQ6","rootDir":"/Users/foobarstein/Documents/courant/courses/agile-dev-devops/generic-mern-stack-project/automations"}
85 changes: 85 additions & 0 deletions automations/Charts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
function emailSprint1ChartsToMe() {
emailChartsToMe('Sprint 1 - Team Plots')
}

function emailSprint2ChartsToMe() {
emailChartsToMe('Sprint 2 - Team Plots')
}

function emailSprint3ChartsToMe() {
emailChartsToMe('Sprint 3 - Team Plots')
}

function emailSprint4ChartsToMe() {
emailChartsToMe('Sprint 4 - Team Plots')
}

function emailChartsToMe(sheetName) {
const charts = getCharts(sheetName)
const to = Session.getActiveUser().getEmail();
const replyTo = "[email protected]"
const subject = "Agile Software Development & DevOps - Team Contribution Charts"
const message = generateEmail(charts)
const options = {
replyTo: replyTo
}

// prepare inline image object
const inlineImages = {}
charts.forEach( (chart, i) => {
inlineImages[`chart_${i}`] = chart
})

// GmailApp.sendEmail(to, subject, message, options);
// Send message with inlineImages object, matching embedded tags.
MailApp.sendEmail(to, subject, "", {
htmlBody: message,
inlineImages: inlineImages
});

Logger.log(message)
}

function generateEmail(charts) {
let imageTags = []
charts.forEach( (chart, i) => {
const tag = `<img src='cid:chart_${i}' />`
imageTags.push(tag)
})
imageTags = imageTags.join('<br />') // convert to string with line break separator
const message = `<h1>Charts</h1>${imageTags}`
return message
}

function generateHtml(sheetName, charts) {
// data:image/gif;base64,
let imageTags = []
charts.forEach( (chart, i) => {
const chartData = Utilities.base64Encode(chart.getBytes()) // get base64 encoded data for this chart
const tag = `<img src='data:image/png;base64,${chartData}' />`
imageTags.push(tag)
})
imageTags = imageTags.join('<br />') // convert to string with line break separator
const message = `<h1>${sheetName}</h1>${imageTags}`
return message
}

function getCharts(sheetName) {
// get all charts
// const sheet = SpreadsheetApp.getActiveSheet();
const ss = SpreadsheetApp.getActiveSpreadsheet() // container spreadsheet
let sheet = ss.getSheetByName(sheetName) // specific worksheet
const range = sheet.getRange("A:Z")
const charts = sheet.getCharts()

// loop through charts
const images = []
charts.forEach( (chart, i) => {
Logger.log(`chart id: ${chart.getChartId()}`)
// Logger.log(JSON.stringify(chart.getOptions(), null, 2))
const image = chart.getAs('image/png')
image.setName(`chart_${i}`)
images.push(image) // add to array
})
return images
}
56 changes: 56 additions & 0 deletions automations/Code.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
const getConfig = () => {
// global settings
return {
defaultChartSheetName: 'Sprint 1 - Team Plots',
logsSheetName: 'GitHub Logs',
logsSheetFields: ['repository', 'event', 'id', 'username', 'email', 'date', 'message', 'num_files', 'num_additions', 'num_deletions']
}
}

const getSheet = () => {
const config = getConfig()
const ss = SpreadsheetApp.getActiveSpreadsheet() // container spreadsheet
let sheet = ss.getSheetByName(config.logsSheetName) // specific worksheet
if (sheet == null) {
// create worksheet if none
sheet = ss.insertSheet(config.logsSheetName)
sheet.appendRow(config.logsSheetFields) // heading row
}
return sheet
}

function doGet(e) {
// get the sheet name with the charts from the query string
const config = getConfig()
// we expect a `sheet` query string in the request
const sheetName = (e.parameter["sheet"]) ? decodeURIComponent(e.parameter["sheet"]) : config.defaultChartSheetName
Logger.log(`Loading charts from sheet: ${sheetName}`)
charts = getCharts(sheetName)
const content = generateHtml(sheetName, charts)
return HtmlService.createHtmlOutput(content)
}

function doPost(e) {
console.log("Incoming post request")
console.log(JSON.stringify(e, null, 2))
const sheet = getSheet()
const res = {
type: 'post',
e: e
}
const commit_data = JSON.parse(e.postData.contents); // should be an array of objects
if (Array.isArray(commit_data)) {
for (let i=0; i<commit_data.length; i++) {
// log this commit!
const commit = commit_data[i]
console.log(JSON.stringify(commit, null, 2))
// append data array to sheet as new row
const row = [commit['repository_url'], commit['event_type'], commit['id'], commit['author_name'], commit['author_email'], commit['date'], commit['message'], commit['files'], commit['additions'], commit['deletions']]
sheet.appendRow(row);
}
return ContentService.createTextOutput(commit_data).setMimeType(ContentService.MimeType.JSON)
}
else {
return ContentService.createTextOutput(typeof(commit_data)).setMimeType(ContentService.MimeType.TEXT)
}
}
7 changes: 4 additions & 3 deletions automations/README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
# Automation used to track contributions
# Automation used to track developer contributions

The developer activity in this repository is tracked using a combination of custom tools. Any action on this repository triggers a sequence of automated steps:

1. A GitHub Actions [workflow](../.github/workflows/event-logger.yml) is set up to detect actions on this repository, such as push, pull request, etc.
1. This workflow executes a Python package named [gitcommitlogger](https://pypi.org/project/gitcommitlogger/) that parses the `git` logs and determines what has changed and who made the changes.
1. The Python package then posts this data to a "container-bound" [Google Apps Script](https://developers.google.com/apps-script) attached to a Google Sheet and set up as a web app to receive such incoming HTTP POST requests. The web app URL where the data is posted is stored in a [GitHub secret](https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions) named, `COMMIT_LOG_API` within this repository.
1. The Python package then posts this data to a "container-bound" [Google Apps Script](./Code.js) attached to a Google Sheet and set up as a web app to receive such incoming HTTP POST requests. The web app URL where the data is posted is stored in a [GitHub secret](https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions) named, `COMMIT_LOG_API` within this repository.
1. The Google Apps Script inserts the data posted by the Python package into a new row in the bound spreadsheet.
1. The Google Sheet contains formulae to add up the number of pull requests opened, pull requests closed, commits, files changed, lines of code added. lines of code removed, etc, by each developer, over a given time period, based on these logs.
1. The sums of these metrics are capped for each developer at reasonable, relatively low levels.
1. Based on all developers' activity, an average score for each of these metrics is computed, and each developer's own score is compared to the average of all developers.
1. A contribution score is calculated for each developer: those who perform average or better on all metrics are given a perfect score; those who perform less than average for any metric have their score scaled down in proportion to how much lower than average they performed.
1. These contribution scores are then pulled into a gradebook spreadsheet.
1. Charts of the developers' contributions over a given time period are [made automatically available](./Charts.js) in the web app via HTTP GET requests.
1. These contribution scores are also automatically pulled into a separate gradebook spreadsheet.
10 changes: 10 additions & 0 deletions automations/appsscript.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"timeZone": "America/New_York",
"dependencies": {},
"webapp": {
"executeAs": "USER_DEPLOYING",
"access": "ANYONE_ANONYMOUS"
},
"exceptionLogging": "STACKDRIVER",
"runtimeVersion": "V8"
}

0 comments on commit 883b3df

Please sign in to comment.