tercul-frontend/server/jsonStorage.ts
mukimovd d67963de51 Add capabilities to store and retrieve user content and analysis data
Implements data access methods for analysis results and annotations in JsonStorage, including get, filter, and create operations.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: cbacfb18-842a-4116-a907-18c0105ad8ec
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/39b5c689-6e8a-4d5a-9792-69cc81a56534/80f57837-9ae3-42b7-8c9f-0582d861d96f.jpg
2025-05-08 00:21:06 +00:00

922 lines
29 KiB
TypeScript

import fs from 'fs';
import path from 'path';
import { IStorage } from './storage';
import {
User, Author, Work, Translation, Tag, Bookmark, Comment,
Like, Collection, CollectionItem, TimelineEvent, BlogPost,
ReadingProgress, InsertUser, InsertAuthor, InsertWork,
InsertTranslation, InsertTag, InsertBookmark, InsertComment,
InsertLike, InsertCollection, InsertCollectionItem, InsertTimelineEvent,
InsertBlogPost, InsertReadingProgress, AnalysisResult, InsertAnalysisResult,
Annotation, InsertAnnotation
} from '../shared/schema';
export class JsonStorage implements IStorage {
private data: {
users: Map<number, User>;
authors: Map<number, Author>;
works: Map<number, Work>;
translations: Map<number, Translation>;
tags: Map<number, Tag>;
workTagsRelations: Map<number, Set<number>>;
blogPostTagsRelations: Map<number, Set<number>>;
bookmarks: Map<number, Bookmark>;
comments: Map<number, Comment>;
likes: Map<number, Like>;
collections: Map<number, Collection>;
collectionItems: Map<number, CollectionItem>;
timelineEvents: Map<number, TimelineEvent>;
blogPosts: Map<number, BlogPost>;
readingProgresses: Map<string, ReadingProgress>;
analysisResults: Map<number, AnalysisResult>;
annotations: Map<number, Annotation>;
};
private counters: {
userId: number;
authorId: number;
workId: number;
translationId: number;
tagId: number;
bookmarkId: number;
commentId: number;
likeId: number;
collectionId: number;
collectionItemId: number;
timelineEventId: number;
blogPostId: number;
readingProgressId: number;
analysisResultId: number;
annotationId: number;
};
private dataFilePath: string;
constructor(dataFilePath: string = path.join(process.cwd(), 'data', 'sampleData.json')) {
this.dataFilePath = dataFilePath;
this.data = {
users: new Map<number, User>(),
authors: new Map<number, Author>(),
works: new Map<number, Work>(),
translations: new Map<number, Translation>(),
tags: new Map<number, Tag>(),
workTagsRelations: new Map<number, Set<number>>(),
blogPostTagsRelations: new Map<number, Set<number>>(),
bookmarks: new Map<number, Bookmark>(),
comments: new Map<number, Comment>(),
likes: new Map<number, Like>(),
collections: new Map<number, Collection>(),
collectionItems: new Map<number, CollectionItem>(),
timelineEvents: new Map<number, TimelineEvent>(),
blogPosts: new Map<number, BlogPost>(),
readingProgresses: new Map<string, ReadingProgress>(),
analysisResults: new Map<number, AnalysisResult>(),
annotations: new Map<number, Annotation>(),
};
this.counters = {
userId: 1,
authorId: 1,
workId: 1,
translationId: 1,
tagId: 1,
bookmarkId: 1,
commentId: 1,
likeId: 1,
collectionId: 1,
collectionItemId: 1,
timelineEventId: 1,
blogPostId: 1,
readingProgressId: 1,
analysisResultId: 1,
annotationId: 1,
};
this.loadDataFromFile();
}
private loadDataFromFile() {
try {
console.log(`Loading data from ${this.dataFilePath}`);
if (!fs.existsSync(this.dataFilePath)) {
console.error(`Data file not found: ${this.dataFilePath}`);
return;
}
const jsonData = JSON.parse(fs.readFileSync(this.dataFilePath, 'utf8'));
// Load users
if (jsonData.users) {
jsonData.users.forEach((user: User) => {
this.data.users.set(user.id, {
...user,
createdAt: new Date(user.createdAt)
});
this.counters.userId = Math.max(this.counters.userId, user.id + 1);
});
}
// Load authors
if (jsonData.authors) {
jsonData.authors.forEach((author: Author) => {
this.data.authors.set(author.id, {
...author,
createdAt: new Date(author.createdAt)
});
this.counters.authorId = Math.max(this.counters.authorId, author.id + 1);
});
}
// Load works
if (jsonData.works) {
jsonData.works.forEach((work: Work) => {
this.data.works.set(work.id, {
...work,
createdAt: new Date(work.createdAt)
});
this.counters.workId = Math.max(this.counters.workId, work.id + 1);
});
}
// Load translations
if (jsonData.translations) {
jsonData.translations.forEach((translation: Translation) => {
this.data.translations.set(translation.id, {
...translation,
createdAt: new Date(translation.createdAt)
});
this.counters.translationId = Math.max(this.counters.translationId, translation.id + 1);
});
}
// Load tags
if (jsonData.tags) {
jsonData.tags.forEach((tag: Tag) => {
this.data.tags.set(tag.id, {
...tag,
createdAt: new Date(tag.createdAt)
});
this.counters.tagId = Math.max(this.counters.tagId, tag.id + 1);
});
}
// Load work-tag relations
if (jsonData.workTagsRelations) {
for (const [workId, tagIds] of Object.entries(jsonData.workTagsRelations)) {
this.data.workTagsRelations.set(
parseInt(workId),
new Set((tagIds as number[]).map(id => id))
);
}
}
// Load blog post-tag relations
if (jsonData.blogPostTagsRelations) {
for (const [postId, tagIds] of Object.entries(jsonData.blogPostTagsRelations)) {
this.data.blogPostTagsRelations.set(
parseInt(postId),
new Set((tagIds as number[]).map(id => id))
);
}
}
// Load bookmarks
if (jsonData.bookmarks) {
jsonData.bookmarks.forEach((bookmark: Bookmark) => {
this.data.bookmarks.set(bookmark.id, {
...bookmark,
createdAt: new Date(bookmark.createdAt)
});
this.counters.bookmarkId = Math.max(this.counters.bookmarkId, bookmark.id + 1);
});
}
// Load comments
if (jsonData.comments) {
jsonData.comments.forEach((comment: Comment) => {
this.data.comments.set(comment.id, {
...comment,
createdAt: new Date(comment.createdAt)
});
this.counters.commentId = Math.max(this.counters.commentId, comment.id + 1);
});
}
// Load likes
if (jsonData.likes) {
jsonData.likes.forEach((like: Like) => {
this.data.likes.set(like.id, {
...like,
createdAt: new Date(like.createdAt)
});
this.counters.likeId = Math.max(this.counters.likeId, like.id + 1);
});
}
// Load collections
if (jsonData.collections) {
jsonData.collections.forEach((collection: Collection) => {
this.data.collections.set(collection.id, {
...collection,
createdAt: new Date(collection.createdAt)
});
this.counters.collectionId = Math.max(this.counters.collectionId, collection.id + 1);
});
}
// Load collection items
if (jsonData.collectionItems) {
jsonData.collectionItems.forEach((item: CollectionItem) => {
this.data.collectionItems.set(item.id, {
...item,
createdAt: new Date(item.createdAt)
});
this.counters.collectionItemId = Math.max(this.counters.collectionItemId, item.id + 1);
});
}
// Load timeline events
if (jsonData.timelineEvents) {
jsonData.timelineEvents.forEach((event: TimelineEvent) => {
this.data.timelineEvents.set(event.id, {
...event,
createdAt: new Date(event.createdAt)
});
this.counters.timelineEventId = Math.max(this.counters.timelineEventId, event.id + 1);
});
}
// Load blog posts
if (jsonData.blogPosts) {
jsonData.blogPosts.forEach((post: BlogPost) => {
this.data.blogPosts.set(post.id, {
...post,
createdAt: new Date(post.createdAt),
publishedAt: post.publishedAt ? new Date(post.publishedAt) : null
});
this.counters.blogPostId = Math.max(this.counters.blogPostId, post.id + 1);
});
}
// Load reading progresses
if (jsonData.readingProgresses) {
jsonData.readingProgresses.forEach((progress: ReadingProgress) => {
const key = `${progress.userId}-${progress.workId}-${progress.translationId || 0}`;
this.data.readingProgresses.set(key, {
...progress,
lastReadAt: new Date(progress.lastReadAt)
});
this.counters.readingProgressId = Math.max(this.counters.readingProgressId, progress.id + 1);
});
}
// Load analysis results
if (jsonData.analysisResults) {
jsonData.analysisResults.forEach((result: AnalysisResult) => {
this.data.analysisResults.set(result.id, {
...result,
createdAt: new Date(result.createdAt)
});
this.counters.analysisResultId = Math.max(this.counters.analysisResultId, result.id + 1);
});
}
// Load annotations
if (jsonData.annotations) {
jsonData.annotations.forEach((annotation: Annotation) => {
this.data.annotations.set(annotation.id, {
...annotation,
createdAt: new Date(annotation.createdAt)
});
this.counters.annotationId = Math.max(this.counters.annotationId, annotation.id + 1);
});
}
console.log('Data loaded successfully');
console.log(`Users: ${this.data.users.size}`);
console.log(`Authors: ${this.data.authors.size}`);
console.log(`Works: ${this.data.works.size}`);
console.log(`Translations: ${this.data.translations.size}`);
console.log(`Analysis Results: ${this.data.analysisResults.size}`);
console.log(`Annotations: ${this.data.annotations.size}`);
} catch (error) {
console.error('Error loading data from file:', error);
}
}
private saveDataToFile() {
try {
const jsonData = {
users: Array.from(this.data.users.values()),
authors: Array.from(this.data.authors.values()),
works: Array.from(this.data.works.values()),
translations: Array.from(this.data.translations.values()),
tags: Array.from(this.data.tags.values()),
workTagsRelations: Object.fromEntries(
Array.from(this.data.workTagsRelations.entries()).map(
([workId, tagIds]) => [workId, Array.from(tagIds)]
)
),
blogPostTagsRelations: Object.fromEntries(
Array.from(this.data.blogPostTagsRelations.entries()).map(
([postId, tagIds]) => [postId, Array.from(tagIds)]
)
),
bookmarks: Array.from(this.data.bookmarks.values()),
comments: Array.from(this.data.comments.values()),
likes: Array.from(this.data.likes.values()),
collections: Array.from(this.data.collections.values()),
collectionItems: Array.from(this.data.collectionItems.values()),
timelineEvents: Array.from(this.data.timelineEvents.values()),
blogPosts: Array.from(this.data.blogPosts.values()),
readingProgresses: Array.from(this.data.readingProgresses.values()),
analysisResults: Array.from(this.data.analysisResults.values()),
annotations: Array.from(this.data.annotations.values())
};
fs.writeFileSync(this.dataFilePath, JSON.stringify(jsonData, null, 2), 'utf8');
console.log(`Data saved to ${this.dataFilePath}`);
} catch (error) {
console.error('Error saving data to file:', error);
}
}
// User methods
async getUser(id: number): Promise<User | undefined> {
return this.data.users.get(id);
}
async getUserByUsername(username: string): Promise<User | undefined> {
return Array.from(this.data.users.values()).find(user => user.username === username);
}
async createUser(insertUser: InsertUser): Promise<User> {
const id = this.counters.userId++;
const now = new Date();
const user: User = {
...insertUser,
id,
role: 'user',
createdAt: now
};
this.data.users.set(id, user);
this.saveDataToFile();
return user;
}
// Author methods
async getAuthors(limit: number = 100, offset: number = 0): Promise<Author[]> {
return Array.from(this.data.authors.values())
.sort((a, b) => a.name.localeCompare(b.name))
.slice(offset, offset + limit);
}
async getAuthorById(id: number): Promise<Author | undefined> {
return this.data.authors.get(id);
}
async getAuthorBySlug(slug: string): Promise<Author | undefined> {
return Array.from(this.data.authors.values()).find(author => author.slug === slug);
}
async createAuthor(insertAuthor: InsertAuthor): Promise<Author> {
const id = this.counters.authorId++;
const now = new Date();
const author: Author = {
...insertAuthor,
id,
createdAt: now
};
this.data.authors.set(id, author);
this.saveDataToFile();
return author;
}
// Work methods
async getWorks(limit: number = 100, offset: number = 0): Promise<Work[]> {
return Array.from(this.data.works.values())
.sort((a, b) => a.title.localeCompare(b.title))
.slice(offset, offset + limit);
}
async getWorkById(id: number): Promise<Work | undefined> {
return this.data.works.get(id);
}
async getWorkBySlug(slug: string): Promise<Work | undefined> {
return Array.from(this.data.works.values()).find(work => work.slug === slug);
}
async getWorksByAuthorId(authorId: number): Promise<Work[]> {
return Array.from(this.data.works.values()).filter(work => work.authorId === authorId);
}
async getWorksByTags(tagIds: number[]): Promise<Work[]> {
const tagSet = new Set(tagIds);
const workIds = new Set<number>();
this.data.workTagsRelations.forEach((tags, workId) => {
for (const tagId of tags) {
if (tagSet.has(tagId)) {
workIds.add(workId);
break;
}
}
});
return Array.from(workIds).map(id => this.data.works.get(id)!).filter(Boolean);
}
async createWork(insertWork: InsertWork): Promise<Work> {
const id = this.counters.workId++;
const now = new Date();
const work: Work = {
...insertWork,
id,
createdAt: now
};
this.data.works.set(id, work);
this.saveDataToFile();
return work;
}
// Translation methods
async getTranslations(workId: number): Promise<Translation[]> {
return Array.from(this.data.translations.values()).filter(
translation => translation.workId === workId
);
}
async getTranslationById(id: number): Promise<Translation | undefined> {
return this.data.translations.get(id);
}
async createTranslation(insertTranslation: InsertTranslation): Promise<Translation> {
const id = this.counters.translationId++;
const now = new Date();
const translation: Translation = {
...insertTranslation,
id,
createdAt: now
};
this.data.translations.set(id, translation);
this.saveDataToFile();
return translation;
}
// Tag methods
async getTags(): Promise<Tag[]> {
return Array.from(this.data.tags.values());
}
async getTagsByType(type: string): Promise<Tag[]> {
return Array.from(this.data.tags.values()).filter(tag => tag.type === type);
}
async createTag(insertTag: InsertTag): Promise<Tag> {
const id = this.counters.tagId++;
const now = new Date();
const tag: Tag = {
...insertTag,
id,
createdAt: now
};
this.data.tags.set(id, tag);
this.saveDataToFile();
return tag;
}
async addTagToWork(workId: number, tagId: number): Promise<void> {
if (!this.data.workTagsRelations.has(workId)) {
this.data.workTagsRelations.set(workId, new Set());
}
this.data.workTagsRelations.get(workId)!.add(tagId);
this.saveDataToFile();
}
async getWorkTags(workId: number): Promise<Tag[]> {
const tagIds = this.data.workTagsRelations.get(workId) || new Set<number>();
return Array.from(tagIds).map(id => this.data.tags.get(id)!).filter(Boolean);
}
async getBlogPostTags(postId: number): Promise<Tag[]> {
const tagIds = this.data.blogPostTagsRelations.get(postId) || new Set<number>();
return Array.from(tagIds).map(id => this.data.tags.get(id)!).filter(Boolean);
}
async addTagToBlogPost(postId: number, tagId: number): Promise<void> {
console.log(`Adding tag ${tagId} to blog post ${postId}`);
if (!this.data.blogPostTagsRelations.has(postId)) {
console.log(`Creating new tag set for blog post ${postId}`);
this.data.blogPostTagsRelations.set(postId, new Set());
}
this.data.blogPostTagsRelations.get(postId)!.add(tagId);
console.log(`After adding tag, blog post ${postId} has tags:`,
Array.from(this.data.blogPostTagsRelations.get(postId) || new Set()));
this.saveDataToFile();
}
// Bookmark methods
async getBookmarksByUserId(userId: number): Promise<Bookmark[]> {
return Array.from(this.data.bookmarks.values()).filter(bookmark => bookmark.userId === userId);
}
async getBookmarkByUserAndWork(userId: number, workId: number): Promise<Bookmark | undefined> {
return Array.from(this.data.bookmarks.values()).find(
bookmark => bookmark.userId === userId && bookmark.workId === workId
);
}
async createBookmark(insertBookmark: InsertBookmark): Promise<Bookmark> {
const id = this.counters.bookmarkId++;
const now = new Date();
const bookmark: Bookmark = {
...insertBookmark,
id,
createdAt: now
};
this.data.bookmarks.set(id, bookmark);
this.saveDataToFile();
return bookmark;
}
async deleteBookmark(id: number): Promise<void> {
this.data.bookmarks.delete(id);
this.saveDataToFile();
}
// Comment methods
async getCommentsByWorkId(workId: number): Promise<Comment[]> {
return Array.from(this.data.comments.values()).filter(
comment => comment.workId === workId
);
}
async getCommentsByTranslationId(translationId: number): Promise<Comment[]> {
return Array.from(this.data.comments.values()).filter(
comment => comment.translationId === translationId
);
}
async getCommentsByUserId(userId: number): Promise<Comment[]> {
return Array.from(this.data.comments.values()).filter(
comment => comment.userId === userId
);
}
async getCommentsByEntityId(entityType: string, entityId: number): Promise<Comment[]> {
return Array.from(this.data.comments.values()).filter(
comment => comment.entityType === entityType && comment.entityId === entityId
);
}
async createComment(insertComment: InsertComment): Promise<Comment> {
const id = this.counters.commentId++;
const now = new Date();
const comment: Comment = {
...insertComment,
id,
createdAt: now
};
this.data.comments.set(id, comment);
this.saveDataToFile();
return comment;
}
// Like methods
async createLike(insertLike: InsertLike): Promise<Like> {
const id = this.counters.likeId++;
const now = new Date();
const like: Like = {
...insertLike,
id,
createdAt: now
};
this.data.likes.set(id, like);
this.saveDataToFile();
return like;
}
async deleteLike(id: number): Promise<void> {
this.data.likes.delete(id);
this.saveDataToFile();
}
async getLikesByEntity(entityType: string, entityId: number): Promise<Like[]> {
return Array.from(this.data.likes.values()).filter(
like => like.entityType === entityType && like.entityId === entityId
);
}
async getLikesByUserId(userId: number): Promise<Like[]> {
return Array.from(this.data.likes.values()).filter(
like => like.userId === userId
);
}
// Collection methods
async getCollections(limit: number = 100, offset: number = 0): Promise<Collection[]> {
return Array.from(this.data.collections.values())
.sort((a, b) => a.title.localeCompare(b.title))
.slice(offset, offset + limit);
}
async getCollectionsByUserId(userId: number): Promise<Collection[]> {
return Array.from(this.data.collections.values()).filter(
collection => collection.userId === userId
);
}
async getCollectionById(id: number): Promise<Collection | undefined> {
return this.data.collections.get(id);
}
async getCollectionBySlug(slug: string): Promise<Collection | undefined> {
return Array.from(this.data.collections.values()).find(
collection => collection.slug === slug
);
}
async createCollection(insertCollection: InsertCollection): Promise<Collection> {
const id = this.counters.collectionId++;
const now = new Date();
const collection: Collection = {
...insertCollection,
id,
createdAt: now
};
this.data.collections.set(id, collection);
this.saveDataToFile();
return collection;
}
async addWorkToCollection(insertCollectionItem: InsertCollectionItem): Promise<CollectionItem> {
const id = this.counters.collectionItemId++;
const now = new Date();
const collectionItem: CollectionItem = {
...insertCollectionItem,
id,
createdAt: now
};
this.data.collectionItems.set(id, collectionItem);
this.saveDataToFile();
return collectionItem;
}
async getCollectionItems(collectionId: number): Promise<CollectionItem[]> {
return Array.from(this.data.collectionItems.values()).filter(
item => item.collectionId === collectionId
);
}
// Timeline events
async getTimelineEventsByAuthorId(authorId: number): Promise<TimelineEvent[]> {
return Array.from(this.data.timelineEvents.values())
.filter(event => event.authorId === authorId)
.sort((a, b) => a.year - b.year);
}
async createTimelineEvent(insertEvent: InsertTimelineEvent): Promise<TimelineEvent> {
const id = this.counters.timelineEventId++;
const now = new Date();
const event: TimelineEvent = {
...insertEvent,
id,
createdAt: now
};
this.data.timelineEvents.set(id, event);
this.saveDataToFile();
return event;
}
// Blog posts
async getBlogPosts(limit: number = 100, offset: number = 0): Promise<BlogPost[]> {
return Array.from(this.data.blogPosts.values())
.sort((a, b) => {
const dateA = a.publishedAt || a.createdAt;
const dateB = b.publishedAt || b.createdAt;
return dateB.getTime() - dateA.getTime(); // newest first
})
.slice(offset, offset + limit);
}
async getBlogPostById(id: number): Promise<BlogPost | undefined> {
return this.data.blogPosts.get(id);
}
async getBlogPostBySlug(slug: string): Promise<BlogPost | undefined> {
return Array.from(this.data.blogPosts.values()).find(
post => post.slug === slug
);
}
async createBlogPost(insertPost: InsertBlogPost): Promise<BlogPost> {
const id = this.counters.blogPostId++;
const now = new Date();
const post: BlogPost = {
...insertPost,
id,
createdAt: now
};
this.data.blogPosts.set(id, post);
console.log('Created blog post with ID:', id);
this.saveDataToFile();
return post;
}
// Reading progress
async getReadingProgress(userId: number, workId: number, translationId?: number): Promise<ReadingProgress | undefined> {
const key = `${userId}-${workId}-${translationId || 0}`;
return this.data.readingProgresses.get(key);
}
async updateReadingProgress(insertProgress: InsertReadingProgress): Promise<ReadingProgress> {
const key = `${insertProgress.userId}-${insertProgress.workId}-${insertProgress.translationId || 0}`;
if (this.data.readingProgresses.has(key)) {
const existing = this.data.readingProgresses.get(key)!;
const updated: ReadingProgress = {
...existing,
progress: insertProgress.progress ?? existing.progress,
lastReadAt: new Date()
};
this.data.readingProgresses.set(key, updated);
this.saveDataToFile();
return updated;
} else {
const id = this.counters.readingProgressId++;
const progress: ReadingProgress = {
id,
workId: insertProgress.workId,
progress: insertProgress.progress ?? 0,
userId: insertProgress.userId,
translationId: insertProgress.translationId ?? null,
lastReadAt: new Date()
};
this.data.readingProgresses.set(key, progress);
this.saveDataToFile();
return progress;
}
}
// Search and filter methods
async searchWorks(query: string, limit: number = 10): Promise<Work[]> {
const searchTerms = query.toLowerCase().split(/\s+/);
const works = Array.from(this.data.works.values());
const scoredWorks = works.map(work => {
let score = 0;
const title = work.title.toLowerCase();
const description = work.description?.toLowerCase() || '';
searchTerms.forEach(term => {
if (title.includes(term)) score += 10;
if (description.includes(term)) score += 5;
});
return { work, score };
});
return scoredWorks
.filter(item => item.score > 0)
.sort((a, b) => b.score - a.score)
.slice(0, limit)
.map(item => item.work);
}
async searchAuthors(query: string, limit: number = 10): Promise<Author[]> {
const searchTerms = query.toLowerCase().split(/\s+/);
const authors = Array.from(this.data.authors.values());
const scoredAuthors = authors.map(author => {
let score = 0;
const name = author.name.toLowerCase();
const biography = author.biography?.toLowerCase() || '';
const country = author.country?.toLowerCase() || '';
searchTerms.forEach(term => {
if (name.includes(term)) score += 10;
if (country.includes(term)) score += 7;
if (biography.includes(term)) score += 3;
});
return { author, score };
});
return scoredAuthors
.filter(item => item.score > 0)
.sort((a, b) => b.score - a.score)
.slice(0, limit)
.map(item => item.author);
}
async filterWorks(params: {
language?: string,
type?: string,
yearStart?: number,
yearEnd?: number,
tags?: number[]
}): Promise<Work[]> {
let filteredWorks = Array.from(this.data.works.values());
if (params.language) {
filteredWorks = filteredWorks.filter(work => work.language === params.language);
}
if (params.type) {
filteredWorks = filteredWorks.filter(work => work.type === params.type);
}
if (params.yearStart) {
filteredWorks = filteredWorks.filter(work => work.year! >= params.yearStart!);
}
if (params.yearEnd) {
filteredWorks = filteredWorks.filter(work => work.year! <= params.yearEnd!);
}
if (params.tags && params.tags.length > 0) {
const taggedWorkIds = new Set<number>();
this.data.workTagsRelations.forEach((tagIds, workId) => {
for (const tagId of params.tags!) {
if (tagIds.has(tagId)) {
taggedWorkIds.add(workId);
break;
}
}
});
filteredWorks = filteredWorks.filter(work => taggedWorkIds.has(work.id));
}
return filteredWorks;
}
// Analysis methods
async getAnalysisResultsByWorkId(workId: number): Promise<AnalysisResult[]> {
return Array.from(this.data.analysisResults.values()).filter(
result => result.workId === workId
);
}
async getAnalysisResultById(id: number): Promise<AnalysisResult | undefined> {
return this.data.analysisResults.get(id);
}
async getAnalysisResultByWorkAndType(workId: number, type: string): Promise<AnalysisResult | undefined> {
return Array.from(this.data.analysisResults.values()).find(
result => result.workId === workId && result.type === type
);
}
async createAnalysisResult(insertResult: InsertAnalysisResult): Promise<AnalysisResult> {
const id = this.counters.analysisResultId++;
const now = new Date();
const result: AnalysisResult = {
...insertResult,
id,
createdAt: now
};
this.data.analysisResults.set(id, result);
this.saveDataToFile();
return result;
}
// Annotation methods
async getAnnotationsByWorkId(workId: number): Promise<Annotation[]> {
return Array.from(this.data.annotations.values()).filter(
annotation => annotation.workId === workId
);
}
async getAnnotationsByTranslationId(translationId: number): Promise<Annotation[]> {
return Array.from(this.data.annotations.values()).filter(
annotation => annotation.translationId === translationId
);
}
async getAnnotationsByLine(workId: number, lineNumber: number, translationId?: number): Promise<Annotation[]> {
return Array.from(this.data.annotations.values()).filter(
annotation => annotation.workId === workId &&
annotation.lineNumber === lineNumber &&
(translationId === undefined || annotation.translationId === translationId)
);
}
async getAnnotationById(id: number): Promise<Annotation | undefined> {
return this.data.annotations.get(id);
}
async createAnnotation(insertAnnotation: InsertAnnotation): Promise<Annotation> {
const id = this.counters.annotationId++;
const now = new Date();
const annotation: Annotation = {
...insertAnnotation,
id,
createdAt: now
};
this.data.annotations.set(id, annotation);
this.saveDataToFile();
return annotation;
}
}