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

Add magazines to weekly debrief #66

Merged
merged 7 commits into from
Mar 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ module.exports = {
'@typescript-eslint/explicit-module-boundary-types': 0,
'@typescript-eslint/no-unused-vars': 0,
'semi-style': ['error', 'last'],
'import/named': 0,
'import/prefer-default-export': 0,
},
// This gets rid of weird react error for non-react project
settings: {
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ nohup.out
*src/certificates*
Makefile
start.sh
*secrets*

# ignore database dumps
dump/
5 changes: 5 additions & 0 deletions src/entities/User.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import WeeklyDebrief from './WeeklyDebrief';
import { PublicationSlug } from '../common/types';
import { Flyer } from './Flyer';
import { Organization } from './Organization';
import { Magazine } from './Magazine';

@ObjectType({ description: 'The User Model' })
export class User {
Expand Down Expand Up @@ -42,6 +43,10 @@ export class User {
@Property({ required: true, type: () => Article, default: [] })
readArticles: mongoose.Types.DocumentArray<DocumentType<Article>>;

@Field((type) => [Magazine])
@Property({ required: true, type: () => Magazine, default: [] })
readMagazines: mongoose.Types.DocumentArray<DocumentType<Magazine>>;

@Field((type) => [Flyer])
@Property({ required: true, type: () => Flyer, default: [] })
readFlyers: mongoose.Types.DocumentArray<DocumentType<Flyer>>;
Expand Down
5 changes: 5 additions & 0 deletions src/entities/WeeklyDebrief.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Field, ID, ObjectType } from 'type-graphql';
import { prop as Property, DocumentType } from '@typegoose/typegoose';
import mongoose from 'mongoose';
import { Article } from './Article';
import { Magazine } from './Magazine';

@ObjectType({ description: 'The Weekly Debrief Model' })
export default class WeeklyDebrief {
Expand Down Expand Up @@ -36,6 +37,10 @@ export default class WeeklyDebrief {
@Property({ required: true, type: () => Article, default: [] })
readArticles: mongoose.Types.DocumentArray<DocumentType<Article>>;

@Field((type) => [Magazine])
@Property({ required: true, type: () => Magazine, default: [] })
readMagazines: mongoose.Types.DocumentArray<DocumentType<Magazine>>;

@Field((type) => [Article])
@Property({ required: true, type: () => Article, default: [] })
randomArticles: mongoose.Types.DocumentArray<DocumentType<Article>>;
Expand Down
22 changes: 22 additions & 0 deletions src/repos/UserRepo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { Article } from '../entities/Article';
import ArticleRepo from './ArticleRepo';
import { PublicationSlug } from '../common/types';
import { User, UserModel } from '../entities/User';
import { Magazine } from '../entities/Magazine';
import MagazineRepo from './MagazineRepo';

/**
* Create new user associated with deviceToken and followedPublicationsSlugs of deviceType.
Expand Down Expand Up @@ -96,6 +98,25 @@ const appendReadArticle = async (uuid: string, articleID: string): Promise<User>
return user;
};

/**
* Add a magazine to a user's readMagazines
*/
const appendReadMagazine = async (uuid: string, magazineID: string): Promise<User> => {
const user = await UserModel.findOne({ uuid });

if (!user) return user;

const magazine = await MagazineRepo.getMagazineByID(magazineID);
const checkDuplicates = (prev: boolean, cur: Magazine) => prev || cur.id === magazineID;

if (magazine && !user.readMagazines.reduce(checkDuplicates, false)) {
user.readMagazines.push(magazine);
}

user.save();
return user;
};

/**
* Increment shoutouts in user's numShoutouts
*/
Expand Down Expand Up @@ -126,6 +147,7 @@ const incrementBookmarks = async (uuid: string): Promise<User> => {

export default {
appendReadArticle,
appendReadMagazine,
createUser,
followPublication,
getUserByUUID,
Expand Down
3 changes: 3 additions & 0 deletions src/repos/WeeklyDebriefRepo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,17 @@ const createWeeklyDebrief = async (
numShoutouts: user.numShoutouts,
numBookmarkedArticles: user.numBookmarkedArticles,
readArticles: user.readArticles.slice(0, 2),
readMagazines: user.readMagazines.slice(0, 2),
numReadArticles: user.readArticles.length,
numReadMagazines: user.readMagazines.length,
randomArticles: await articleAggregate.sample(2).exec(),
});
return UserModel.findOneAndUpdate(
{ uuid },
{
$set: {
readArticles: [],
readMagazines: [],
numShoutouts: 0,
numBookmarkedArticles: 0,
weeklyDebrief,
Expand Down
8 changes: 8 additions & 0 deletions src/resolvers/UserResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,14 @@ class UserResolver {
return await UserRepo.appendReadArticle(uuid, articleID);
}

@Mutation((_returns) => User, {
nullable: true,
description: "Adds the <Magazine> given by the <magazineID> to the <User's> read magazines",
})
async readMagazine(@Arg('uuid') uuid: string, @Arg('magazineID') magazineID: string) {
return await UserRepo.appendReadMagazine(uuid, magazineID);
}

@Mutation((_returns) => User, {
nullable: true,
description: 'Increments the number of bookmarks for the <User> given by <uuid>',
Expand Down
56 changes: 56 additions & 0 deletions src/tests/data/MagazineFactory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/* eslint-disable no-underscore-dangle */
import { faker } from '@faker-js/faker';
import { _ } from 'underscore';
import PublicationFactory from './PublicationFactory';
import { Magazine } from '../../entities/Magazine';
import FactoryUtils from './FactoryUtils';

class MagazineFactory {
public static async create(n: number): Promise<Magazine[]> {
/**
* Returns a list of n number of random Magazine objects
*/
return Promise.all(FactoryUtils.create(n, MagazineFactory.fake));
}

public static async createSpecific(
n: number,
newMappings: { [key: string]: any },
): Promise<Magazine[]> {
/**
* Returns a list of n number of random Magazine objects with specified
* field values in new Mappings
*/
const arr = await MagazineFactory.create(n);
return arr.map((x) => {
const newDoc = x;
Object.entries(newMappings).forEach(([k, v]) => {
newDoc[k] = v;
});
return newDoc;
});
}

public static async fake(): Promise<Magazine> {
/**
* Returns a Magazine with random values in its instance variables
*/
const fakeMagazine = new Magazine();
const examplePublication = await PublicationFactory.getRandomPublication();

fakeMagazine.date = faker.date.past();
fakeMagazine.semester = _.sample(['FA22', 'SP23']);
fakeMagazine.pdfURL = faker.image.cats();
fakeMagazine.publication = examplePublication;
fakeMagazine.publicationSlug = examplePublication.slug;
fakeMagazine.shoutouts = _.random(0, 50);
fakeMagazine.title = faker.commerce.productDescription();
fakeMagazine.nsfw = _.sample([true, false]);
fakeMagazine.isFeatured = _.sample([true, false]);
fakeMagazine.trendiness = 0;
fakeMagazine.isFiltered = _.sample([true, false]);

return fakeMagazine;
}
}
export default MagazineFactory;
55 changes: 55 additions & 0 deletions src/tests/data/UserFactory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/* eslint-disable no-underscore-dangle */
import { faker } from '@faker-js/faker';
import { _ } from 'underscore';
import { User } from '../../entities/User';
import FactoryUtils from './FactoryUtils';

class UserFactory {
public static async create(n: number): Promise<User[]> {
/**
* Returns a list of n number of random User objects
*
* @param n The number of desired random User objects
* @returns A Promise of the list of n number of random User objects
*/
return Promise.all(FactoryUtils.create(n, UserFactory.fake));
}

public static async createSpecific(
n: number,
newMappings: { [key: string]: any },
): Promise<User[]> {
/**
* Returns a list of n number of random User objects with specified
* fields values in newMappings
*
* @param n The number of desired random User objects
* @param newMappings The specified values for User parameters [key]
* @returns A Promise of the list of n number of random User objects
*/
const arr = await UserFactory.create(n);
return arr.map((x) => {
const newDoc = x;
Object.entries(newMappings).forEach(([k, v]) => {
newDoc[k] = v;
});
return newDoc;
});
}

public static async fake(): Promise<User> {
/**
* Returns a User with random values in its instance variables
*
* @returns The User object with random values in its instance variables
*/
const fakeUser = new User();

fakeUser.deviceToken = faker.datatype.string();
fakeUser.deviceType = _.sample(['IOS', 'ANDROID']);
fakeUser.uuid = faker.datatype.uuid();

return fakeUser;
}
}
export default UserFactory;
Loading