Skip to content
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

Auto-approve trippees #91

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open

Auto-approve trippees #91

wants to merge 10 commits into from

Conversation

ziruihao
Copy link
Contributor

@ziruihao ziruihao commented Apr 8, 2023

To test, reseed the database, then login as userId = 1 and open tripId = 5, called "POCO x NAD Sugaring Trip!🍁🥞".

  • Send "Leader 1" back to the waitlist
  • This should auto-approve "Trippee 1" and "Tripped 2"
  • Send another back to the waitlist
  • This should auto-approve "Trippee 3"

Resolves #46

@ziruihao ziruihao requested a review from alexpetros April 8, 2023 21:41
src/emails.js Show resolved Hide resolved
Comment on lines 120 to 121
export function autoApprove (tripId) {
return (db) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't think of any reason why this function should return a function instead of just being:

export function autoApprove(db, tripId)

Am I missing something?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was an overkill here, but the idea is that tripId and db are very different kinds of input. They're different in where those values are available. So tripId is readily available in any business logic, but db is provided only through the Express middleware. The idea is a function that only takes tripId may be useful in other parts of the stack that doesn't necessarily have access to db.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know what you're thinking, but this function in particular is never going to work that way, because as autoApprove, it's always going to need DB access to do its job.

src/routes/trip-members.js Outdated Show resolved Hide resolved
Comment on lines +4 to +5
ALTER TABLE trip_members
ADD COLUMN added_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't think we need this—the order they're in the table based on the ROWID is going to be the order that they were added at. To create new ROWIDs, SQLite basically does SELECT max(id) + 1.

trip_members does not have an explicit INTEGER PRIMARY KEY, but it still has a hidden one, essentially, because INTEGER PRIMARY KEY is just an alias for ROWID, which SQLite will always include unless you explicitly ask it not to.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case trip_members table has a PRIMARY KEY (trip, user) and so it ends up sorting by the trip ID then user ID, instead of order added. It definitely would've been nice to just rely on insertion order.

Copy link
Member

@alexpetros alexpetros Apr 11, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Normally you would be right, but SQLite works a little differently in this regard (namely, you always have ROWID unless you explicitly say WITHOUT ROWID). See: https://www.sqlite.org/rowidtable.html

src/templates/trip/leader-trip-card.njk Outdated Show resolved Hide resolved
@alexpetros
Copy link
Member

Also add: "Resolves: #46" at the end of the description so that it links to that issue and auto-resolves it when the PR gets merged.

export function autoApprove (tripId) {
return (db) => {
let moreToApprove = true
while (moreToApprove) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had a substantial comment that got lost somehow, sorry to add this after.

A while loop is not the way to go here, for two reasons:

  1. If there's any possibility of the while loop not terminating, it will crash the program by blocking the event loop. The other two while loops in this code have much simpler terminating conditions, and writing this makes me think I should re-evaluate them, honestly.
  2. Generally speaking, it is much simpler (and more efficient) to let SQL handle operations over multiple entries, in this case meaning "all the people who are pending admittance". Think functionally (operations over arrays) not procedurally (loops).

A better way to do it is the following:

  1. If the time is less than 24 hours from now, return
  2. Get the total number of members currently in the trip
  3. Get the number of auto approved members
  4. Let X be auto_approved_members - total_members. If X >= 0, get the pending trip members, limit X, ordered by ROWID
  5. Admit them to the trip
  6. Send them emails

No loops required (though a map or forEach is the simplest way to do the emails). I would do (4) and (5) as virtually identical statements, with (5) as an update; this is much easier than trying to do an array input to SQL. (2) and (3) can be combined into one statement pretty easily, if you want to test your SQL.

@@ -32,8 +33,20 @@ export function sendToWaitlist (req, res) {
const owner = req.db.get('SELECT owner FROM trips WHERE id = ?', tripId).owner
if (userId === owner) return res.sendStatus(400)

req.db.run('UPDATE trip_members SET pending = 1 WHERE trip = ? and user = ?', tripId, userId)
/**
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Total style nitpick, but I think you're over-explaining yourself here. Consider:

  // Remove then re-add member so that they're at the back of the auto-approval queue
  req.db.run('DELETE FROM trip_members WHERE trip = ? and user = ?', tripId, userId)
  req.db.run('INSERT INTO trip_members (trip, user, leader, pending) VALUES (?, ?, ?, ?)', tripId, userId, 0, 1)

  autoApprove(tripId)(req.db)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that I think about it, the UX is going to be pretty bad here if you're sending people to the waitlist but the auto-approval is on and it adds them right back. What do you think is the right solution to that? I think maybe we should disable "send to waitlist" entirely if the auto-approval is on.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have thought about this more and let's do it this way: can't send anyone to the waitlist if you're auto-approving. Makes everything cleaner, less likely to have unexpected behavior.

If the trip already left, show attendance record
Seed trip 5 demonstrates auto-approve feature
@alexpetros alexpetros force-pushed the main branch 2 times, most recently from 013451b to d42cb94 Compare July 12, 2023 23:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add ability to auto-approve up to X trip members
2 participants