Skip to content

Pennon/hours reminder cron #171

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Nov 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions .github/workflows/hours-email-reminder.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: Run Email Reminder on a CRON Schedule

on:
schedule:
- cron: "0 0 1 1 *" # Jan 1st
- cron: "0 0 1 6 *" # Jun 1st
workflow_dispatch:

jobs:
send-hours-email:
runs-on: ubuntu-latest
name: A job to run the email reminder cron twice a year

steps:
- name: Checkout
uses: actions/checkout@v2

- name: Run Email Reminder Action Step
uses: ./hours-email-reminder
id: hours-email
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
DATABASE_NAME: ${{ secrets.DATABASE_NAME }}
JUNO_API_KEY: ${{ secrets.JUNO_API_KEY }}
JUNO_BASE_URL: ${{ secrets.JUNO_BASE_URL }}
JUNO_SENDER_EMAIL: ${{ secrets.JUNO_SENDER_EMAIL }}
JUNO_SENDER_NAME: ${{ secrets.JUNO_SENDER_NAME }}

keepalive-job:
name: Keepalive Workflow
runs-on: ubuntu-latest
permissions:
actions: write
steps:
- uses: actions/checkout@v4
- uses: gautamkrishnar/keepalive-workflow@2.0.7
Binary file added backend/public/healing4heroes.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions hours-email-reminder/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
DATABASE_URL=""
DATABASE_NAME=""
JUNO_API_KEY=""
JUNO_BASE_URL=""
JUNO_SENDER_EMAIL=""
JUNO_SENDER_NAME=""
1 change: 1 addition & 0 deletions hours-email-reminder/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.env
15 changes: 15 additions & 0 deletions hours-email-reminder/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Use the official Node.js image as base
FROM node:latest

# Set working directory
WORKDIR /app

# Copy package.json and package-lock.json to the working directory
COPY ./package*.json ./

# Install dependencies
RUN npm install

COPY . ./

CMD ["npx", "tsx", "/app/send-hour-email.ts"]
5 changes: 5 additions & 0 deletions hours-email-reminder/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
name: "Run Send Reminder Email"
description: "Executes the script which sends a reminder email to all users with less than 800 hours"
runs:
using: "docker"
image: "Dockerfile"
70 changes: 70 additions & 0 deletions hours-email-reminder/email.pug
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
doctype html
html(lang="en" dir="ltr" xmlns="http://www.w3.org/1999/xhtml" xmlns:o="urn:schemas-microsoft-com:office:office")
head
meta(charset="UTF-8")
meta(name="viewport" content="width=device-width, initial-scale=1")
meta(name="x-apple-disable-message-reformatting")
meta(http-equiv="X-UA-Compatible" content="IE=edge")
meta(name="format-detection" content="telephone=no")
title Copy of (2) New Message
style.
.rollover:hover .rollover-first { max-height:0px!important; display:none!important; }
.rollover:hover .rollover-second { max-height:none!important; display:block!important; }
.rollover span { font-size:0px; }
u + .body img ~ div div { display:none; }
#outlook a { padding:0; }
span.MsoHyperlink, span.MsoHyperlinkFollowed { color:inherit; mso-style-priority:99; }
a.es-button { mso-style-priority:100!important; text-decoration:none!important; }
a[x-apple-data-detectors], #MessageViewBody a { color:inherit!important; text-decoration:none!important; font-size:inherit!important; font-family:inherit!important; font-weight:inherit!important; line-height:inherit!important; }
.es-desk-hidden { display:none; float:left; overflow:hidden; width:0; max-height:0; line-height:0; mso-hide:all; }
body(style="width:100%;height:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;padding:0;margin:0")
.es-wrapper-color(style="background-color:#F6F6F6")
table.es-wrapper(role="none" cellspacing="0" cellpadding="0" width="100%" style="border-collapse:collapse;width:100%;height:100%;background-repeat:repeat;background-position:center top;background-color:#F6F6F6")
tr
td(valign="top")
table.es-header(role="none" cellspacing="0" cellpadding="0" align="center" style="width:100%;background-color:transparent")
tr
td(align="center")
table.es-header-body(role="none" cellspacing="0" cellpadding="0" align="center" bgcolor="#ffffff" style="background-color:#FFFFFF;width:600px")
tr
td(align="left" style="padding:20px 150px")
table.es-right(role="none" cellspacing="0" cellpadding="0" align="right" style="float:right")
tr
td(align="left" style="width:300px")
table(role="presentation" width="100%" cellspacing="0" cellpadding="0")
tr
td(align="center" style="font-size:0")
img.adapt-img(src="https://epiujlr.stripocdn.email/content/guids/CABINET_b671897a35158e25b88e73b31b95f12e48ace5edc8e5b734899d083c66476053/images/h4hlogo_zhD.png" alt="" width="150" height="161" style="display:block;border:0")

table.es-content(role="none" cellspacing="0" cellpadding="0" align="center" style="width:100%")
tr
td(align="center")
table.es-content-body(role="none" cellspacing="0" cellpadding="0" align="center" bgcolor="#ffffff" style="background-color:#FFFFFF;width:600px")
tr
td(align="left" style="padding:20px")
table(role="none" width="100%" cellspacing="0" cellpadding="0")
tr
td(align="center" style="width:560px")
table(role="presentation" width="100%" cellspacing="0" cellpadding="0")
tr
td(align="left")
p This is a friendly reminder that completing 800 hours of training is required in order to attend our 4-day advanced training at Camp Grace in Mobile, Alabama! Please make sure to track your progress and work towards the 800-hour goal. This training is essential to help you gain the skills and knowledge needed for success in the program. Thank you for your commitment, and we look forward to seeing you at Camp Grace!

table.es-footer(role="none" cellspacing="0" cellpadding="0" align="center" style="width:100%;background-color:transparent")
tr
td(align="center")
table.es-footer-body(role="none" cellspacing="0" cellpadding="0" align="center" bgcolor="#ffffff" style="background-color:#FFFFFF;width:600px")
tr
td(align="left" style="padding:20px")
table.es-left(role="none" cellspacing="0" cellpadding="0" align="left" style="float:left")
tr
td(align="left" class="es-m-p20b" style="width:270px")
table(role="none" width="100%" cellspacing="0" cellpadding="0")
tr
td(align="center" style="display:none")
table.es-right(role="none" cellspacing="0" cellpadding="0" align="right" style="float:right")
tr
td(align="left" style="width:270px")
table(role="none" width="100%" cellspacing="0" cellpadding="0")
tr
td(align="center" style="display:none")
135 changes: 135 additions & 0 deletions hours-email-reminder/models.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import mongoose from "mongoose";
import { HandlerType, Role, User, ServiceAnimal } from "./types";
const { Schema } = mongoose;

const UserSchema = new Schema<User>({
email: {
type: String,
required: true,
unique: true,
index: true,
},
firstName: {
type: String,
required: false,
},
lastName: {
type: String,
required: false,
},
annualPetVisitDay: {
type: Date,
required: true,
default: Date.now(),
},
nextPrescriptionReminder: {
type: Date,
required: false,
},
address: {
type: String,
required: true,
default: "",
},
firebaseUid: {
type: String,
required: true,
unique: true,
},
birthday: {
type: Date,
required: false,
},
handlerType: {
type: String,
required: false,
enum: Object.values(HandlerType),
},
roles: {
type: [String],
required: true,
enum: Object.values(Role),
default: [],
},
verifiedByAdmin: {
type: Boolean,
required: true,
default: false,
},
profileImage: {
type: String,
required: false,
},
emailVerified: {
type: Boolean,
required: true,
default: false,
},
});
export const UserModel = mongoose.models.User as mongoose.Model<User> || mongoose.model("User", UserSchema);



const AnimalSchema = new Schema<ServiceAnimal>({
handler: {
type: mongoose.Types.ObjectId,
required: true,
},
name: {
type: String,
required: true,
},
totalHours: {
type: Number,
required: true,
default: 0,
},
subHandler: {
name: {
type: String,
required: false,
},
relation: {
type: String,
required: false,
},
type: {
type: String,
required: false,
enum: Object.values(HandlerType),
},
},
dateOfBirth: {
type: Date,
required: false,
},
dateOfAdoption: {
type: Date,
required: false,
},
dateOfRabiesShot: {
type: Date,
required: false,
},
rabiesShotTimeInterval: {
type: Number,
required: false,
},
dateOfTrainingClass: {
type: Date,
required: false,
},
microchipExpiration: {
type: Date,
required: false,
},
checkUpDate: {
type: Date,
required: false,
},
profileImage: {
type: String,
required: false,
},
});
export const AnimalModel = mongoose.models.Animal as mongoose.Model<ServiceAnimal> || mongoose.model<ServiceAnimal>("Animal", AnimalSchema);
Loading
Loading