فهرست منبع

Create basic Planner routing, model and controller

Tatiana Inama 7 سال پیش
والد
کامیت
ed7e3b4dc5
6فایلهای تغییر یافته به همراه151 افزوده شده و 5 حذف شده
  1. 6 4
      ktchn/src/mongo.ts
  2. 73 0
      ktchn/src/planner/controller.ts
  3. 41 0
      ktchn/src/planner/model.ts
  4. 24 0
      ktchn/src/planner/routes.ts
  5. 4 1
      ktchn/src/promise-all-middleware.ts
  6. 3 0
      ktchn/src/server.ts

+ 6 - 4
ktchn/src/mongo.ts

@@ -1,4 +1,4 @@
-import { Db, Cursor, MongoClient, InsertOneWriteOpResult, Collection, ObjectID, FilterQuery, ObjectId, UpdateWriteOpResult, WriteOpResult } from 'mongodb';
+import { Db, Cursor, MongoClient, InsertOneWriteOpResult, Collection, ObjectID, FilterQuery, ObjectId, UpdateWriteOpResult, WriteOpResult, CollectionAggregationOptions, AggregationCursorResult } from 'mongodb';
 
 export interface IDDocument {
   _id: ObjectID
@@ -12,11 +12,12 @@ export interface IMongoService {
   findOne<T>(query: FilterQuery<T>): Promise<IDBDocument<T>|null>,
   findOneById<T>(idParam: string): Promise<IDBDocument<T>|null>,
   find<T>(query: FilterQuery<T>, optionalQuery?: FilterQuery<T>): Promise<IDBDocument<T>[]>,
-  update<T>(id: string, data: T): Promise<WriteOpResult>
+  update<T>(id: string, data: T): Promise<WriteOpResult>,
+  aggregate<T>(pipeline: Object[]): Promise<T[]>
 }
 
 export const mongoService = (db: Db) => (col: string): IMongoService => {
-  const collection:Collection = db.collection(col);
+  const collection: Collection = db.collection(col);
 
   return {
     insertOne: <T>(data: T): Promise<IDBDocument<T>> => collection.insertOne(data).then(insertOneResult => insertOneResult.ops[0]),
@@ -24,7 +25,8 @@ export const mongoService = (db: Db) => (col: string): IMongoService => {
     findOne: <T>(query: FilterQuery<T>) => collection.findOne(query),
     findOneById: (idParam: string) => collection.findOne({ _id: new ObjectId(idParam) }),
     find: <T>(query: FilterQuery<T>, optionalQuery: FilterQuery<T> = {} ): Promise<IDBDocument<T>[]> => collection.find(query, optionalQuery).toArray(),
-    update: <T>(id: string, data: T) => collection.update({_id: new ObjectId(id)}, data)
+    update: <T>(id: string, data: T) => collection.update({_id: new ObjectId(id)}, data),
+    aggregate: <T>(pipeline: Object[]) => collection.aggregate<T>(pipeline).toArray()
   }
 };
 

+ 73 - 0
ktchn/src/planner/controller.ts

@@ -0,0 +1,73 @@
+
+import { Request, Response, NextFunction } from 'express';
+import { IMongoService } from '../mongo';
+import { ChainPController } from '../promise-all-middleware';
+import PlanDB, { WeeklyPlanner, CompletePlanDB, Weekday } from './model';
+import moment, { Moment } from 'moment';
+import { ObjectId } from 'bson';
+import { RecipeDB } from '../recipes/model';
+
+type Controller<U, T> = (db: IMongoService) => ChainPController<U, T>;
+
+export const getWeekDay = (day: Moment): Weekday => day.format('dddd').toLowerCase() as Weekday;
+
+const mkWeekQuery = (week: string) => ({
+  week: parseInt(week) || moment().isoWeek()
+});
+
+const getWeekPlanner: Controller<void, PlanDB[]> =
+  db => req => () => db.find<PlanDB>(mkWeekQuery(req.params.week));
+
+// const getWeekPlannerComplete: Controller<WeeklyPlanner> =
+//   db => req => () => db.find<PlanDB>(mkWeekQuery(req.params.week)).then(
+//     plans => plans.reduce((planner, plan) => {
+//       const date = moment(plan.day);
+//       return {
+//         ...planner,
+//         [getWeekDay(date)]: {
+//           date,
+//           [plan.meal]: plan.recipe
+//         }
+//       }
+//     }, {
+//       week: mkWeekQuery(req.params.week).week
+//     } as WeeklyPlanner)
+//   )
+
+const formatPlanner: Controller<PlanDB[], WeeklyPlanner> = db => req => prevResult => {
+  const week = mkWeekQuery(req.params.week).week;
+  return db.aggregate<CompletePlanDB>([
+    {
+      '$match': {
+        'week': week
+      }
+    },
+    {
+      '$lookup': {
+        'from': 'recipes', 
+        'localField': 'recipe', 
+        'foreignField': '_id', 
+        'as': 'recipe'
+      }
+    }, {
+      '$unwind': {
+        'path': '$recipe'
+      }
+    }
+  ]).then(plans => plans.reduce((planner, plan) => {
+    const day = moment(plan.day);
+    return {
+      ...planner,
+      [getWeekDay(day)]: {
+        ...planner[getWeekDay(day)],
+        day,
+        [plan.meal]: plan.recipe
+      }
+    };
+  }, { week } as WeeklyPlanner))
+}
+
+export {
+  getWeekPlanner,
+  formatPlanner,
+}

+ 41 - 0
ktchn/src/planner/model.ts

@@ -0,0 +1,41 @@
+import { ObjectID } from 'mongodb';
+import { RecipeDB } from '../recipes/model';
+import { Moment, weekdays } from 'moment';
+
+export type Meal = 'lunch' | 'dinner';
+
+export type Weekday =
+  'monday' |
+  'tuesday' |
+  'wednsday' |
+  'thursday' |
+  'friday' |
+  'saturday' |
+  'sunday';
+
+export type WeekPlan = {
+  [day in Weekday]: DayPlan;
+};
+
+export default interface PlanDB {
+  _id: ObjectID,
+  day: Date,
+  week: number,
+  recipe: ObjectID,
+  meal: Meal
+}
+
+export interface CompletePlanDB extends Omit<PlanDB, 'recipe'> {
+  recipe: RecipeDB
+}
+
+export interface DayPlan {
+  date: Moment,
+  lunch?: RecipeDB,
+  dinner?: RecipeDB
+}
+
+export interface WeeklyPlanner extends WeekPlan {
+  week: number
+}
+

+ 24 - 0
ktchn/src/planner/routes.ts

@@ -0,0 +1,24 @@
+import { Router } from 'express';
+import mongoService, { IMongoService } from '../mongo';
+import MongoClient from 'mongodb';
+import { chainP } from '../promise-all-middleware';
+import { getWeekPlanner, formatPlanner } from './controller';
+
+class PlannerRoutes {
+  public router: Router;
+  private PlannerDB: IMongoService;
+
+  public constructor(db: MongoClient.Db) {
+    this.router = Router();
+    this.PlannerDB = mongoService(db)('planner');
+    this.init();
+  }
+
+  private init() {
+    this.router.get('/all/');
+    this.router.get('/week/:week', chainP([getWeekPlanner(this.PlannerDB)]))
+    this.router.get('/week/:week/organized', chainP([formatPlanner(this.PlannerDB)]))
+  }
+}
+
+export default PlannerRoutes;

+ 4 - 1
ktchn/src/promise-all-middleware.ts

@@ -9,6 +9,8 @@ export interface ChainedMiddleware {
   (req: Request, res: Response, next: NextFunction): (result: any) => Promise<any>
 }
 
+export type ChainPController<T, U> = (req: Request, res: Response, next: NextFunction) => (result: T) => Promise<U>;
+
 const piddleware = (middlewares: IMiddleware[]) => (req: Request, res: Response, next: NextFunction): void => {
   middlewares.reduce((middlewaresChain, currentMiddleware) => {
     return middlewaresChain.then(x => currentMiddleware(req, res, next))
@@ -17,11 +19,12 @@ const piddleware = (middlewares: IMiddleware[]) => (req: Request, res: Response,
   }).catch(error => next(error));
 }
 
+
 const chainP = (middlewares: ChainedMiddleware[]) => (req: Request, res: Response, next: NextFunction): void => {
   middlewares.reduce(async (middlewaresChain, currentMiddleware) => {
     const result = await middlewaresChain;
     return await currentMiddleware(req, res, next)(result);
-  }, Promise.resolve()).then(results => { // results would ever be necessary ?  
+  }, Promise.resolve()).then(results => {
     res.json(results)
   }).catch(error => next(error));
 }

+ 3 - 0
ktchn/src/server.ts

@@ -6,6 +6,7 @@ import express, { Application } from "express";
 import rootRouter from "./routes/root";
 import recipeRouter from "./recipes/routes";
 import ingredientsRouter from './ingredients/routes';
+import plannerRouter from './planner/routes';
 import dotenv from 'dotenv';
 import MongoClient from 'mongodb';
 import { Response } from 'express';
@@ -46,6 +47,7 @@ class App {
   private routes(db: MongoClient.Db): void {
     this.app.use("/recipes", new recipeRouter(db).router);
     this.app.use("/ingredients", new ingredientsRouter(db).router);
+    this.app.use('/planner', new plannerRouter(db).router);
     this.app.use("/", rootRouter);
   }
 
@@ -87,6 +89,7 @@ class App {
       })
     });
     db.createCollection('recipes');
+    db.createCollection('planner');
   }
 
   private launchApp() {