Skip to content

Commit

Permalink
CLUCK v2
Browse files Browse the repository at this point in the history
  • Loading branch information
rutmanz committed Aug 2, 2024
2 parents b15df18 + a878888 commit 8fdd426
Show file tree
Hide file tree
Showing 136 changed files with 18,039 additions and 0 deletions.
47 changes: 47 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: CI

on: [push, pull_request]

jobs:
lint:
name: 'Lint'
runs-on: ubuntu-latest
permissions:
checks: write
steps:
- uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 21
cache: 'npm'
- name: Install Node Dependencies
run: npm ci

# Linting
- name: Create ESLint Report
run: node_modules/.bin/eslint --output-file eslint_report.json --format json .
continue-on-error: true
- name: Annotate Code Linting Results
uses: ataylorme/eslint-annotate-action@v3
continue-on-error: true
with:
report-json: 'eslint_report.json'
- name: Run ESLint
run: node_modules/.bin/eslint .

build:
name: 'Build'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 21
cache: 'npm'
- name: Install Node Dependencies
run: npm ci
# Build
- name: Build
run: npm run build
40 changes: 40 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# dev
.yarn/
!.yarn/releases
.vscode/*
!.vscode/launch.json
!.vscode/*.code-snippets
.idea/workspace.xml
.idea/usage.statistics.xml
.idea/shelf

# deps
node_modules/

# env
.env
.env.production

# logs
logs/
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

# misc
.DS_Store


/config/*.*
!/config/custom-environment-variables.json
!/config/example.json
/static/app.css
_*.ts
/.idea
/public/

/dev/*.manifest.json
/build/
17 changes: 17 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"trailingComma": "none",
"bracketSpacing": true,
"quoteProps": "consistent",
"tabWidth": 4,
"semi": false,
"singleQuote": true,
"printWidth": 1000,
"overrides": [
{
"files": ["**/*.ts"],
"options": {
"printWidth": 180
}
}
]
}
19 changes: 19 additions & 0 deletions LICENSE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
Copyright (c) 2023 FRC Team 1540

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
55 changes: 55 additions & 0 deletions MAINTAINING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Maintaining

## Adding Members

Visit `/admin/members/` and enter data in the bottom row. Slack IDs will automatically populate by the email

## Adjusting Seasons

Set the `start_date` field to the date you want to start tracking current hour information from, typically around kickoff or the end of summer. Any hour submissions after this point will be counted towards totals. It can be in any format accepted by the [Javascript Date constructor](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/Date)

## Refreshing Veracross photos

Open [the student directory](https://portals.veracross.com/catlin/student/directory/1) and paste the following into the browser console. You may need to disable CSP to run this script.

```js
function loadNext() {
console.log('loading more pages...')
const elem = document.querySelector('.DirectoryEntries_LoadMoreEntriesButton')
if (elem) {
elem.click()
setTimeout(loadNext, 1000)
} else {
const people = document.querySelectorAll('.directory-Entry_Header')
const data = {}
people.forEach((person) => {
const email = person.querySelector('a')?.innerHTML?.trim()
const photo = person.querySelector('.directory-Entry_PersonPhoto--full')?.src
if (email && photo) {
data[email] = photo
} else {
console.log('missing data for', person)
}
})
fetch('https://cluck.team1540.org/api/members/fallback_photos', {
method: 'POST',
headers: { 'X-Api-Key': 'YOUR-API-KEY' },
body: JSON.stringify(data)
})
.then((resp) => resp.text())
.then(console.log)
}
}

loadNext()
```
## Adding Member Fields
Additional fields not used by CLUCK can be added to the spreadsheet directly. See the [registered column](https://docs.google.com/spreadsheets/d/1p18eJW29CzLn-zZKBKm-OOM6BtR-oLlrZVfNJtNPl9A/edit?gid=568325748#gid=568325748&range=B2:B46) and ['extra' sheet](https://docs.google.com/spreadsheets/d/1p18eJW29CzLn-zZKBKm-OOM6BtR-oLlrZVfNJtNPl9A/edit?gid=2140052736#gid=2140052736) in the template spreadsheet for an example
- Hours & Certs row 3 represents the table that the column is found in
- Hours & Certs row 4 is the column name
- Hours & Certs row 5 is automatically calculated to be the column index
Make sure to update [the db model](prisma/schema.prisma), [the spreadsheet mapping](src/spreadsheet/index.ts), and the [member dashboard](src/views/admin_members) if adding new fields to CLUCK.
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# CLUCK

CLUCK is a team management tool designed by and for FRC Team 1540. It manages hour tracking and certifications, integrating with Slack and Google Sheets.

## Installation

See [SETUP.md](SETUP.md) for installation instructions.

## Feature Showcase

| Label | Image |
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Spreadsheet | [![Spreadsheet](https://github.com/user-attachments/assets/1cbaee67-7ab1-4e45-9774-339cd8300b24)](https://docs.google.com/spreadsheets/d/1p18eJW29CzLn-zZKBKm-OOM6BtR-oLlrZVfNJtNPl9A/edit?gid=568325748#gid=568325748) |
| Slack Time Logging | ![Slack](https://github.com/user-attachments/assets/80a61a51-43b1-4673-a483-513a408e6726) |
| Slack User Info | ![Slack](https://github.com/user-attachments/assets/df06e2c2-933e-4472-a1c3-cf830798bffa) |
| Member Dashboard | ![Dashboard](https://github.com/user-attachments/assets/3e1229ec-ecdf-44c8-9ab9-4ed958d4e78b) |
| Certifications Dashboard | ![Certifications](https://github.com/user-attachments/assets/cdd3dbf4-bbce-4a3b-9463-8fe612d294e5) |
55 changes: 55 additions & 0 deletions SETUP.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Setup

## Config

Set the `start_date` field to the date you want to start tracking current hour information from. This should be the start of the current season, and is used to calculate which hour logs count towards totals. It can be in any format accepted by the [Javascript Date constructor](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/Date)

Make a copy of the `./config/example.json` file as `./config/yourenvlabel.json` and adjust the fields as needed. You will need to set the environment variable `NODE_ENV='yourenvlabel'` when running to load the correct config file

Create a `.env` file in the base folder, and in it set the following DATABASE_URL variable for Prisma

```bash
DATABASE_URL="postgres://user@host/db"
```

## Database

Once you have a postgres database running, run the following to apply the Prisma schema

```bash
npx prisma migrate deploy
```

Then, to populate the database with dummy data (for development), run `dev/seed_db.ts`. This will remove existing database records, do not use in production

```bash
tsx dev/seed_db.ts
```

## Slack

If you plan to run this in a workspace with other instances, you'll need to generate a manifest with different slash command names. First, set the `slack.app.command_prefix` field in your config file, then run the following to create the customized manifest:

```bash
tsx dev/generate_manifest.ts
```

If your instance is the only one, you can use `dev/manifest.json` directly
You'll need to [create a Slack application](https://api.slack.com/apps), which you should do from the app manifest you're using.

- After you complete the setup flow, click "Install to Workspace"
- `slack.app.signing_secret` can be found in Basic Information > App Credentials > Signing Secret
- `slack.app.app_token` should be generated in Basic Information > App-Level Tokens with scope `connections:write`
- `slack.app.bot_token` can be found in OAuth & Permissions > Bot User OAuth Token

The slack user token needs to be authorized by an administrator, and requires the scopes shown in `dev/user_manifest.json`. You can either add these to the main app's manifest (if the main app will be authorized by a slack administrator) or create a separate app for the user token. The user token is used to set profile fields and usergroups by selected departments.
`slack.app.user_token` can be left as an empty string if desired to disable the profile field and department usergroup functionality, otherwise it will appear next to where the bot token was after reauthorization.

Make sure to also set the appropriate user and channel ids

## Spreadsheet

CLUCK is designed to work with a Google Sheets spreadsheet as a frontend. You can make a copy of the [template spreadsheet](https://docs.google.com/spreadsheets/d/1p18eJW29CzLn-zZKBKm-OOM6BtR-oLlrZVfNJtNPl9A/copy) and set the `google.sheet.id` field in your config file to the id in the URL.
Then, to create the credentials, follow [Google's instructions to create a service account](https://developers.google.com/workspace/guides/create-credentials#service-account), and download the `credentials.json` file. Set the `google.account.private_key` and `google.account.client_email` entries in your config file to the matching keys in `credentials.json`.

You'll need to share the spreadsheet with the email address in `client_email` as an editor to allow the bot to access it.
37 changes: 37 additions & 0 deletions config/example.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"slack": {
"app": {
"signing_secret": "XXX",
"bot_token": "xoxb-XXX",
"app_token": "xapp-X-XXX",
"user_token": "xoxp-XXX",
"command_prefix": ""
},
"profile": {
"department": "XAAAAA",
"certs": "XAAAA"
},
"channels": {
"celebration": "CXXXX",
"void": "CXXX",
"approval": "DXXX",
"certification_approval": "DXXX"
},
"groups": {
"students": "SXXX"
},
"users": {
"admins": ["UXXX"]
}
},
"google": {
"sheet_id": "",
"account": {
"private_key": "",
"client_email": ""
}
},
"start_date": "2024-06-01",
"port": 3000,
"default_photo": "/static/img/default_member.webp"
}
14 changes: 14 additions & 0 deletions dev/generate_manifest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import prod_manifest from './manifest.json'
import fs from 'fs/promises'
import config from '~config'

const dev_manifest = structuredClone(prod_manifest)
const prefix = config.slack.app.command_prefix
if (prefix) {
dev_manifest.features.slash_commands.forEach((cmd) => {
cmd.command = '/' + prefix + '_' + cmd.command.slice(1)
})
dev_manifest.display_information.name += ` (${prefix})`
dev_manifest.features.bot_user.display_name += ` (${prefix})`
}
await fs.writeFile(`./dev/${prefix}.manifest.json`, JSON.stringify(dev_manifest, null, 4))
84 changes: 84 additions & 0 deletions dev/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
{
"display_information": {
"name": "CLUCK",
"description": "A Bot for Logging Robotics Hours",
"background_color": "#2b323d"
},
"features": {
"app_home": {
"home_tab_enabled": true,
"messages_tab_enabled": true,
"messages_tab_read_only_enabled": false
},
"bot_user": {
"display_name": "Time Sheet",
"always_online": true
},
"shortcuts": [
{
"name": "Log Hours",
"type": "global",
"callback_id": "log_time",
"description": "Opens interactive hours logging popup"
}
],
"slash_commands": [
{
"command": "/log",
"description": "Log Hours and Minutes",
"usage_hint": "[hours]h [minutes]m [activity]",
"should_escape": false
},
{
"command": "/graph",
"description": "Graphs hours for members",
"usage_hint": "[@user]...",
"should_escape": true
},
{
"command": "/certify",
"description": "[Manager Only] Give a user a certification",
"should_escape": true
},
{
"command": "/clearlogin",
"description": "Logs you out, giving no hours",
"should_escape": false
},
{
"command": "/voidtime",
"description": "[Copresident Only]",
"usage_hint": "<user>",
"should_escape": true
},
{
"command": "/loggedin",
"description": "Shows who is currently signed in",
"should_escape": false
},
{
"command": "/hours",
"description": "Shows your current hour information",
"should_escape": false
},
{
"command": "/departments",
"description": "Allows you to manage your department associations",
"should_escape": false
}
]
},
"oauth_config": {
"scopes": {
"bot": ["app_mentions:read", "channels:history", "channels:join", "channels:read", "chat:write", "chat:write.public", "commands", "files:write", "groups:read", "im:history", "im:read", "im:write", "mpim:read", "usergroups:read", "users:read", "users.profile:read", "users:read.email"]
}
},
"settings": {
"event_subscriptions": {
"bot_events": ["app_home_opened", "app_mention"]
},
"org_deploy_enabled": false,
"socket_mode_enabled": true,
"token_rotation_enabled": false
}
}
Loading

0 comments on commit 8fdd426

Please sign in to comment.