first
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
import { Expose, plainToClass } from "class-transformer";
|
||||
import { IsNotEmpty, IsOptional, IsString } from "class-validator";
|
||||
|
||||
import { ApiProperty } from "../../../common/decorator/swggerDocs";
|
||||
import { IsValidId } from "../../../common/decorator/validation.decorator";
|
||||
import { ILearning } from "../model/Abstractions/ILearning";
|
||||
import { LearningCategoryModel } from "../model/learningCategory.model";
|
||||
|
||||
export class CreateLearningDTO {
|
||||
@Expose()
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
@ApiProperty({ type: "string", description: "title of learning", example: "عنوان ۱" })
|
||||
title: string;
|
||||
|
||||
@Expose()
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
@ApiProperty({ type: "string", description: "description of learning", example: "توضیحات ویدیو" })
|
||||
description: string;
|
||||
|
||||
@Expose()
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
@ApiProperty({ type: "string", description: "video url of learning", example: "https://loremflickr.com/454/340" })
|
||||
videoUrl: string;
|
||||
|
||||
@Expose()
|
||||
@IsOptional()
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
@ApiProperty({ type: "string", description: "cover url of learning", example: "https://loremflickr.com/454/340" })
|
||||
cover?: string;
|
||||
|
||||
@Expose()
|
||||
@IsNotEmpty()
|
||||
@IsValidId(LearningCategoryModel)
|
||||
@ApiProperty({ type: "string", description: "id of learning category", example: "66ed83563858f185c68449ff" })
|
||||
category: string;
|
||||
|
||||
public static transformLearning(data: ILearning): CreateLearningDTO {
|
||||
const LearningDto = plainToClass(CreateLearningDTO, data, {
|
||||
excludeExtraneousValues: true,
|
||||
enableImplicitConversion: true,
|
||||
});
|
||||
|
||||
return LearningDto;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { Expose } from "class-transformer";
|
||||
import { IsNotEmpty, IsString } from "class-validator";
|
||||
|
||||
import { ApiProperty } from "../../../common/decorator/swggerDocs";
|
||||
|
||||
export class CreateLearningCategoryDTO {
|
||||
@Expose()
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
@ApiProperty({ type: "string", description: "title of learning", example: "عنوان ۱" })
|
||||
title: string;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { Expose } from "class-transformer";
|
||||
import { IsNotEmpty } from "class-validator";
|
||||
|
||||
import { ApiProperty } from "../../../common/decorator/swggerDocs";
|
||||
import { IsValidId } from "../../../common/decorator/validation.decorator";
|
||||
import { LearningModel } from "../model/learning.model";
|
||||
|
||||
export class CreateLearningProgressDTO {
|
||||
@Expose()
|
||||
@IsNotEmpty()
|
||||
@IsValidId(LearningModel)
|
||||
@ApiProperty({ type: "string", description: "id of learning video", example: "66ed83563858f185c68449ff" })
|
||||
videoId: string;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { Expose } from "class-transformer";
|
||||
import { IsOptional, IsString } from "class-validator";
|
||||
|
||||
import { ApiProperty } from "../../../common/decorator/swggerDocs";
|
||||
|
||||
export class UpdateLearningDTO {
|
||||
@Expose()
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@ApiProperty({ type: "string", description: "title of learning", example: "عنوان ۱" })
|
||||
title: string;
|
||||
|
||||
@Expose()
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@ApiProperty({ type: "string", description: "description of learning", example: "توضیحات ویدیو" })
|
||||
description: string;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { Expose } from "class-transformer";
|
||||
import { IsOptional, IsString } from "class-validator";
|
||||
|
||||
import { ApiProperty } from "../../../common/decorator/swggerDocs";
|
||||
|
||||
export class UpdateLearningCategoryDTO {
|
||||
@Expose()
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@ApiProperty({ type: "string", description: "title of learning category", example: "عنوان ۱" })
|
||||
title: string;
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
import { Request } from "express";
|
||||
import { inject } from "inversify";
|
||||
import { controller, httpGet, httpPost, request, requestBody, requestParam } from "inversify-express-utils";
|
||||
|
||||
import { LearningService } from "./learning.service";
|
||||
import { HttpStatus } from "../../common";
|
||||
import { CreateLearningProgressDTO } from "./DTO/createLearningProgress.dto";
|
||||
import { BaseController } from "../../common/base/controller";
|
||||
import { ApiAuth, ApiModel, ApiOperation, ApiParam, ApiResponse, ApiTags } from "../../common/decorator/swggerDocs";
|
||||
import { Guard } from "../../core/middlewares/guard.middleware";
|
||||
import { ValidationMiddleware } from "../../core/middlewares/validator.middleware";
|
||||
import { IOCTYPES } from "../../IOC/ioc.types";
|
||||
import { ISeller } from "../seller/models/Abstraction/ISeller";
|
||||
|
||||
@controller("/learning")
|
||||
@ApiTags("Learning")
|
||||
export class LearningController extends BaseController {
|
||||
@inject(IOCTYPES.LearningService) learningService: LearningService;
|
||||
|
||||
@ApiOperation("get a list of learning")
|
||||
@ApiResponse("successful", HttpStatus.Ok)
|
||||
@httpGet("")
|
||||
public async getAllLearning() {
|
||||
const data = await this.learningService.getAllLearning();
|
||||
return this.response(data, HttpStatus.Ok);
|
||||
}
|
||||
|
||||
@ApiOperation("get a list of learning")
|
||||
@ApiResponse("successful", HttpStatus.Ok)
|
||||
@ApiAuth()
|
||||
@httpGet("/seller", Guard.authSeller())
|
||||
public async getAllLearningByCategories(@request() req: Request) {
|
||||
const seller = req.user as ISeller;
|
||||
const data = await this.learningService.getAllLearningByCategory(seller._id.toString());
|
||||
return this.response(data, HttpStatus.Ok);
|
||||
}
|
||||
|
||||
@ApiOperation("get a list of learning category")
|
||||
@ApiResponse("successful", HttpStatus.Ok)
|
||||
@httpGet("/category")
|
||||
public async getAllLearningCategory() {
|
||||
const data = await this.learningService.getAllLearningCategory();
|
||||
return this.response(data, HttpStatus.Ok);
|
||||
}
|
||||
|
||||
@ApiOperation("get a learning")
|
||||
@ApiResponse("successful", HttpStatus.Ok)
|
||||
@ApiParam("id", "the learning id", true)
|
||||
@httpGet("/:id")
|
||||
public async getLearning(@requestParam("id") learningId: string) {
|
||||
const data = await this.learningService.getById(learningId);
|
||||
return this.response(data, HttpStatus.Ok);
|
||||
}
|
||||
|
||||
@ApiOperation("learning progress for seller ===> need to login as seller")
|
||||
@ApiResponse("successful", HttpStatus.Ok)
|
||||
@ApiModel(CreateLearningProgressDTO)
|
||||
@ApiAuth()
|
||||
@httpPost("/progress", Guard.authSeller(), ValidationMiddleware.validateInput(CreateLearningProgressDTO))
|
||||
public async sellerPanel(@request() req: Request, @requestBody() createLearningProgressDto: CreateLearningProgressDTO) {
|
||||
const seller = req.user as ISeller;
|
||||
const data = await this.learningService.createLearningProgress(seller._id.toString(), createLearningProgressDto.videoId);
|
||||
return this.response(data);
|
||||
}
|
||||
|
||||
@ApiOperation("get a list of learning video watched")
|
||||
@ApiResponse("successful", HttpStatus.Ok)
|
||||
@ApiParam("id", "the category id", true)
|
||||
@ApiAuth()
|
||||
@httpGet("/:id/seller", Guard.authSeller())
|
||||
public async getVideoWatchedStatus(@request() req: Request, @requestParam("id") catId: string) {
|
||||
const seller = req.user as ISeller;
|
||||
const data = await this.learningService.getLearningProgressWatched(seller._id.toString(), catId);
|
||||
return this.response(data, HttpStatus.Ok);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
import { inject, injectable } from "inversify";
|
||||
import { isValidObjectId } from "mongoose";
|
||||
|
||||
import { CreateLearningDTO } from "./DTO/createLearning.dto";
|
||||
import { CreateLearningCategoryDTO } from "./DTO/createLearningCategory.dto";
|
||||
import { UpdateLearningDTO } from "./DTO/updateLearning.dto";
|
||||
import { UpdateLearningCategoryDTO } from "./DTO/updateLearningCategory.dto";
|
||||
import { LearningRepo } from "./repository/learning";
|
||||
import { LearningCategoryRepo } from "./repository/learningCategory";
|
||||
import { LearningProgressRepo } from "./repository/learningProgress";
|
||||
import { CommonMessage, SellerMessage } from "../../common/enums/message.enum";
|
||||
import { BadRequestError } from "../../core/app/app.errors";
|
||||
import { IOCTYPES } from "../../IOC/ioc.types";
|
||||
|
||||
@injectable()
|
||||
export class LearningService {
|
||||
@inject(IOCTYPES.LearningRepo) learningRepo: LearningRepo;
|
||||
@inject(IOCTYPES.LearningProgressRepo) learningProgressRepo: LearningProgressRepo;
|
||||
@inject(IOCTYPES.LearningCategoryRepo) learningCategoryRepo: LearningCategoryRepo;
|
||||
|
||||
async createLearning(createDto: CreateLearningDTO) {
|
||||
const category = await this.learningCategoryRepo.model.findOne({ _id: createDto.category, deleted: false });
|
||||
if (!category) {
|
||||
throw new BadRequestError(CommonMessage.NotFoundById);
|
||||
}
|
||||
const learning = await this.learningRepo.model.create({
|
||||
...createDto,
|
||||
category,
|
||||
});
|
||||
|
||||
return {
|
||||
message: CommonMessage.Created,
|
||||
learning,
|
||||
};
|
||||
}
|
||||
|
||||
async createLearningCategory(createDto: CreateLearningCategoryDTO) {
|
||||
const learningCategory = await this.learningCategoryRepo.model.create({
|
||||
...createDto,
|
||||
});
|
||||
|
||||
return {
|
||||
message: CommonMessage.Created,
|
||||
learningCategory,
|
||||
};
|
||||
}
|
||||
|
||||
async getAllLearning() {
|
||||
const learning = await this.learningRepo.model.find({ deleted: false }).populate("category");
|
||||
return { learning };
|
||||
}
|
||||
|
||||
async getAllLearningByCategory(sellerId: string) {
|
||||
const learning = await this.learningRepo.getVideosGroupByCategories(sellerId);
|
||||
return { learning };
|
||||
}
|
||||
|
||||
async getAllLearningCategory() {
|
||||
const learningCategory = await this.learningCategoryRepo.model.find({
|
||||
deleted: false,
|
||||
});
|
||||
return { learningCategory };
|
||||
}
|
||||
|
||||
async getById(learningId: string) {
|
||||
if (!isValidObjectId(learningId)) throw new BadRequestError(CommonMessage.NotValidId);
|
||||
const learning = await this.learningRepo.model.findById(learningId).populate("category");
|
||||
if (!learning) throw new BadRequestError(CommonMessage.NotFoundById);
|
||||
return {
|
||||
learning,
|
||||
};
|
||||
}
|
||||
|
||||
async update(learningId: string, updateDto: UpdateLearningDTO) {
|
||||
console.log(learningId);
|
||||
if (!isValidObjectId(learningId)) throw new BadRequestError(CommonMessage.NotValidId);
|
||||
const learning = await this.learningRepo.model.findByIdAndUpdate(learningId, updateDto, { new: true });
|
||||
console.log(learning);
|
||||
if (!learning) throw new BadRequestError(CommonMessage.NotFoundById);
|
||||
return {
|
||||
message: CommonMessage.Updated,
|
||||
learning,
|
||||
};
|
||||
}
|
||||
|
||||
async updateCategory(catId: string, updateDto: UpdateLearningCategoryDTO) {
|
||||
if (!isValidObjectId(catId)) throw new BadRequestError(CommonMessage.NotValidId);
|
||||
const learningCategory = await this.learningCategoryRepo.model.findByIdAndUpdate(catId, updateDto, { new: true });
|
||||
console.log(learningCategory);
|
||||
if (!learningCategory) throw new BadRequestError(CommonMessage.NotFoundById);
|
||||
return {
|
||||
message: CommonMessage.Updated,
|
||||
learningCategory,
|
||||
};
|
||||
}
|
||||
|
||||
async delete(learningId: string) {
|
||||
if (!isValidObjectId(learningId)) throw new BadRequestError(CommonMessage.NotValidId);
|
||||
await this.learningRepo.model.findByIdAndUpdate(learningId, { deleted: true });
|
||||
return {
|
||||
message: CommonMessage.Deleted,
|
||||
};
|
||||
}
|
||||
|
||||
async createLearningProgress(sellerId: string, videoId: string) {
|
||||
const learningVideo = await this.learningRepo.findById(videoId);
|
||||
if (!learningVideo) {
|
||||
throw new BadRequestError(CommonMessage.NotFoundById);
|
||||
}
|
||||
|
||||
const learningProgress = await this.learningProgressRepo.model.findOne({
|
||||
sellerId,
|
||||
videoId,
|
||||
});
|
||||
|
||||
if (learningProgress) throw new BadRequestError(SellerMessage.VideoWasWached);
|
||||
|
||||
const newLearningProgress = await this.learningProgressRepo.model.create({
|
||||
sellerId,
|
||||
videoId: learningVideo._id,
|
||||
watchedAt: new Date(),
|
||||
});
|
||||
return {
|
||||
message: CommonMessage.Created,
|
||||
newLearningProgress,
|
||||
};
|
||||
}
|
||||
|
||||
async getLearningProgressBySellerForAdmin() {
|
||||
const progress = await this.learningProgressRepo.getLearningProgressBySellers();
|
||||
console.log(progress);
|
||||
return { progress };
|
||||
}
|
||||
|
||||
async getLearningProgressWatched(sellerId: string, catId: string) {
|
||||
const progress = await this.learningRepo.getVideosWithWatchStatus(sellerId, catId);
|
||||
console.log(progress);
|
||||
return { progress };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import { Schema } from "mongoose";
|
||||
|
||||
export interface ILearning {
|
||||
title: string;
|
||||
description: string;
|
||||
videoUrl: string;
|
||||
cover: string;
|
||||
category: Schema.Types.ObjectId;
|
||||
deleted: boolean;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface ILearningCategory {
|
||||
title: string;
|
||||
deleted: boolean;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import { Schema } from "mongoose";
|
||||
|
||||
export interface ILearningProgress {
|
||||
sellerId: Schema.Types.ObjectId;
|
||||
videoId: Schema.Types.ObjectId;
|
||||
watchedAt?: Date;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { Schema, model } from "mongoose";
|
||||
|
||||
import { ILearning } from "./Abstractions/ILearning";
|
||||
|
||||
const LearningSchema = new Schema<ILearning>(
|
||||
{
|
||||
title: { type: String, required: true },
|
||||
description: { type: String, required: true },
|
||||
videoUrl: { type: String, required: true },
|
||||
cover: { type: String, required: true },
|
||||
category: { type: Schema.Types.ObjectId, ref: "LearningCategory", required: true },
|
||||
deleted: { type: Boolean, default: false },
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
toJSON: { versionKey: false, virtuals: true },
|
||||
id: false,
|
||||
},
|
||||
);
|
||||
|
||||
const LearningModel = model<ILearning>("Learning", LearningSchema);
|
||||
|
||||
export { LearningModel };
|
||||
@@ -0,0 +1,19 @@
|
||||
import { Schema, model } from "mongoose";
|
||||
|
||||
import { ILearningCategory } from "./Abstractions/ILearningCategory";
|
||||
|
||||
const LearningCategorySchema = new Schema<ILearningCategory>(
|
||||
{
|
||||
title: { type: String, required: true, unique: true },
|
||||
deleted: { type: Boolean, default: false },
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
toJSON: { versionKey: false, virtuals: true },
|
||||
id: false,
|
||||
},
|
||||
);
|
||||
|
||||
const LearningCategoryModel = model<ILearningCategory>("LearningCategory", LearningCategorySchema);
|
||||
|
||||
export { LearningCategoryModel };
|
||||
@@ -0,0 +1,23 @@
|
||||
import { Schema, model } from "mongoose";
|
||||
|
||||
import { ILearningProgress } from "./Abstractions/ILearningProgress";
|
||||
|
||||
const LearningProgressSchema = new Schema<ILearningProgress>(
|
||||
{
|
||||
sellerId: { type: Schema.Types.ObjectId, ref: "Seller", required: true },
|
||||
videoId: { type: Schema.Types.ObjectId, ref: "Learning", required: true },
|
||||
watchedAt: { type: Date },
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
toJSON: { versionKey: false, virtuals: true },
|
||||
id: false,
|
||||
},
|
||||
);
|
||||
|
||||
// Defining a unique compound index for sellerId and videoId
|
||||
LearningProgressSchema.index({ sellerId: 1, videoId: 1 }, { unique: true });
|
||||
|
||||
const LearningProgressModel = model<ILearningProgress>("LearningProgress", LearningProgressSchema);
|
||||
|
||||
export { LearningProgressModel };
|
||||
@@ -0,0 +1,127 @@
|
||||
import { Types } from "mongoose";
|
||||
|
||||
import { BaseRepository } from "../../../common/base/repository";
|
||||
import { ILearning } from "../model/Abstractions/ILearning";
|
||||
import { LearningModel } from "../model/learning.model";
|
||||
|
||||
export class LearningRepo extends BaseRepository<ILearning> {
|
||||
constructor() {
|
||||
super(LearningModel);
|
||||
}
|
||||
|
||||
async getVideosWithWatchStatus(sellerId: string, catId: string) {
|
||||
const result = await LearningModel.aggregate([
|
||||
{
|
||||
$match: {
|
||||
category: catId,
|
||||
deleted: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
$lookup: {
|
||||
from: "learningprogresses",
|
||||
let: { seller: new Types.ObjectId(sellerId), videoId: "$_id" },
|
||||
pipeline: [
|
||||
{
|
||||
$match: {
|
||||
$expr: {
|
||||
$and: [{ $eq: ["$sellerId", "$$seller"] }, { $eq: ["$videoId", "$$videoId"] }],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
as: "watchStatus",
|
||||
},
|
||||
},
|
||||
{
|
||||
$addFields: {
|
||||
isWatched: { $gt: [{ $size: { $ifNull: ["$watchStatus", []] } }, 0] },
|
||||
},
|
||||
},
|
||||
{
|
||||
$project: {
|
||||
_id: 1,
|
||||
title: 1,
|
||||
description: 1,
|
||||
videoUrl: 1,
|
||||
cover: 1,
|
||||
isWatched: 1,
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
async getVideosGroupByCategories(sellerId: string) {
|
||||
const result = await LearningModel.aggregate([
|
||||
{
|
||||
$match: {
|
||||
deleted: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
$lookup: {
|
||||
from: "learningprogresses",
|
||||
let: { seller: new Types.ObjectId(sellerId), videoId: "$_id" },
|
||||
pipeline: [
|
||||
{
|
||||
$match: {
|
||||
$expr: {
|
||||
$and: [{ $eq: ["$sellerId", "$$seller"] }, { $eq: ["$videoId", "$$videoId"] }],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
as: "watchStatus",
|
||||
},
|
||||
},
|
||||
{
|
||||
$addFields: {
|
||||
isWatched: { $gt: [{ $size: { $ifNull: ["$watchStatus", []] } }, 0] },
|
||||
},
|
||||
},
|
||||
{
|
||||
$lookup: {
|
||||
from: "learningcategories",
|
||||
localField: "category",
|
||||
foreignField: "_id",
|
||||
as: "categoryDetails",
|
||||
},
|
||||
},
|
||||
{
|
||||
$unwind: "$categoryDetails",
|
||||
},
|
||||
{
|
||||
$group: {
|
||||
_id: "$category",
|
||||
categoryTitle: { $first: "$categoryDetails.title" },
|
||||
videos: {
|
||||
$push: {
|
||||
_id: "$_id",
|
||||
title: "$title",
|
||||
cover: "$cover",
|
||||
description: "$description",
|
||||
videoUrl: "$videoUrl",
|
||||
isWatched: "$isWatched",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
$project: {
|
||||
_id: 0,
|
||||
category: "$_id",
|
||||
categoryTitle: 1,
|
||||
videos: 1,
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
export function CreateLearningRepo(): LearningRepo {
|
||||
return new LearningRepo();
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { BaseRepository } from "../../../common/base/repository";
|
||||
import { ILearningCategory } from "../model/Abstractions/ILearningCategory";
|
||||
import { LearningCategoryModel } from "../model/learningCategory.model";
|
||||
|
||||
export class LearningCategoryRepo extends BaseRepository<ILearningCategory> {
|
||||
constructor() {
|
||||
super(LearningCategoryModel);
|
||||
}
|
||||
}
|
||||
|
||||
export function CreateLearningCategoryRepo(): LearningCategoryRepo {
|
||||
return new LearningCategoryRepo();
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
import { BaseRepository } from "../../../common/base/repository";
|
||||
import { ILearningProgress } from "../model/Abstractions/ILearningProgress";
|
||||
import { LearningProgressModel } from "../model/learningProgress.model";
|
||||
|
||||
export class LearningProgressRepo extends BaseRepository<ILearningProgress> {
|
||||
constructor() {
|
||||
super(LearningProgressModel);
|
||||
}
|
||||
|
||||
async getLearningProgressBySellers() {
|
||||
const result = await LearningProgressModel.aggregate([
|
||||
{
|
||||
$lookup: {
|
||||
from: "learnings",
|
||||
localField: "videoId",
|
||||
foreignField: "_id",
|
||||
as: "videoDetails",
|
||||
},
|
||||
},
|
||||
{
|
||||
$lookup: {
|
||||
from: "sellers",
|
||||
localField: "sellerId",
|
||||
foreignField: "_id",
|
||||
as: "sellerDetails",
|
||||
},
|
||||
},
|
||||
{
|
||||
$lookup: {
|
||||
from: "shops",
|
||||
localField: "sellerDetails._id",
|
||||
foreignField: "owner",
|
||||
as: "shopDetails",
|
||||
},
|
||||
},
|
||||
{
|
||||
$unwind: "$shopDetails",
|
||||
},
|
||||
{
|
||||
$addFields: {
|
||||
"sellerDetails.shopName": "$shopDetails.shopName",
|
||||
"sellerDetails.shopId": "$shopDetails._id",
|
||||
},
|
||||
},
|
||||
{
|
||||
$project: {
|
||||
_id: 0,
|
||||
sellerId: 1,
|
||||
videoId: 1,
|
||||
watchedAt: 1,
|
||||
videoDetails: { $arrayElemAt: ["$videoDetails", 0] },
|
||||
sellerDetails: { $arrayElemAt: ["$sellerDetails", 0] },
|
||||
},
|
||||
},
|
||||
{
|
||||
$group: {
|
||||
_id: "$sellerId",
|
||||
sellerDetails: { $first: "$sellerDetails" },
|
||||
watchedVideos: {
|
||||
$push: {
|
||||
videoId: "$videoId",
|
||||
videoDetails: "$videoDetails",
|
||||
watchedAt: "$watchedAt",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
$lookup: {
|
||||
from: "learnings",
|
||||
pipeline: [],
|
||||
as: "allVideos",
|
||||
},
|
||||
},
|
||||
{
|
||||
$project: {
|
||||
sellerDetails: {
|
||||
_id: 1,
|
||||
fullName: 1,
|
||||
shopName: 1,
|
||||
shopId: 1,
|
||||
},
|
||||
watchedVideos: 1,
|
||||
unwatchedVideos: {
|
||||
$filter: {
|
||||
input: "$allVideos",
|
||||
as: "video",
|
||||
cond: {
|
||||
$not: {
|
||||
$in: ["$$video._id", "$watchedVideos.videoId"],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
console.log(result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
export function CreateLearningProgressRepo(): LearningProgressRepo {
|
||||
return new LearningProgressRepo();
|
||||
}
|
||||
Reference in New Issue
Block a user