Swift + Apps Scripts

This commit is contained in:
Norm Rasmussen
2022-09-14 16:22:13 -04:00
parent 9bbfdebee8
commit 7c1b45c15f
27 changed files with 604 additions and 39 deletions

View File

@ -0,0 +1 @@
{"scriptId":"1NKCzBIDEvcD4HUFEXadwtoGbElLem0g0asxQO4lWjCxyp5B-yMvSZnMD","rootDir":"/Users/normrasmussen/Documents/Northpass/AppScriptsProjects"}

View File

@ -1408,7 +1408,21 @@ Andres/Paula - 8/22/22
* Users within an organization account
* MAIN: Users should not be seeing any default Northpass pages (login, enrollment emails, etc)
## Proof of Concept from HelloInsights
## Proof of Concept from HelloInsight
## Design Handoff 9/13/2022
## Hours Estimates
* They want estimates of how long eerything will take to build it all for the MVP
* Then we will decide how everything should be split up
* They don't want to burn through all 48 hours.
* API work will also take some hours
* Font Awesome icons via property to update on their own
* HOURS NEEDED:
* Templates Design: 6 hours
* Properties & Logic (ie. static completions): 3 hours
* LX Design: 8 with quiz styling
* LX Quiz:HARD NO SAYS KAITLYN TBD
# NP Proof of Concept
As a software engineer, I want to implement a proof of concept integration of Northpass, so that I can better understand the effort level required to build the basic functionality necessary for Q1 2022/23 as well as test some functionality that will be implemented in Q2 and beyond.

BIN
Google_Scripts/.DS_Store vendored Normal file

Binary file not shown.

View File

@ -0,0 +1 @@
{"scriptId":"1TJplbGBnAl-BN8Kuw5jrC3b8LHCSmkrZUvG_KDMTgwQBfESlWL2lDJd4","rootDir":"/Users/normrasmussen/Documents/Northpass/Google_Scripts/AE_Reminders"}

View File

@ -0,0 +1,217 @@
/*
This script sends a daily reminder to the Sales and Marketing channel as to which
lines of data are unclean and not filled out. Currently, it is only pulling from the last 5 days.
This will be sent out between 3-4pm so that AEs can get their data in and clean before EOD.
Any questions or changes needed, see Norm.
Webhook URL in this sheet is for #sales-n-marketing channel.
*/
// Setup of the sheet
const sheet = SpreadsheetApp.getActiveSheet();
// Setup of the date range to compare. Currently, it is using 5 days until Present.
var now = new Date();
var formatNow = Utilities.formatDate(now, 'America/New_York', 'MM/dd/yyyy'); // Today
var daysToSubtract = 2;
var withinWeek = new Date(now.getTime()-daysToSubtract*(3600*24*1000));
var formatWeek = Utilities.formatDate(withinWeek, 'America/New_York', 'MM/dd/yyyy'); // 5 Days ago
// Counter for formatting the final list
var meetingCount = 0;
// Other empty Globals
var finalAEList;
var tagUsers;
/*
This function will create two empty arrays, one for the list of missed entries and one for tagging users in Slack
First, the function gets the data ranges in spreadsheed and adds those columns to an array index.
Second, the for loop cycles through the super long array (4 results per line). Based on the if statement,
the loop removes all arrays that don't fit the statement.
*/
function findMeetings() { // Setting up data range and empty arrays
var startRow = 2; // First row of data to process
var numRows = sheet.getLastRow()-1; // Number of rows to process
var dataRange = sheet.getRange(startRow, 1, numRows, sheet.getLastColumn());
var data = dataRange.getValues();
var slackingAEListOne = [];
var tagList = [];
for (i in data) { // For a data row within the entire data range
var row = data[i];
let attended = row[6]; // Column G
var name = row[1]; // Column - B
var date = Utilities.formatDate(row[3],'America/New_York','MM/dd/yyyy'); // Column - D
var company = row[4]; // Column - E
let missedEntries = [name, date, company, attended];
/*
Adding a For Loop will pull a result for EACH element, aka 4 results per line.
This pulls one for each group of missedEntries
This then removes the last value (attended, since we already know it is a blank), converts to a string,
and adds to a new array. The counter will compare if there is more than one entry in the array.
*/
if ((missedEntries[3] == "") && (missedEntries[1] >= formatWeek) && (missedEntries[1] <= formatNow)) {
meetingCount += 1;
missedEntries.pop();
missedEntries.toString();
slackingAEListOne.push(missedEntries);
//Logger.log(slackingAEListOne);
};
};
/* Now outside of the if statement:
the array is built, and we want each group to be on a new line, remove the commas and add a hyphen.
The counter counts if there is 1 or more meetings, or not. If 0, it sends a certain message, not tagging anyone.
If it is one or more, it splices by the first array (index 0), adds a new line, and replaces commans with hyphens.
*/
if (meetingCount >= 1) {
var slackingAEListTwo = slackingAEListOne.splice(0).join('\n');
var finalAEList = slackingAEListTwo.replace(/,/g, ' - ');
// This if statement is going to only tag those who appear in the previous list.
// No need to tag people who have done their work and contribute to clean data.
if (finalAEList.includes('Norm')) {
tagList.push('<@U020KRBDSDQ>');
}
if (finalAEList.includes('Dan')) {
tagList.push('<@U01P7DTFSQZ>');
}
if (finalAEList.includes('Charles')) {
tagList.push('<@U01286MQUS2>');
}
if (finalAEList.includes('Travis')) {
tagList.push('<@UFE3T14UX>');
}
if (finalAEList.includes('Nick')) {
tagList.push('<@U0276LMA70F>');
}
if (finalAEList.includes('Mike')) {
tagList.push('<@U027MAQUPM0>');
}
if (finalAEList.includes('Doug')) {
tagList.push('<@U02CK55FHFX>');
}
if (finalAEList.includes('Isabel')) {
tagList.push('<@U03019S7R6F>');
}
if (finalAEList.includes('Ryan')) {
tagList.push('<@U0325CWA3N3>');
}
var tagUsers = tagList.toString();
/*
Now, we're building the payload for the Slack Message.
This is very specific and prone to errors, so check that it works using Slack's tool:
https://app.slack.com/block-kit-builder/
Copy from Rich:
":rotating_light::rotating_light::rotating_light: New Meeting Tracker Alert! :rotating_light::rotating_light::rotating_light:
It's your daily reminder to update the new meeting tracker.
If you've been tagged, please address this by EOD."
*/
let payloadText =
{
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": ":rotating_light::rotating_light::rotating_light: New Meeting Tracker Alert! :rotating_light::rotating_light::rotating_light:"
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "It's your daily reminder to update the new meeting tracker."
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "If you've been tagged, please address this by EOD."
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": finalAEList // Sends list with AE, meeting date, and company
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "In case you need it, here's a link to the sheet. :point_right:"
},
"accessory": {
"type": "button",
"text": {
"type": "plain_text",
"text": "Sales New Meeting Tracker",
"emoji": true
},
"value": "sales_tracker_link_123",
"url": "https://docs.google.com/spreadsheets/d/150nSuHQLJHpJaYdQ6KHeN7nQ6iexwtnQEpicM77PK8A/edit#gid=0 | First Meeting Tracker",
"action_id": "button-action"
}
},
{
"type": "divider"
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": tagUsers // Tags with slack user numbers if they appear in finalSend
}
}
]
};
// This is standard operating procedure to creating the payload and destination
const webhook = "https://hooks.slack.com/services/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";
const options = {
method: "post",
contentType: "application/json",
muteHttpExceptions: true,
payload: JSON.stringify(payloadText),
};
const sendMsg = UrlFetchApp.fetch(webhook, options);
var respCode = sendMsg.getResponseCode();
//Logger.log(sendMsg); // Debug to confirm send
//Logger.log(respCode); // Debug to show errors, if any
/*
This is the else statement that sends a certain message not tagging anyone and saying that data is clean.
*/
} else {
let noMeetingMsg =
{
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": ":star::star::star: New Meeting Tracker Alert! :star::star::star:"
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "Good job, everyone! All meetings from the last 5 days are up to date. Keep doing what you're doing!"
}
}
]
};
// This is standard operating procedure to creating the payload and destination
const webhook = "https://hooks.slack.com/services/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";
const options = {
method: "post",
contentType: "application/json",
muteHttpExceptions: true,
payload: JSON.stringify(noMeetingMsg),
};
const sendMsg = UrlFetchApp.fetch(webhook, options);
var respCode = sendMsg.getResponseCode();
//Logger.log(sendMsg); // Debug to confirm send
//Logger.log(respCode); // Debug to show errors, if any
}
};

View File

@ -0,0 +1,7 @@
{
"timeZone": "America/New_York",
"dependencies": {
},
"exceptionLogging": "STACKDRIVER",
"runtimeVersion": "V8"
}

View File

@ -0,0 +1 @@
{"scriptId":"1bG7euzp-k-XWRrCkTEwdOLYxuLgIMgzKcfvWjdOKU226uYPC7aTU_JfJ","rootDir":"/Users/normrasmussen/Documents/Northpass/Google_Scripts/Dept_Calendar"}

View File

@ -0,0 +1,27 @@
function createEvent() {
let calendarID = '';
let eventName = '';
let startTime = '';
let endTime = '';
let zoomLink = '';
var event = CalendarApp.getCalendarById(calendarID).create(
eventName, startTime, endTime, zoomLink);
eventID = event.getID();
}
function addLearner(eventID, calendarID) {
let attendeeEmail = '{{ current_person.email }}';
let calendar = calendarID;
let event = eventID;
let basCalendar = CalendarApp.getCalendarById(calendar);
if (basCalendar == null) {
return;
}
let currentEvent = calendar.getEventById(event);
if (currentEvent == null) {
return;
}
event.addGuest(attendeeEmail);
}

View File

@ -0,0 +1,7 @@
{
"timeZone": "America/New_York",
"dependencies": {
},
"exceptionLogging": "STACKDRIVER",
"runtimeVersion": "V8"
}

View File

@ -0,0 +1 @@
{"scriptId":"11ghDxFT1kf_3PLsjOUcd3isFIXoJtyNzu2mEbhdci53nJXYTpSRHYJxl","rootDir":"/Users/normrasmussen/Documents/Northpass/Google_Scripts/SDR_Meetings"}

View File

@ -0,0 +1,110 @@
const sheetTwo = SpreadsheetApp.getActiveSheet();
var now = new Date();
var formatToday = Utilities.formatDate(now, 'America/New_York', 'MM/dd/yyyy');
function newMeetings() {
var startRow = 2; // First row of data to process
var numRows = sheetTwo.getLastRow()-1; // Number of rows to process
var dataRange = sheetTwo.getRange(startRow, 1, numRows, sheet.getLastColumn());
var data = dataRange.getValues();
var sdrMeeting = [];
for (i in data) {
var row = data[i];
var sdr = row[0]; // Column A
var date = Utilities.formatDate(row[3],'America/New_York','MM/dd/yyyy'); // Column D
var company = row[4]; // Column E
var outbound = row[5]; // Column F
let sdrMeetingListOne = [sdr, date, company, outbound];
if ((sdrMeetingListOne[1] == formatToday) && (sdrMeetingListOne[0] != '') && (sdrMeetingListOne[3] == "Outbound")) {
sdrMeetingListOne.toString();
sdrMeeting.push(sdrMeetingListOne);
Logger.log(sdrMeetingListOne)
} else if ((sdrMeetingListOne[1] == formatToday) && (sdrMeetingListOne[0] != '')) {
let noMeetingText =
{
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": ":star: *C'mon team!* :star:"
}
},
{
"type": "divider"
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "No new meetings today? We can do better than that! Hustle!"
}
},
]
}
const webhook = "https://hooks.slack.com/services/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";
const options = {
method: "post",
contentType: "application/json",
muteHttpExceptions: true,
payload: JSON.stringify(noMeetingText),
}
const sendMsg = UrlFetchApp.fetch(webhook, options);
var respCode = sendMsg.getResponseCode();
Logger.log(sendMsg);
Logger.log(respCode);
}
};
Logger.log(sdrMeeting);
var toSend = sdrMeeting.splice(1).join('\n');
var getEm = toSend.replace(/,/g, ' - ')
Logger.log(getEm)
/*
let payloadMeetingText =
{
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": ":star: *Pitter Patter!* :star:"
}
},
{
"type": "divider"
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "Here's who has meetings today. Get after it boys and girls!"
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": getEm
}
},
{
"type": "image",
"image_url": "https://media.giphy.com/media/3ohjURBuAtWDV8d3SE/giphy.gif",
"alt_text": "inspiration"
}
]
}
//(array[0] + ", you still need to fill out the cells for" + array[2] + " from " + array[1] + "!\n");
const webhook = "https://hooks.slack.com/services/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";
const options = {
method: "post",
contentType: "application/json",
muteHttpExceptions: true,
payload: JSON.stringify(payloadText),
};
const sendMsg = UrlFetchApp.fetch(webhook, options);
var respCode = sendMsg.getResponseCode();
Logger.log(sendMsg);
Logger.log(respCode);*/
};

View File

@ -0,0 +1,7 @@
{
"timeZone": "America/New_York",
"dependencies": {
},
"exceptionLogging": "STACKDRIVER",
"runtimeVersion": "V8"
}

View File

@ -0,0 +1 @@
{"scriptId":"1VUbCbC6whkodvYSfTSmaFCNVxowtahxUHaKcr7Dg_Ly0vevBLqlDWmdS","rootDir":"/Users/normrasmussen/Documents/Northpass/Google_Scripts/Sheets_to_Jira"}

View File

@ -0,0 +1,119 @@
/* Pertinent Information and Codes:
Custom Fields:
Channel = customfield_10121
Campaign = customfield_10120
SDR = customfield_10122
Start Date = customfield_10015
Users:
Travis Nardin = 5d9cb42c0265ca0db955b965
Dan Peski = 603318815ddf020069969cad
Norm Rasmussen = 6092af20d353800068863d15
Michael Valido = 61fbf60cd8d7cf006a90941c
Jon Newfield = 6092af212c2f6c0068ec92c4
Isabel Katz = 620145241fec260068c107e5
Charles McGovern = 5eaaf1c4021ae30ba8fcb184
Nick Zuppe = 60eddda64257a90070aeebef
Doug Goldsmith = 61294a4845f753006951a590
Nick Appleby = 6183eab7892c420072f9c437
Quba Williams-Wilfong = 62101fe4e41f76006a6f6510
Without accounts: Adan, Drew
*/
function myFunction() {
var URL = "https://northpass.atlassian.net/rest/api/3/issue/";
var token = "2NrKYv22TLWnxTo7EhU3633E";
var user = "nrasmussen@northpass.com";
var UserCredentials = Utilities.base64Encode(user + ":" + token);
// Setting up data range and empty arrays
const sheet = SpreadsheetApp.getActiveSheet();
var startRow = 2; // First col of data to process
var numRows = 1132; // Number of rows to process
var dataRange = sheet.getRange(startRow, 1, numRows, sheet.getLastColumn()).getValues();
for (col in dataRange) { // For a data col within the entire data range
var col = dataRange[col];
let sdr = col[0]; // Column - A
let name = col[1]; // Column - B
let bookingDate = Utilities.formatDate(col[2], "America/New_York", "MM/dd/yyyy"); // Column - C
let meetingDate = Utilities.formatDate(col[3], "America/New_York", "MM/dd/yyyy"); // Column - D
let rawDate = Utilities.formatDate(col[3], "America/New_York", "yyyy-MM-dd");
let company = col[4]; // Column - E
let channel = col[5]; // Column - F
let result = col[7]; // Column - H
let campaign = col[8]; // Column - I
let info = col[9]; // Column - J
let list = [sdr, name, rawDate, bookingDate, meetingDate, company, channel, result, campaign, info];
let sdrDict = {
"Mike" :
"61fbf60cd8d7cf006a90941c",
"Nick" :
"60eddda64257a90070aeebef",
"Appleby" :
"6183eab7892c420072f9c437",
"Doug" :
"61294a4845f753006951a590",
"Quba" :
"62101fe4e41f76006a6f6510",
};
if (list.includes("Norm")) {
var data = {
"fields": {
"project": {
"key": "NPIPE",
},
"issuetype": {
"id": "10275",
},
"summary": list[5],
"description": {
"type": "doc",
"version": 1,
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "Booked Date - " + list[3] + '\n' +
"Meeting Date - " + list[4] + '\n' +
"Description from Tracker: " + list[9] + '\n' +
"Last Status - " + list[7]
}
]
}
],
},
"customfield_10015": list[2], // Start (Meeting) Date - rawDate
"customfield_10127": [ // Campaign
list[8],
],
"customfield_10128": [ // Channel
list[6],
],
"customfield_10129": [{"id" : sdrDict[list[0]]}], // SDR
}
},
payload = JSON.stringify(data);
Logger.log(payload);
var headers = {
"Accept": "application/json",
"Content-Type": "application/json",
"Authorization": "Basic " + UserCredentials,
"muteHttpExceptions": true,
}
var options = {
"method": "POST",
"headers": headers,
"payload": payload
}
//Logger.log(options);
var response = UrlFetchApp.fetch(URL, options);
//Logger.log(response);
var respCode = response.getResponseCode();
//Logger.log(respCode);
}
}
};

View File

@ -0,0 +1,7 @@
{
"timeZone": "America/New_York",
"dependencies": {
},
"exceptionLogging": "STACKDRIVER",
"runtimeVersion": "V8"
}

View File

@ -0,0 +1 @@
{"scriptId":"1NKCzBIDEvcD4HUFEXadwtoGbElLem0g0asxQO4lWjCxyp5B-yMvSZnMD","rootDir":"/Users/normrasmussen/Documents/Northpass/Google_Scripts/WCA_Webhook"}

View File

@ -0,0 +1,47 @@
function doGet(e) {
return HtmlService.createHtmlOutput("get request received");
}
//this is a function that fires when the webapp receives a POST request
function doPost(e) {
var params = JSON.stringify(e.postData.contents);
params = JSON.parse(params);
var now = new Date();
var timeAdded = Utilities.formatDate(now,'America/New_York','MM/dd/yyyy');
var learnerName = params.substring(params.indexOf('{"name":"') + 7, params.lastIndexOf('"email"'));
learnerName = learnerName.slice(learnerName.indexOf('"') + 1, learnerName.lastIndexOf('"'),);
//Logger.log("Name = " + learnerName);
var learnerEmail = params.substring(params.indexOf('"sso_uid":"') + 9, params.lastIndexOf('"full_name"'));
learnerEmail = learnerEmail.slice(learnerName.indexOf('"') + 3, learnerEmail.lastIndexOf('"'),);
//Logger.log("Email = " + learnerEmail);
var courseName = params.substring(params.indexOf('"course_name":"') + 4, params.lastIndexOf('"attributes"'));
courseName = courseName.slice(courseName.indexOf('"') + 3, courseName.lastIndexOf('"'),);
//Logger.log("Course = " + courseName);
var activityName = params.substring(params.indexOf('"title":"') + 4, params.lastIndexOf('"course_name"'));
activityName = activityName.slice(activityName.indexOf('"') + 3, activityName.lastIndexOf('"'),);
//Logger.log("Activity = " + activityName);
var dateCompleted = params.substring(params.indexOf('"updated_at":"') + 4, params.lastIndexOf('"activated_at"'));
dateCompleted = dateCompleted.slice(dateCompleted.indexOf('"') + 3, dateCompleted.lastIndexOf('"'),);
//Logger.log("Completed on = " + dateCompleted);
var sheet = SpreadsheetApp.getActiveSheet();
var lastRow = Math.max(sheet.getLastRow(),1);
sheet.insertRowAfter(lastRow);
sheet.getRange(lastRow + 1, 1).setValue(timeAdded);
sheet.getRange(lastRow + 1, 2).setValue(learnerName);
sheet.getRange(lastRow + 1, 3).setValue(learnerEmail);
sheet.getRange(lastRow + 1, 4).setValue(courseName);
sheet.getRange(lastRow + 1, 5).setValue(activityName);
sheet.getRange(lastRow + 1, 6).setValue(dateCompleted);
sheet.getRange(lastRow + 1, 7).setValue(params);
SpreadsheetApp.flush();
return HtmlService.createHtmlOutput("post request received");
}

View File

@ -0,0 +1,10 @@
{
"timeZone": "America/New_York",
"dependencies": {},
"exceptionLogging": "STACKDRIVER",
"runtimeVersion": "V8",
"webapp": {
"executeAs": "USER_DEPLOYING",
"access": "ANYONE_ANONYMOUS"
}
}

View File

@ -1,34 +0,0 @@
<div class="np-card">
<div class="np-card-container">
{% if course.ribbon %}
<div class="np-card-ribbon">
{{ course.ribbon }}
</div>
{% endif %}
<img
class="np-card-image"
alt="{{ course.name }}"
src="{{ course.image_url }}"
>
<div class="np-card-content np-card-content-vertical np-card-padding">
<h3 class="np-card-content-title">
{{ course.name }}
</h3>
<div class="np-card-content-subtitle">
{{ course.instructor_names }}
</div>
<div class="np-card-content-footer">
<div class="np-card-content-progress np-button-color">
{% t shared.progress, count: course.progress %}
</div>
{% capture course_path %}{% route course, id: course.id %}{% endcapture %}
{% if course.has_to_restart? %}
{% include "course_version_outdated_popup", path: course_path %}
{% endif %}
<a class="np-button np-button-wide" href="{{ course_path }}">
{% t shared.view %}
</a>
</div>
</div>
</div>
</div>

View File

@ -1,8 +1,7 @@
{% if courses.in_catalog.any? %}
<div class="np-catalog-courses row row-with-thumbnails">
{% assign sorted_courses = courses.in_catalog | map: "properties" | sort: "course_order" %}
{% for course in sorted_courses %}
<div class="col-xs-12 col-md-6 col-lg-4 np-stretch-content">
<div class="np-catalog-courses row row-with-thumbnails" id="catalog-courses">
{% for course in courses.in_catalog %}
<div class="col-xs-12 col-md-6 col-lg-4 np-stretch-content" order="{{course.properties.course_order}}">
{% include "cards_course" with course %}
</div>
{% endfor %}
@ -15,3 +14,25 @@
{% endcapture %}
{% include "courses_zero_state", message: message %}
{% endif %}
<script>
var list = document.getElementById('catalog-courses');
var items = list.childNodes;
var itemsArr = [];
for (var i in items) {
if (items[i].nodeType == 1) {
itemsArr.push(items[i]);
}
}
itemsArr.sort(function (a, b) {
return a.getAttribute('order') == b.getAttribute('order')
? 0
: (a.getAttribute('order') > b.getAttribute('order') ? 1 : -1);
});
for (i = 0; i < itemsArr.length; ++i) {
list.appendChild(itemsArr[i]);
}
</script>