fix recommendations

This commit is contained in:
Hongbo Wu 2023-09-20 09:20:06 +08:00
parent 9cf5d2bc4e
commit 0c5ba53c26
8 changed files with 114 additions and 100 deletions

View file

@ -34,6 +34,10 @@ export const highlightRepository = entityManager
return this.save(unescapeHighlight(highlight))
},
createAndSaves(highlights: DeepPartial<Highlight>[]) {
return this.save(highlights.map(unescapeHighlight))
},
updateAndSave(
highlightId: string,
highlight: QueryDeepPartialEntity<Highlight>

View file

@ -46,20 +46,10 @@ export const createGroupResolver = authorized<
CreateGroupSuccess,
CreateGroupError,
MutationCreateGroupArgs
>(async (_, { input }, { claims: { uid }, log }) => {
log.info('Creating group', {
input,
labels: {
source: 'resolver',
resolver: 'createGroupResolver',
uid,
},
})
>(async (_, { input }, { uid, log }) => {
try {
const userData = await userRepository.findOne({
where: { id: uid },
relations: ['profile'],
const userData = await userRepository.findOneBy({
id: uid,
})
if (!userData) {
return {
@ -106,14 +96,7 @@ export const createGroupResolver = authorized<
},
}
} catch (error) {
log.error('Error creating group', {
error,
labels: {
source: 'resolver',
resolver: 'createGroupResolver',
uid,
},
})
log.error('Error creating group', error)
return {
errorCodes: [CreateGroupErrorCode.BadRequest],
@ -122,15 +105,7 @@ export const createGroupResolver = authorized<
})
export const groupsResolver = authorized<GroupsSuccess, GroupsError>(
async (_, __, { claims: { uid }, log }) => {
log.info('Getting groups', {
labels: {
source: 'resolver',
resolver: 'groupsResolver',
uid,
},
})
async (_, __, { uid, log }) => {
try {
const user = await userRepository.findOneBy({
id: uid,
@ -168,15 +143,6 @@ export const recommendResolver = authorized<
RecommendError,
MutationRecommendArgs
>(async (_, { input }, { uid, log, signToken }) => {
log.info('Recommend', {
input,
labels: {
source: 'resolver',
resolver: 'recommendResolver',
uid,
},
})
try {
const item = await findLibraryItemById(input.pageId, uid)
if (!item) {
@ -208,11 +174,11 @@ export const recommendResolver = authorized<
member.user.id,
item.id,
{
group,
note: input.note ?? null,
recommender: item.user,
group: { id: group.id },
note: input.note,
recommender: { id: uid },
createdAt: new Date(),
libraryItem: item,
libraryItem: { id: item.id },
},
auth,
recommendedHighlightIds

View file

@ -162,7 +162,7 @@ export function pageRouter() {
return res.status(400).send({ errorCode: 'BAD_DATA' })
}
const item = await findLibraryItemById(itemId, userId)
const item = await findLibraryItemById(itemId, claims.uid)
if (!item) {
return res.status(404).send({ errorCode: 'NOT_FOUND' })
}
@ -170,7 +170,7 @@ export function pageRouter() {
const recommendedItem = await addRecommendation(
item,
recommendation,
claims.uid,
userId,
highlightIds
)
if (!recommendedItem) {

View file

@ -20,6 +20,31 @@ export const getHighlightLocation = (patch: string): number | undefined => {
export const getHighlightUrl = (slug: string, highlightId: string): string =>
`${homePageURL()}/me/${slug}#${highlightId}`
export const createHighlights = async (
highlights: DeepPartial<Highlight>[],
libraryItemId: string,
userId: string,
pubsub = createPubSubClient()
) => {
const newHighlights = await authTrx(
async (tx) =>
tx.withRepository(highlightRepository).createAndSaves(highlights),
undefined,
userId
)
await pubsub.entityCreated<CreateHighlightEvent[]>(
EntityType.HIGHLIGHT,
newHighlights.map((highlight) => ({
...highlight,
pageId: libraryItemId,
})),
userId
)
return newHighlights
}
export const createHighlight = async (
highlight: DeepPartial<Highlight>,
libraryItemId: string,

View file

@ -311,6 +311,7 @@ export const findLibraryItemById = async (
.createQueryBuilder(LibraryItem, 'library_item')
.leftJoinAndSelect('library_item.labels', 'labels')
.leftJoinAndSelect('library_item.highlights', 'highlights')
.leftJoinAndSelect('highlights.user', 'user')
.where('library_item.id = :id', { id })
.getOne(),
undefined,

View file

@ -1,12 +1,11 @@
import { nanoid } from 'nanoid'
import { DeepPartial } from 'typeorm'
import { LibraryItem } from '../entity/library_item'
import { Recommendation } from '../entity/recommendation'
import { getRepository } from '../repository'
import { logger } from '../utils/logger'
import {
createLibraryItem,
findLibraryItemByUrl,
updateLibraryItem,
} from './library_item'
import { createHighlights } from './highlights'
import { createLibraryItem, findLibraryItemByUrl } from './library_item'
export const addRecommendation = async (
item: LibraryItem,
@ -15,65 +14,83 @@ export const addRecommendation = async (
highlightIds?: string[]
) => {
try {
const highlights = item.highlights?.filter((highlight) =>
highlightIds?.includes(highlight.id)
)
// check if the item is already recommended to the group
const existingItem = await findLibraryItemByUrl(item.originalUrl, userId)
if (existingItem) {
const existingHighlights = existingItem.highlights || []
let recommendedItem = await findLibraryItemByUrl(item.originalUrl, userId)
// if (existingItem) {
// const existingHighlights = existingItem.highlights || []
// remove duplicates
const newHighlights =
highlights?.filter(
(highlight) =>
!existingHighlights.find(
(existingHighlight) => existingHighlight.quote === highlight.quote
)
) || []
// // remove duplicates
// const newHighlights =
// highlights?.filter(
// (highlight) =>
// !existingHighlights.find(
// (existingHighlight) => existingHighlight.quote === highlight.quote
// )
// ) || []
const existingRecommendations = existingItem.recommendations || []
// return existingItem
// }
// update recommendations in the existing item
await updateLibraryItem(
existingItem.id,
{
recommendations: existingRecommendations.concat(recommendation),
highlights: existingHighlights.concat(newHighlights),
},
userId
)
if (!recommendedItem) {
// create a new item
const newItem: DeepPartial<LibraryItem> = {
user: { id: userId },
slug: item.slug,
title: item.title,
author: item.author,
description: item.description,
originalUrl: item.originalUrl,
originalContent: item.originalContent,
contentReader: item.contentReader,
directionality: item.directionality,
itemLanguage: item.itemLanguage,
itemType: item.itemType,
readableContent: item.readableContent,
siteIcon: item.siteIcon,
siteName: item.siteName,
thumbnail: item.thumbnail,
uploadFile: item.uploadFile,
wordCount: item.wordCount,
}
return existingItem
recommendedItem = await createLibraryItem(newItem, userId)
}
// create a new item
const newItem: DeepPartial<LibraryItem> = {
recommendations: [recommendation],
user: { id: userId },
highlights,
slug: item.slug,
title: item.title,
author: item.author,
description: item.description,
originalUrl: item.originalUrl,
originalContent: item.originalContent,
contentReader: item.contentReader,
directionality: item.directionality,
itemLanguage: item.itemLanguage,
itemType: item.itemType,
readableContent: item.readableContent,
siteIcon: item.siteIcon,
siteName: item.siteName,
thumbnail: item.thumbnail,
uploadFile: item.uploadFile,
wordCount: item.wordCount,
const highlights = item.highlights
?.filter((highlight) => highlightIds?.includes(highlight.id))
.map((highlight) => ({
shortId: nanoid(8),
createdAt: new Date(),
libraryItem: { id: recommendedItem?.id },
user: { id: userId },
quote: highlight.quote,
annotation: highlight.annotation,
prefix: highlight.prefix,
suffix: highlight.suffix,
patch: highlight.patch,
updatedAt: new Date(),
sharedAt: new Date(),
html: highlight.html,
color: highlight.color,
}))
if (highlights) {
await createHighlights(highlights, recommendedItem.id, userId)
}
return createLibraryItem(newItem, userId)
await createRecommendation({
...recommendation,
libraryItem: { id: recommendedItem.id },
})
return recommendedItem
} catch (err) {
logger.error('Error adding recommendation', err)
return null
}
}
export const createRecommendation = async (
recommendation: DeepPartial<Recommendation>
) => {
return getRepository(Recommendation).save(recommendation)
}

View file

@ -5,6 +5,7 @@ import { CloudTasksClient, protos } from '@google-cloud/tasks'
import { google } from '@google-cloud/tasks/build/protos/protos'
import axios from 'axios'
import { nanoid } from 'nanoid'
import { DeepPartial } from 'typeorm'
import { Recommendation } from '../entity/recommendation'
import { Subscription } from '../entity/subscription'
import { env } from '../env'
@ -443,7 +444,7 @@ export const enqueueTextToSpeech = async ({
export const enqueueRecommendation = async (
userId: string,
itemId: string,
recommendation: Partial<Recommendation>,
recommendation: DeepPartial<Recommendation>,
authToken: string,
highlightIds?: string[]
): Promise<string> => {

View file

@ -11,7 +11,7 @@ from elasticsearch.helpers import async_scan
PG_HOST = os.getenv('PG_HOST', 'localhost')
PG_PORT = os.getenv('PG_PORT', 5432)
PG_USER = os.getenv('PG_USER', 'app_user')
PG_USER = os.getenv('PG_USER', 'hongbo')
PG_PASSWORD = os.getenv('PG_PASSWORD', 'app_pass')
PG_DB = os.getenv('PG_DB', 'omnivore')
ES_URL = os.getenv('ES_URL', 'http://localhost:9200')