Browse Source

added the concept of a writer to a broadcast show

develop
Rob Colbert 3 months ago
parent
commit
a45d059764
  1. 37
      src/app/models/broadcast-show.ts
  2. 67
      src/app/models/post.ts
  3. 4
      src/app/services/broadcast-show.ts
  4. 168
      src/app/services/post.ts

37
src/app/models/broadcast-show.ts

@ -21,18 +21,37 @@ export enum BroadcastShowStatus {
export interface IBroadcastShowHost { export interface IBroadcastShowHost {
name: string; name: string;
description: string; bio: string;
gender: HumanGender; gender: HumanGender;
role: string; role: string;
speech: ISpeechPersonality; speech: ISpeechPersonality;
} }
export const BroadcastShowHostSchema = new Schema({ export const BroadcastShowHostSchema = new Schema(
{
name: { type: String, required: true }, name: { type: String, required: true },
description: { type: String, required: true }, bio: { type: String, required: true },
gender: { type: String, enum: HumanGender, required: true }, gender: { type: String, enum: HumanGender, required: true },
role: { type: String, required: true }, role: { type: String, required: true },
personality: { type: SpeechPersonalitySchema, required: true }, personality: { type: SpeechPersonalitySchema, required: true },
}); },
{ _id: false }
);
export interface IBroadcastShowWriter {
name: string;
bio: string;
gender: HumanGender;
role: string;
}
export const BroadcastShowWriterSchema = new Schema<IBroadcastShowWriter>(
{
name: { type: String, required: true },
bio: { type: String, required: true },
gender: { type: String, enum: HumanGender, required: true },
role: { type: String, required: true },
},
{ _id: false }
);
export interface IBroadcastShowProducer { export interface IBroadcastShowProducer {
name: string; name: string;
@ -40,12 +59,15 @@ export interface IBroadcastShowProducer {
gender: HumanGender; gender: HumanGender;
role: string; role: string;
} }
export const BroadcastShowProducerSchema = new Schema({ export const BroadcastShowProducerSchema = new Schema(
{
name: { type: String, required: true }, name: { type: String, required: true },
description: { type: String, required: true }, description: { type: String, required: true },
gender: { type: String, enum: HumanGender, required: true }, gender: { type: String, enum: HumanGender, required: true },
role: { type: String, required: true }, role: { type: String, required: true },
}); },
{ _id: false }
);
export interface IBroadcastShow { export interface IBroadcastShow {
_id: Types.ObjectId; // MongoDB concern _id: Types.ObjectId; // MongoDB concern
@ -55,10 +77,10 @@ export interface IBroadcastShow {
title: string; title: string;
description: string; description: string;
producers: Array<IBroadcastShowProducer>; producers: Array<IBroadcastShowProducer>;
writer: IBroadcastShowWriter;
hosts: Array<IBroadcastShowHost>; hosts: Array<IBroadcastShowHost>;
recentEpisodes: Array<IEpisode | Types.ObjectId>; recentEpisodes: Array<IEpisode | Types.ObjectId>;
} }
export const BroadcastShowSchema = new Schema<IBroadcastShow>({ export const BroadcastShowSchema = new Schema<IBroadcastShow>({
status: { status: {
type: String, type: String,
@ -73,6 +95,7 @@ export const BroadcastShowSchema = new Schema<IBroadcastShow>({
default: [], default: [],
required: true, required: true,
}, },
writer: { type: BroadcastShowWriterSchema, required: true },
hosts: { type: [BroadcastShowHostSchema], default: [], required: true }, hosts: { type: [BroadcastShowHostSchema], default: [], required: true },
recentEpisodes: { recentEpisodes: {
type: [Types.ObjectId], type: [Types.ObjectId],

67
src/app/models/post.ts

@ -0,0 +1,67 @@
// app/models/post.ts
// Copyright (C) 2025 DTP Technologies, LLC
// All Rights Reserved
import { Schema, Types, model } from "mongoose";
import {
BroadcastShowWriterSchema,
IBroadcastShow,
IBroadcastShowWriter,
} from "./broadcast-show.js";
import { IImage } from "./image.js";
export enum PostStatus {
Draft = "draft",
Live = "live",
Removed = "removed",
}
/**
* Virtual authors will compose written articles for the site in blog format.
*/
export interface IPost {
_id: Types.ObjectId; // MongoDB concern
__v: number; // MongoDB concern
created: Date;
status: PostStatus;
show: IBroadcastShow | Types.ObjectId;
slug: string;
author: IBroadcastShowWriter;
title: string;
summary: string;
body: string;
tags: Array<string>;
headerImage: IImage | Types.ObjectId;
bodyImages?: Array<IImage | Types.ObjectId>;
}
export const PostSchema = new Schema<IPost>({
created: { type: Date, default: Date.now, required: true, index: -1 },
status: {
type: String,
enum: PostStatus,
default: PostStatus.Draft,
required: true,
index: 1,
},
show: {
type: Schema.ObjectId,
required: true,
index: 1,
ref: "BroadcastShow",
},
slug: { type: String, lowercase: true, required: true, index: 1 },
author: { type: BroadcastShowWriterSchema, required: true },
title: { type: String, required: true },
summary: { type: String, required: true },
body: { type: String, required: true },
tags: { type: [String], default: [], required: true },
headerImage: { Type: Schema.ObjectId, required: true, ref: "Image" },
bodyImages: { Type: [Schema.ObjectId], ref: "Image" },
});
export const Post = model<IPost>("Post", PostSchema);
export default Post;

4
src/app/services/broadcast-show.ts

@ -138,7 +138,9 @@ export class BroadcastShowService extends DtpService {
exclude = [exclude]; exclude = [exclude];
} }
pipeline.push({ pipeline.push({
$match: { $nin: exclude.map((show) => show._id) }, $match: {
_id: { $nin: exclude.map((show) => show._id) },
},
}); });
} }

168
src/app/services/post.ts

@ -0,0 +1,168 @@
// app/services/broadcast-show.ts
// Copyright (C) 2025 DTP Technologies, LLC
// All Rights Reserved
import mongoose from "mongoose";
import slug from "slug";
import { IImage } from "../models/image.js";
import {
IBroadcastShow,
IBroadcastShowWriter,
} from "../models/broadcast-show.js";
import Post, { IPost, PostStatus } from "../models/post.js";
import {
DtpPlatform,
DtpService,
DtpServiceUpdate,
WebError,
WebPaginationParameters,
} from "../../lib/dtplib.js";
export interface PostDefinition {
show: IBroadcastShow | mongoose.Types.ObjectId;
author: IBroadcastShowWriter;
title: string;
summary: string;
body: string;
tags: Array<string>;
headerImage: IImage | mongoose.Types.ObjectId;
}
export interface PostLibrary {
posts: Array<IPost | mongoose.Types.ObjectId>;
totalPostCount: number;
}
/**
* A service for managing blog posts on the Newsroom platform.
*/
export class PostService extends DtpService {
populatePost: Array<mongoose.PopulateOptions>;
static get name() {
return "PostService";
}
static get slug() {
return "post";
}
constructor(platform: DtpPlatform) {
super(platform, PostService);
this.populatePost = [
{
path: "headerImage",
},
{
path: "bodyImages",
},
];
}
async create(
show: IBroadcastShow | mongoose.Types.ObjectId,
definition: PostDefinition
): Promise<IPost> {
const NOW = new Date();
const post = new Post();
let titleSlug = definition.title.split(" ").slice(0, 5).join(" ");
titleSlug = slug(titleSlug);
titleSlug += `-${post._id.toString()}`;
post.created = NOW;
post.show = show._id;
post.slug = titleSlug;
post.author = definition.author;
post.title = definition.title;
post.summary = definition.summary;
post.body = definition.body;
post.tags = definition.tags;
post.headerImage = definition.headerImage._id;
this.log.info("creating blog post", {
_id: post._id,
slug: titleSlug,
title: post.title,
});
await post.save();
return post.toObject();
}
async update(post: IPost, definition: PostDefinition): Promise<IPost> {
const update: DtpServiceUpdate = {};
update.$set = {};
update.$set.author = definition.author;
update.$set.title = definition.title;
update.$set.summary = definition.summary;
update.$set.body = definition.body;
update.$set.tags = definition.tags;
update.$set.headerImage = definition.headerImage._id;
const newPost = await Post.findByIdAndUpdate(post._id, update, {
new: true,
populate: this.populatePost,
}).lean();
if (!newPost) {
throw new WebError(404, "Post not found");
}
return newPost;
}
async getBySlug(urlSlug: string): Promise<IPost | null> {
return Post.findOne({ slug: urlSlug.trim().toLowerCase() })
.populate(this.populatePost)
.lean();
}
async getById(postId: mongoose.Types.ObjectId): Promise<IPost | null> {
return Post.findById(postId).populate(this.populatePost).lean();
}
async getLive(pagination: WebPaginationParameters): Promise<PostLibrary> {
const search = { status: PostStatus.Live };
const posts = await Post.find(search)
.sort({ created: -1 })
.skip(pagination.skip)
.limit(pagination.cpp)
.populate(this.populatePost)
.lean();
const totalPostCount = await Post.countDocuments(search);
return { posts, totalPostCount };
}
async getForShow(
show: IBroadcastShow | mongoose.Types.ObjectId,
pagination: WebPaginationParameters
): Promise<PostLibrary> {
const search = { show: show._id, status: PostStatus.Live };
const posts = await Post.find(search)
.sort({ created: -1 })
.skip(pagination.skip)
.limit(pagination.cpp)
.populate(this.populatePost)
.lean();
const totalPostCount = await Post.countDocuments(search);
return { posts, totalPostCount };
}
async getAll(pagination: WebPaginationParameters): Promise<PostLibrary> {
const posts = await Post.find()
.sort({ created: -1 })
.skip(pagination.skip)
.limit(pagination.cpp)
.populate(this.populatePost)
.lean();
const totalPostCount = await Post.estimatedDocumentCount();
return { posts, totalPostCount };
}
}
export default PostService;
Loading…
Cancel
Save