Browse Source

Moving the actual merge logic into the back-end, which makes live updating easier

Kirk Trombley 5 years ago
parent
commit
dc9028d9ad
4 changed files with 62 additions and 61 deletions
  1. 1 1
      README.md
  2. 23 35
      client/src/App.js
  3. 2 0
      client/src/api.js
  4. 36 25
      server/app.py

+ 1 - 1
README.md

@@ -45,5 +45,5 @@ Accepts
 }
 ```
 Returns
-204 - If the data was accepted
+200 - If the data was accepted, with a JSON body equivalent to the `GET` above
 400 - Malformed request

+ 23 - 35
client/src/App.js

@@ -1,12 +1,12 @@
 import React, { useState, useEffect } from 'react';
-import { getAvailability } from './api';
+import { getAvailability, setAvailability } from './api';
 
 const NameForm = ({ onNameSet }) => {
   const [name, setName] = useState("");
 
   return (
     <div>
-      <form onSubmit={() => onNameSet(name)}>
+      <form onSubmit={() => onNameSet(name.trim())}>
         <input 
           type="text" 
           onChange={({ target }) => setName(target.value)}
@@ -80,43 +80,31 @@ const chunk = (arr, len) => {
   return chunks;
 }
 
-const advanceDay = (dates, name, month, day) => {
-  const dateInd = dates.findIndex(item => item.month === month && item.day === day);
-  if (dateInd === -1) {
-    return dates;
-  }
-  const leftDates = dates.slice(0, dateInd);
-  const date = dates[dateInd];
-  const rightDates = dates.slice(dateInd + 1);
-  const avInd = date.availability.findIndex(item => item.name === name);
-
-  let otherAvail;
-  let status;
-  if (avInd === -1) {
-    otherAvail = date.availability;
-    status = nextState.unknown;
-  } else {
-    const leftAvail = date.availability.slice(0, avInd);
-    const rightAvail = date.availability.slice(avInd + 1);
-    otherAvail = [...leftAvail, ...rightAvail];
-    status = nextState[date.availability[avInd].status];
-  }
-  return [...leftDates, {...date, availability: [{ name, status }, ...otherAvail]}, ...rightDates];
-}
-
-const advanceWeek = (dates, name, dateRange) => dateRange.reduce((acc, { month, day }) => advanceDay(acc, name, month, day), dates)
-
 function App() {
   const [name, setName] = useState(null);
-  const [avail, setAvail] = useState(null);
-  useEffect(() => { getAvailability().then(({availability}) => setAvail(availability)) }, []);
+  const [calendar, setCalendar] = useState(null);
+  useEffect(() => { getAvailability().then(({availability}) => setCalendar(availability)) }, []);
+  const advance = async (dates) => {
+    const updates = dates.map(({ month, day }) => {
+      const currentStatus = calendar
+        .find(item => item.month === month && item.day === day)
+        ?.availability
+        ?.find(item => item.name === name)
+        ?.status 
+        ?? "unknown"
+      const status = nextState[currentStatus] || "unknown";
+      return { month, day, status }
+    });
+    const { availability } = await setAvailability(name, updates);
+    setCalendar(availability);
+  }
 
 
   if (name === null) {
     return <NameForm onNameSet={setName} />
   }
 
-  if (avail === null) {
+  if (calendar === null) {
     return <span>Loading...</span>
   }
 
@@ -127,9 +115,9 @@ function App() {
         flexFlow: "column nowrap",
       }}
     >
-      <span>I will plan this vacation if it kills me.</span>
+      <span>Logged in as: {name}</span>
       {
-        chunk(avail, 7).map((row, ind) => (
+        chunk(calendar, 7).map((row, ind) => (
           <div
             key={ind} 
             style={{
@@ -139,7 +127,7 @@ function App() {
             }}
           >
             <div
-              onClick={() => setAvail(advanceWeek(avail, name, row))}
+              onClick={() => advance(row)}
             >
               WEEK
             </div>
@@ -150,7 +138,7 @@ function App() {
               }}
             >
               {
-                row.map(item => <Tile onClick={() => setAvail(advanceDay(avail, name, item.month, item.day))} key={item.day} user={name} {...item}/>)
+                row.map(item => <Tile onClick={() => advance([item])} key={item.day} user={name} {...item}/>)
               }
             </div>
           </div>

+ 2 - 0
client/src/api.js

@@ -33,4 +33,6 @@ export const setAvailability = async (name, availability) => {
   if (!res.ok) {
     throw new Error(res.statusText);
   }
+  
+  return await res.json();
 }

+ 36 - 25
server/app.py

@@ -4,7 +4,7 @@ import pickle
 import atexit
 import datetime
 
-from flask import Flask, jsonify, request
+from flask import Flask, jsonify, request, abort
 from flask_cors import CORS
 
 DB_FILE = "vacation.db"
@@ -41,31 +41,42 @@ def health():
 @app.route("/availability", methods=["GET", "POST"])
 def status():
     global last_update
-
-    if request.method == "GET":
-        return jsonify({
-            "lastUpdated": last_update.isoformat(),
-            "availability": [
-                {
-                    "month": month,
-                    "day": day,
-                    "availability": [{
-                        "name": name,
-                        "status": status,
-                    } for (name, status) in avail.items()],
-                } for (month, day), avail in db.items()
-            ],
-        })
-
-    body = request.get_json()
-
-    print(body)
-
-    last_update = datetime.datetime.now()
-
-    # TODO actually implement updates
     
-    return "", 204
+    if request.method == "POST":
+        body = request.get_json()
+        if body is None:
+            abort(400)
+        name = body.get("name", None)
+        availability = body.get("availability", None)
+        if not isinstance(name, str) or not isinstance(availability, list):
+            abort(400)
+        
+        transaction = []
+        for a in availability:
+            month = a.get("month", None)
+            day = a.get("day", None)
+            status = a.get("status", None)
+            if (month, day) not in db or status not in ("yes", "no", "maybe", "unknown"):
+                abort(400)
+            transaction.append((month, day, status))
+        
+        last_update = datetime.datetime.now()
+        for (month, day, status) in transaction:
+            db[(month, day)][name] = status
+
+    return jsonify({
+        "lastUpdated": last_update.isoformat(),
+        "availability": [
+            {
+                "month": month,
+                "day": day,
+                "availability": [{
+                    "name": name,
+                    "status": status,
+                } for (name, status) in avail.items()],
+            } for (month, day), avail in db.items()
+        ],
+    })
 
 
 if __name__ == "__main__":