tercul-frontend/server/storage.ts
mukimovd 14a42223a8 Add ability to analyze content and create personal reading notes
Implements analysis result and annotation methods with in-memory storage for managing work analysis and user annotations.

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/6727ab9c-0666-4f48-a09c-58761e6a20ea.jpg
2025-05-08 00:16:22 +00:00

1213 lines
48 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import {
users, User, InsertUser,
authors, Author, InsertAuthor,
works, Work, InsertWork,
translations, Translation, InsertTranslation,
tags, Tag, InsertTag,
workTags,
bookmarks, Bookmark, InsertBookmark,
comments, Comment, InsertComment,
likes, Like, InsertLike,
collections, Collection, InsertCollection,
collectionItems, CollectionItem, InsertCollectionItem,
timelineEvents, TimelineEvent, InsertTimelineEvent,
blogPosts, BlogPost, InsertBlogPost,
readingProgress, ReadingProgress, InsertReadingProgress,
AnalysisResult, InsertAnalysisResult,
Annotation, InsertAnnotation
} from "@shared/schema";
export interface IStorage {
// User methods
getUser(id: number): Promise<User | undefined>;
getUserByUsername(username: string): Promise<User | undefined>;
createUser(user: InsertUser): Promise<User>;
// Author methods
getAuthors(limit?: number, offset?: number): Promise<Author[]>;
getAuthorById(id: number): Promise<Author | undefined>;
getAuthorBySlug(slug: string): Promise<Author | undefined>;
createAuthor(author: InsertAuthor): Promise<Author>;
// Work methods
getWorks(limit?: number, offset?: number): Promise<Work[]>;
getWorkById(id: number): Promise<Work | undefined>;
getWorkBySlug(slug: string): Promise<Work | undefined>;
getWorksByAuthorId(authorId: number): Promise<Work[]>;
getWorksByTags(tagIds: number[]): Promise<Work[]>;
createWork(work: InsertWork): Promise<Work>;
// Translation methods
getTranslations(workId: number): Promise<Translation[]>;
getTranslationById(id: number): Promise<Translation | undefined>;
createTranslation(translation: InsertTranslation): Promise<Translation>;
// Tag methods
getTags(): Promise<Tag[]>;
getTagsByType(type: string): Promise<Tag[]>;
createTag(tag: InsertTag): Promise<Tag>;
addTagToWork(workId: number, tagId: number): Promise<void>;
getWorkTags(workId: number): Promise<Tag[]>;
getBlogPostTags(postId: number): Promise<Tag[]>;
// Bookmark methods
getBookmarksByUserId(userId: number): Promise<Bookmark[]>;
getBookmarkByUserAndWork(userId: number, workId: number): Promise<Bookmark | undefined>;
createBookmark(bookmark: InsertBookmark): Promise<Bookmark>;
deleteBookmark(id: number): Promise<void>;
// Comment methods
getCommentsByWorkId(workId: number): Promise<Comment[]>;
getCommentsByTranslationId(translationId: number): Promise<Comment[]>;
getCommentsByUserId(userId: number): Promise<Comment[]>;
getCommentsByEntityId(entityType: string, entityId: number): Promise<Comment[]>;
createComment(comment: InsertComment): Promise<Comment>;
// Like methods
createLike(like: InsertLike): Promise<Like>;
deleteLike(id: number): Promise<void>;
getLikesByEntity(entityType: string, entityId: number): Promise<Like[]>;
getLikesByUserId(userId: number): Promise<Like[]>;
// Collection methods
getCollections(limit?: number, offset?: number): Promise<Collection[]>;
getCollectionsByUserId(userId: number): Promise<Collection[]>;
getCollectionById(id: number): Promise<Collection | undefined>;
getCollectionBySlug(slug: string): Promise<Collection | undefined>;
createCollection(collection: InsertCollection): Promise<Collection>;
addWorkToCollection(collectionItem: InsertCollectionItem): Promise<CollectionItem>;
getCollectionItems(collectionId: number): Promise<CollectionItem[]>;
// Timeline events
getTimelineEventsByAuthorId(authorId: number): Promise<TimelineEvent[]>;
createTimelineEvent(event: InsertTimelineEvent): Promise<TimelineEvent>;
// Blog posts
getBlogPosts(limit?: number, offset?: number): Promise<BlogPost[]>;
getBlogPostById(id: number): Promise<BlogPost | undefined>;
getBlogPostBySlug(slug: string): Promise<BlogPost | undefined>;
createBlogPost(post: InsertBlogPost): Promise<BlogPost>;
// Reading progress
getReadingProgress(userId: number, workId: number, translationId?: number): Promise<ReadingProgress | undefined>;
updateReadingProgress(progress: InsertReadingProgress): Promise<ReadingProgress>;
// Search and filter methods
searchWorks(query: string, limit?: number): Promise<Work[]>;
searchAuthors(query: string, limit?: number): Promise<Author[]>;
filterWorks(params: { language?: string, type?: string, yearStart?: number, yearEnd?: number, tags?: number[] }): Promise<Work[]>;
// Analysis methods
getAnalysisResultsByWorkId(workId: number): Promise<AnalysisResult[]>;
getAnalysisResultById(id: number): Promise<AnalysisResult | undefined>;
getAnalysisResultByWorkAndType(workId: number, type: string): Promise<AnalysisResult | undefined>;
createAnalysisResult(result: InsertAnalysisResult): Promise<AnalysisResult>;
// Annotation methods
getAnnotationsByWorkId(workId: number): Promise<Annotation[]>;
getAnnotationsByTranslationId(translationId: number): Promise<Annotation[]>;
getAnnotationsByLine(workId: number, lineNumber: number, translationId?: number): Promise<Annotation[]>;
getAnnotationById(id: number): Promise<Annotation | undefined>;
createAnnotation(annotation: InsertAnnotation): Promise<Annotation>;
}
export class MemStorage implements IStorage {
private users: Map<number, User>;
private authors: Map<number, Author>;
private works: Map<number, Work>;
private translations: Map<number, Translation>;
private tags: Map<number, Tag>;
private workTagsRelations: Map<number, Set<number>>;
private blogPostTagsRelations: Map<number, Set<number>>;
private bookmarks: Map<number, Bookmark>;
private comments: Map<number, Comment>;
private likes: Map<number, Like>;
private collections: Map<number, Collection>;
private collectionItems: Map<number, CollectionItem>;
private timelineEvents: Map<number, TimelineEvent>;
private blogPosts: Map<number, BlogPost>;
private readingProgresses: Map<string, ReadingProgress>;
private analysisResults: Map<number, AnalysisResult>;
private annotations: Map<number, Annotation>;
private userId: number = 1;
private authorId: number = 1;
private workId: number = 1;
private translationId: number = 1;
private tagId: number = 1;
private bookmarkId: number = 1;
private commentId: number = 1;
private likeId: number = 1;
private collectionId: number = 1;
private collectionItemId: number = 1;
private timelineEventId: number = 1;
private blogPostId: number = 1;
private readingProgressId: number = 1;
private analysisResultId: number = 1;
private annotationId: number = 1;
constructor() {
this.users = new Map();
this.authors = new Map();
this.works = new Map();
this.translations = new Map();
this.tags = new Map();
this.workTagsRelations = new Map();
this.blogPostTagsRelations = new Map();
this.bookmarks = new Map();
this.comments = new Map();
this.likes = new Map();
this.collections = new Map();
this.collectionItems = new Map();
this.timelineEvents = new Map();
this.blogPosts = new Map();
this.readingProgresses = new Map();
this.analysisResults = new Map();
this.annotations = new Map();
// Initialize with some data
this.initializeData();
}
private initializeData() {
// Create sample users with different roles
const admin = this.createUser({
username: "admin",
password: "password",
email: "admin@example.com",
displayName: "Administrator",
role: "admin",
bio: "Platform administrator and content curator",
avatar: "https://i.pravatar.cc/150?u=admin"
});
const editor = this.createUser({
username: "editor",
password: "password",
email: "editor@example.com",
displayName: "Content Editor",
role: "editor",
bio: "Professional editor with expertise in Russian literature",
avatar: "https://i.pravatar.cc/150?u=editor"
});
const translator = this.createUser({
username: "translator",
password: "password",
email: "translator@example.com",
displayName: "Literary Translator",
role: "translator",
bio: "Certified translator specializing in Slavic languages",
avatar: "https://i.pravatar.cc/150?u=translator"
});
const reader = this.createUser({
username: "booklover",
password: "password",
email: "reader@example.com",
displayName: "Book Enthusiast",
role: "user",
bio: "Avid reader and literature fan",
avatar: "https://i.pravatar.cc/150?u=reader"
});
const scholar = this.createUser({
username: "scholar",
password: "password",
email: "scholar@example.com",
displayName: "Literary Scholar",
role: "user",
bio: "PhD in Comparative Literature focusing on Romanticism",
avatar: "https://i.pravatar.cc/150?u=scholar"
});
// Create sample authors
const pushkin = this.createAuthor({
name: "Alexander Pushkin",
slug: "alexander-pushkin",
birthYear: 1799,
deathYear: 1837,
country: "Russia",
biography: "Alexander Sergeyevich Pushkin was a Russian poet, playwright, and novelist of the Romantic era. He is considered by many to be the greatest Russian poet and the founder of modern Russian literature. Pushkin was born into Russian nobility in Moscow. His father, Sergey Lvovich Pushkin, was descended from a distinguished family of the Russian nobility that traced its ancestry back to the 12th century. Pushkin's works embraced a wide variety of literary forms, from drama and lyric poetry to literary criticism, fairy tales, and historical narrative.",
portrait: "https://images.unsplash.com/photo-1541688329375-fd29c1b13276?ixlib=rb-1.2.1&auto=format&fit=crop&w=256&h=256&q=80",
createdBy: admin.id
});
const tolstoy = this.createAuthor({
name: "Leo Tolstoy",
slug: "leo-tolstoy",
birthYear: 1828,
deathYear: 1910,
country: "Russia",
biography: "Count Lev Nikolayevich Tolstoy, usually referred to in English as Leo Tolstoy, was a Russian writer who is regarded as one of the greatest authors of all time. He received nominations for the Nobel Prize in Literature every year from 1902 to 1906 and for the Nobel Peace Prize in 1901, 1902, and 1909. Born to an aristocratic Russian family in 1828, Tolstoy's notable works include the novels War and Peace (1869) and Anna Karenina (1878), often cited as pinnacles of realist fiction.",
portrait: "https://images.unsplash.com/photo-1583583214294-20e9a2c176a6?ixlib=rb-1.2.1&auto=format&fit=crop&w=256&h=256&q=80",
createdBy: editor.id
});
const dostoevsky = this.createAuthor({
name: "Fyodor Dostoevsky",
slug: "fyodor-dostoevsky",
birthYear: 1821,
deathYear: 1881,
country: "Russia",
biography: "Fyodor Mikhailovich Dostoevsky was a Russian novelist, philosopher, short story writer, essayist, and journalist. His literary works explore human psychology in the troubled political, social, and spiritual atmospheres of 19th-century Russia, and engage with a variety of philosophical and religious themes. His most acclaimed novels include Crime and Punishment (1866), The Idiot (1869), Demons (1872), and The Brothers Karamazov (1880). Dostoevsky's body of work consists of 12 novels, four novellas, 16 short stories, and numerous other works.",
portrait: "https://images.unsplash.com/photo-1589229722039-eb062f5d834c?ixlib=rb-1.2.1&auto=format&fit=crop&w=256&h=256&q=80",
createdBy: editor.id
});
// Create sample tags (more extensive set)
const lyricTag = this.createTag({ name: "Lyric", type: "genre" });
const loveTag = this.createTag({ name: "Love", type: "theme" });
const poetryTag = this.createTag({ name: "Poetry", type: "genre" });
const romanticismTag = this.createTag({ name: "Romanticism", type: "period" });
const novelTag = this.createTag({ name: "Novel", type: "genre" });
const verseTag = this.createTag({ name: "Verse", type: "form" });
const historyTag = this.createTag({ name: "Historical", type: "theme" });
const gothicTag = this.createTag({ name: "Gothic", type: "genre" });
const fictionTag = this.createTag({ name: "Fiction", type: "genre" });
const warTag = this.createTag({ name: "War", type: "theme" });
const philosophyTag = this.createTag({ name: "Philosophy", type: "theme" });
const russianTag = this.createTag({ name: "Russian", type: "language" });
const epicTag = this.createTag({ name: "Epic", type: "genre" });
const classicTag = this.createTag({ name: "Classic", type: "period" });
const socialCriticismTag = this.createTag({ name: "Social Criticism", type: "theme" });
const existentialismTag = this.createTag({ name: "Existentialism", type: "theme" });
const psychologicalTag = this.createTag({ name: "Psychological", type: "genre" });
const deathTag = this.createTag({ name: "Death", type: "theme" });
const realismTag = this.createTag({ name: "Realism", type: "period" });
// Create sample works with detailed content
const lovedYouWork = this.createWork({
title: "I Loved You",
slug: "i-loved-you",
authorId: pushkin.id,
year: 1829,
type: "poem",
language: "Russian",
content: "Я вас любил: любовь еще, быть может,\nВ душе моей угасла не совсем;\nНо пусть она вас больше не тревожит;\nЯ не хочу печалить вас ничем.\nЯ вас любил безмолвно, безнадежно,\nТо робостью, то ревностью томим;\nЯ вас любил так искренно, так нежно,\nКак дай вам Бог любимой быть другим.",
description: "A short lyrical poem expressing unselfish love and one of Pushkin's most famous works. The poem describes the narrator's admiration for a beloved from whom he has parted. Written in 1829 and first published in 1830, this work showcases Pushkin's ability to express profound emotion with elegant simplicity. The poem's conclusion is particularly notable for its generous sentiment, wishing that the beloved will be loved by someone else as genuinely as the narrator loved her.",
createdBy: admin.id
});
// Add translations for "I Loved You"
const englishTranslation1 = this.createTranslation({
workId: lovedYouWork.id,
translatorId: translator.id,
language: "English",
year: 1930,
title: "I Loved You (Avrahm Yarmolinsky Translation)",
content: "I loved you; even now, I may confess,\nWithin my heart, the embers yet are warm;\nBut do not let it cause you more distress,\nI do not want to sadden you or harm.\nI loved you without hope, without a word,\nIn jealousy and fear, I've been the slave,\nI loved you with such passion, such pure love,\nAs may your future lover never have.",
description: "A classic translation by Avrahm Yarmolinsky that tries to preserve the rhyme scheme and meter of the original Russian poem."
});
const englishTranslation2 = this.createTranslation({
workId: lovedYouWork.id,
translatorId: scholar.id,
language: "English",
year: 2001,
title: "I Loved You (Modern Translation)",
content: "I loved you once, and still, perhaps, love's yearning\nWithin my soul has not quite died away.\nBut let it not dismay you any longer;\nI do not wish to cause you any pain.\nI loved you wordlessly, without hope, shyly,\nNow jealous and now diffident and shy;\nI loved you with such tenderness, such candor,\nAs God grant you be loved by and by.",
description: "A more modern translation that focuses on conveying the emotional content rather than strict adherence to form."
});
const frenchTranslation = this.createTranslation({
workId: lovedYouWork.id,
translatorId: translator.id,
language: "French",
year: 1947,
title: "Je vous aimais",
content: "Je vous aimais: peut-être cet amour\nN'est-il pas tout à fait éteint dans mon âme;\nMais ne vous tourmentez plus désormais;\nJe ne veux plus rien qui vous afflige.\nJe vous aimais sans rien dire, sans espoir,\nTour à tour timide et jaloux;\nJe vous aimais avec tant de tendresse, tant de sincérité,\nQue Dieu veuille que vous soyez aimée ainsi par un autre.",
description: "A French translation that captures the lyrical quality of Pushkin's original poem."
});
const germanTranslation = this.createTranslation({
workId: lovedYouWork.id,
translatorId: scholar.id,
language: "German",
year: 1952,
title: "Ich liebte Sie",
content: "Ich liebte Sie, und diese Liebe mag\nIn meiner Seele noch nicht ganz verklungen sein,\nDoch soll sie Ihnen keinen Kummer bringen,\nIch will Sie nicht betrüben, das soll nicht sein.\nIch liebte Sie so hoffnungslos, so stumm,\nVon Schüchternheit, von Eifersucht gequält,\nIch liebte Sie so treu, so zärtlich, wie\nGott gebe, daß Sie jemand so noch liebt.",
description: "A German translation that maintains the formal structure while conveying the emotional depth of the original."
});
// Add tags to "I Loved You" work
this.addTagToWork(lovedYouWork.id, lyricTag.id);
this.addTagToWork(lovedYouWork.id, loveTag.id);
this.addTagToWork(lovedYouWork.id, poetryTag.id);
this.addTagToWork(lovedYouWork.id, romanticismTag.id);
this.addTagToWork(lovedYouWork.id, russianTag.id);
// Add comments to the work and translations
this.createComment({
userId: reader.id,
entityType: "work",
entityId: lovedYouWork.id,
content: "This is one of my favorite Pushkin poems. The selflessness expressed in the final lines is truly moving.",
createdAt: new Date()
});
this.createComment({
userId: scholar.id,
entityType: "work",
entityId: lovedYouWork.id,
content: "The poem's structure reinforces its emotional journey. Notice how the final couplet resolves the tension built throughout the octave.",
createdAt: new Date()
});
this.createComment({
userId: translator.id,
entityType: "translation",
entityId: englishTranslation1.id,
content: "This translation captures the rhyme scheme well, but I find it loses some of the gentle tone of the original Russian.",
createdAt: new Date()
});
this.createComment({
userId: editor.id,
entityType: "translation",
entityId: englishTranslation2.id,
content: "The modern translation does a better job of conveying the emotional nuance, particularly in the last two lines.",
createdAt: new Date()
});
// Add line-specific annotations
this.createComment({
userId: scholar.id,
entityType: "line",
entityId: lovedYouWork.id,
lineNumber: 1,
content: "The opening phrase 'Я вас любил' (I loved you) is stated in the past tense, immediately establishing the poem's premise of a love that has ended.",
createdAt: new Date()
});
this.createComment({
userId: scholar.id,
entityType: "line",
entityId: lovedYouWork.id,
lineNumber: 4,
content: "This line demonstrates the narrator's selfless concern for the beloved's happiness.",
createdAt: new Date()
});
this.createComment({
userId: translator.id,
entityType: "line",
entityId: lovedYouWork.id,
lineNumber: 7,
content: "The repetition of 'так' (so) emphasizes the depth and purity of the speaker's love.",
createdAt: new Date()
});
this.createComment({
userId: reader.id,
entityType: "line",
entityId: lovedYouWork.id,
lineNumber: 8,
content: "The final line is what makes this poem so powerful - wishing for someone else to love her as genuinely as he did.",
createdAt: new Date()
});
// Add likes
this.createLike({
userId: reader.id,
entityType: "work",
entityId: lovedYouWork.id
});
this.createLike({
userId: scholar.id,
entityType: "work",
entityId: lovedYouWork.id
});
this.createLike({
userId: translator.id,
entityType: "work",
entityId: lovedYouWork.id
});
this.createLike({
userId: editor.id,
entityType: "translation",
entityId: englishTranslation2.id
});
this.createLike({
userId: reader.id,
entityType: "translation",
entityId: frenchTranslation.id
});
// Add bookmarks
this.createBookmark({
userId: reader.id,
workId: lovedYouWork.id,
note: "Beautiful love poem to revisit"
});
this.createBookmark({
userId: scholar.id,
workId: lovedYouWork.id,
note: "Good example of Pushkin's lyrical style"
});
// Create timeline events for Pushkin
this.createTimelineEvent({
authorId: pushkin.id,
year: 1799,
title: "Birth",
description: "Alexander Pushkin was born in Moscow to a noble family."
});
this.createTimelineEvent({
authorId: pushkin.id,
year: 1811,
title: "Lyceum",
description: "Entered the prestigious Imperial Lyceum at Tsarskoye Selo."
});
this.createTimelineEvent({
authorId: pushkin.id,
year: 1820,
title: "Southern Exile",
description: "Exiled to the south of Russia due to his political verses."
});
this.createTimelineEvent({
authorId: pushkin.id,
year: 1824,
title: "Northern Exile",
description: "Confined to his family estate at Mikhailovskoye."
});
this.createTimelineEvent({
authorId: pushkin.id,
year: 1826,
title: "Return from Exile",
description: "Pardoned by Tsar Nicholas I and allowed to return from exile."
});
this.createTimelineEvent({
authorId: pushkin.id,
year: 1829,
title: "Composition of 'I Loved You'",
description: "Wrote the poem 'I Loved You', possibly inspired by his complicated relationship with Anna Olenina."
});
this.createTimelineEvent({
authorId: pushkin.id,
year: 1830,
title: "Engagement",
description: "Became engaged to Natalia Goncharova."
});
this.createTimelineEvent({
authorId: pushkin.id,
year: 1837,
title: "Death",
description: "Fatally wounded in a duel with Georges d'Anthès over the honor of his wife, and died two days later."
});
// Create a collection
const pushkinCollection = this.createCollection({
title: "Essential Pushkin",
slug: "essential-pushkin",
description: "A collection of Alexander Pushkin's most important works, showcasing his range and significance in Russian literature.",
userId: scholar.id,
isPublic: true
});
this.addWorkToCollection({
collectionId: pushkinCollection.id,
workId: lovedYouWork.id,
order: 1,
note: "A perfect introduction to Pushkin's lyrical poetry."
});
// Create a blog post about literary analysis
const analysisPost = this.createBlogPost({
title: "The Art of Selflessness in Pushkin's 'I Loved You'",
slug: "selflessness-pushkins-i-loved-you",
content: "# The Art of Selflessness in Pushkin's 'I Loved You'\n\nAlexander Pushkin's short lyric 'I Loved You' (Я вас любил) stands as one of the most profound expressions of selfless love in world literature. Written in 1829 and published in 1830, this eight-line poem has captivated readers for nearly two centuries.\n\n## The Power of Simplicity\n\nWhat makes this poem so enduring is its elegant simplicity. In just eight lines, Pushkin creates a complete emotional journey from past love to present generosity of spirit. The poem begins with a straightforward declaration—'I loved you'—establishing both the subject and the past tense nature of the emotion.\n\n## Linguistic Analysis\n\nThe original Russian employs a formal 'you' (вас) throughout, creating a sense of respectful distance between the speaker and the addressee. This formality contrasts with the deeply personal nature of the sentiments expressed, creating a tension that underscores the speaker's restraint.\n\n## The Culminating Wish\n\nThe poem's final line—'Как дай вам Бог любимой быть другим' (As God grant you be loved by another)—represents the apex of selfless love in literature. Here, the speaker doesn't merely accept that his beloved will love another; he actively wishes that she will experience a love as genuine and deep as his own.\n\n## Translation Challenges\n\nTranslators have long struggled with capturing the perfect balance of formal structure and emotional resonance in this poem. The challenge lies in preserving the musicality of Pushkin's iambic tetrameter while conveying the subtle emotional shadings of the original Russian.\n\n## Conclusion\n\nPushkin's 'I Loved You' demonstrates how the most profound human emotions can be expressed with remarkable economy and precision. Its enduring appeal lies in its universal theme—the selfless wish for another's happiness—expressed with a sincerity that transcends cultural and temporal boundaries.",
authorId: scholar.id,
publishedAt: new Date(),
featured: true
});
const eugeneOnegin = this.createWork({
title: "Eugene Onegin",
slug: "eugene-onegin",
authorId: pushkin.id,
year: 1833,
type: "poem",
language: "Russian",
content: "Sample content for Eugene Onegin...",
description: "A classic of Russian literature, this novel in verse follows the story of Eugene Onegin, a bored aristocrat who rejects the love of Tatyana and later comes to regret it.",
createdBy: admin.id
});
this.addTagToWork(eugeneOnegin.id, romanticismTag.id);
this.addTagToWork(eugeneOnegin.id, novelTag.id);
this.addTagToWork(eugeneOnegin.id, verseTag.id);
// Add translations for Eugene Onegin directly
console.log("Adding translations for Eugene Onegin with ID:", eugeneOnegin.id);
// Create translations directly
const englishTransId = this.translationId++;
const englishTrans: Translation = {
id: englishTransId,
workId: eugeneOnegin.id,
translatorId: translator.id,
language: "English",
year: 1964,
title: "Eugene Onegin (Nabokov Translation)",
content: "Sample content of Nabokov's English translation of Eugene Onegin...",
description: "Vladimir Nabokov's renowned literal translation that focuses on precision and accuracy at the expense of poetic form.",
createdAt: new Date()
};
this.translations.set(englishTransId, englishTrans);
const modernEnglishTransId = this.translationId++;
const modernEnglishTrans: Translation = {
id: modernEnglishTransId,
workId: eugeneOnegin.id,
translatorId: scholar.id,
language: "English",
year: 2008,
title: "Eugene Onegin (Modern Translation)",
content: "Sample content of a modern English translation of Eugene Onegin...",
description: "A modern translation that attempts to balance accuracy with poetic rhythm and flow.",
createdAt: new Date()
};
this.translations.set(modernEnglishTransId, modernEnglishTrans);
const frenchTransId = this.translationId++;
const frenchTrans: Translation = {
id: frenchTransId,
workId: eugeneOnegin.id,
translatorId: translator.id,
language: "French",
year: 1960,
title: "Eugène Onéguine",
content: "Sample content of French translation of Eugene Onegin...",
description: "A French translation that captures the spirit and elegance of Pushkin's original verse novel.",
createdAt: new Date()
};
this.translations.set(frenchTransId, frenchTrans);
console.log("Added translations with IDs:", englishTransId, modernEnglishTransId, frenchTransId);
const bronzeHorseman = this.createWork({
title: "The Bronze Horseman",
slug: "the-bronze-horseman",
authorId: pushkin.id,
year: 1833,
type: "poem",
language: "Russian",
content: "Sample content for The Bronze Horseman...",
description: "A narrative poem depicting the equestrian statue of Peter the Great in Saint Petersburg and the great flood of 1824, widely considered one of Pushkin's masterpieces.",
createdBy: admin.id
});
this.addTagToWork(bronzeHorseman.id, poetryTag.id);
this.addTagToWork(bronzeHorseman.id, historyTag.id);
const queenOfSpades = this.createWork({
title: "The Queen of Spades",
slug: "the-queen-of-spades",
authorId: pushkin.id,
year: 1834,
type: "story",
language: "Russian",
content: "Sample content for The Queen of Spades...",
description: "A short story about human avarice, telling the tale of a young officer seeking the secret of an elderly countess's success at faro, a card game.",
createdBy: admin.id
});
this.addTagToWork(queenOfSpades.id, fictionTag.id);
this.addTagToWork(queenOfSpades.id, gothicTag.id);
// Create sample translations
this.createTranslation({
workId: lovedYouWork.id,
title: "I Loved You",
language: "English",
content: "I loved you, and I probably still do,\nAnd for a while the feeling may remain...\nBut let my love no longer trouble you,\nI do not wish to cause you any pain.\nI loved you silently, without hope,\nAt times too jealous and at times too shy.\nI loved you truly, with a tender scope,\nAs may God grant you to be loved now by.",
translatorId: admin.id,
year: 2018,
notes: "Translated by John Fenwick in 2018. This translation attempts to preserve the rhyme scheme (ABAB CDCD) and structure of the original while conveying the tender sentiment of Pushkin's lyric."
});
// Create timeline events
this.createTimelineEvent({
authorId: pushkin.id,
year: 1799,
title: "Birth",
description: "Born in Moscow to Sergei Lvovich and Nadezhda Ossipovna Pushkin"
});
this.createTimelineEvent({
authorId: pushkin.id,
year: 1811,
title: "Education",
description: "Enrolled at the Imperial Lyceum at Tsarskoye Selo"
});
this.createTimelineEvent({
authorId: pushkin.id,
year: 1820,
title: "First major work",
description: "Published his first long poem 'Ruslan and Ludmila'"
});
this.createTimelineEvent({
authorId: pushkin.id,
year: 1837,
title: "Death",
description: "Died in St. Petersburg from wounds received in a duel with Georges d'Anthès"
});
// Create sample blog post - work around async issues by creating the object directly
const blogPostId = this.blogPostId++;
const blogPost: BlogPost = {
id: blogPostId,
title: "Exploring Russian Poetry in the Digital Age",
slug: "exploring-russian-poetry-digital-age",
content: `
# Exploring Russian Poetry in the Digital Age
The rich tradition of Russian poetry, from the romantic verses of Alexander Pushkin to the Soviet-era works of Anna Akhmatova, continues to captivate readers worldwide. In our increasingly digital world, how do we preserve and promote this literary heritage while making it accessible to new generations?
## The Digital Transformation of Literature
Digital platforms offer unprecedented opportunities for exploring Russian poetry:
- Side-by-side translations allow readers to compare different interpretations
- Interactive annotations provide historical and cultural context
- Audio recordings capture the rhythm and musicality of the original language
## Challenges and Opportunities
While digitization opens new doors, it also presents challenges. How do we balance modernization with respect for tradition? How can we ensure that the emotional impact of these works isn't lost in the transition from page to screen?
As we continue to develop the Tercul platform, we're committed to addressing these questions, creating an immersive experience that honors the depth and beauty of Russian poetic traditions while embracing the possibilities of digital innovation.
`,
authorId: admin.id,
excerpt: "Exploring how digital platforms can help preserve and promote the rich tradition of Russian poetry in the modern age.",
publishedAt: new Date(),
createdAt: new Date()
};
// Add the blog post to storage manually to avoid async issues
this.blogPosts.set(blogPostId, blogPost);
console.log('Created blog post with ID:', blogPostId);
// Add tags to the blog post with explicit tag IDs
console.log('Adding tags to blog post', blogPostId);
const blogPoetryTagId = this.tags.get(3)?.id; // 3 is typically the ID for poetryTag
const blogRomanticismTagId = this.tags.get(4)?.id; // 4 is typically the ID for romanticismTag
if (blogPoetryTagId) {
this.addTagToBlogPost(blogPostId, blogPoetryTagId);
}
if (blogRomanticismTagId) {
this.addTagToBlogPost(blogPostId, blogRomanticismTagId);
}
// Add a comment to the blog post
console.log('Creating comment for blog post', blogPostId);
this.createComment({
content: "Fascinating exploration of how technology can preserve literary traditions!",
userId: admin.id,
workId: null as any, // TypeScript workaround for optional field
translationId: null,
lineNumber: null,
entityType: "blogPost",
entityId: blogPostId,
parentId: null
});
}
// User methods
async getUser(id: number): Promise<User | undefined> {
return this.users.get(id);
}
async getUserByUsername(username: string): Promise<User | undefined> {
return Array.from(this.users.values()).find(user => user.username === username);
}
async createUser(insertUser: InsertUser): Promise<User> {
const id = this.userId++;
const now = new Date();
const user: User = {
...insertUser,
id,
role: 'user',
createdAt: now
};
this.users.set(id, user);
return user;
}
// Author methods
async getAuthors(limit: number = 100, offset: number = 0): Promise<Author[]> {
return Array.from(this.authors.values())
.sort((a, b) => a.name.localeCompare(b.name))
.slice(offset, offset + limit);
}
async getAuthorById(id: number): Promise<Author | undefined> {
return this.authors.get(id);
}
async getAuthorBySlug(slug: string): Promise<Author | undefined> {
return Array.from(this.authors.values()).find(author => author.slug === slug);
}
async createAuthor(insertAuthor: InsertAuthor): Promise<Author> {
const id = this.authorId++;
const now = new Date();
const author: Author = {
...insertAuthor,
id,
createdAt: now
};
this.authors.set(id, author);
return author;
}
// Work methods
async getWorks(limit: number = 100, offset: number = 0): Promise<Work[]> {
return Array.from(this.works.values())
.sort((a, b) => a.title.localeCompare(b.title))
.slice(offset, offset + limit);
}
async getWorkById(id: number): Promise<Work | undefined> {
return this.works.get(id);
}
async getWorkBySlug(slug: string): Promise<Work | undefined> {
return Array.from(this.works.values()).find(work => work.slug === slug);
}
async getWorksByAuthorId(authorId: number): Promise<Work[]> {
return Array.from(this.works.values()).filter(work => work.authorId === authorId);
}
async getWorksByTags(tagIds: number[]): Promise<Work[]> {
const tagSet = new Set(tagIds);
const workIds = new Set<number>();
this.workTagsRelations.forEach((tags, workId) => {
for (const tagId of tags) {
if (tagSet.has(tagId)) {
workIds.add(workId);
break;
}
}
});
return Array.from(workIds).map(id => this.works.get(id)!).filter(Boolean);
}
async createWork(insertWork: InsertWork): Promise<Work> {
const id = this.workId++;
const now = new Date();
const work: Work = {
...insertWork,
id,
createdAt: now
};
this.works.set(id, work);
return work;
}
// Translation methods
async getTranslations(workId: number): Promise<Translation[]> {
return Array.from(this.translations.values()).filter(
translation => translation.workId === workId
);
}
async getTranslationById(id: number): Promise<Translation | undefined> {
return this.translations.get(id);
}
async createTranslation(insertTranslation: InsertTranslation): Promise<Translation> {
const id = this.translationId++;
const now = new Date();
const translation: Translation = {
...insertTranslation,
id,
createdAt: now
};
this.translations.set(id, translation);
return translation;
}
// Tag methods
async getTags(): Promise<Tag[]> {
return Array.from(this.tags.values());
}
async getTagsByType(type: string): Promise<Tag[]> {
return Array.from(this.tags.values()).filter(tag => tag.type === type);
}
async createTag(insertTag: InsertTag): Promise<Tag> {
const id = this.tagId++;
const now = new Date();
const tag: Tag = {
...insertTag,
id,
createdAt: now
};
this.tags.set(id, tag);
return tag;
}
async addTagToWork(workId: number, tagId: number): Promise<void> {
if (!this.workTagsRelations.has(workId)) {
this.workTagsRelations.set(workId, new Set());
}
this.workTagsRelations.get(workId)!.add(tagId);
}
async getWorkTags(workId: number): Promise<Tag[]> {
const tagIds = this.workTagsRelations.get(workId) || new Set();
return Array.from(tagIds).map(id => this.tags.get(id)!).filter(Boolean);
}
async getBlogPostTags(postId: number): Promise<Tag[]> {
console.log(`Getting tags for blog post ${postId}`);
console.log(`BlogPostTagsRelations map:`, Array.from(this.blogPostTagsRelations.entries()));
const tagIds = this.blogPostTagsRelations.get(postId) || new Set();
console.log(`Tag IDs for blog post ${postId}:`, Array.from(tagIds));
const tags = Array.from(tagIds).map(id => this.tags.get(id)!).filter(Boolean);
console.log(`Found ${tags.length} tags for blog post ${postId}:`, tags);
return tags;
}
async addTagToBlogPost(postId: number, tagId: number): Promise<void> {
console.log(`Adding tag ${tagId} to blog post ${postId}`);
if (!this.blogPostTagsRelations.has(postId)) {
console.log(`Creating new tag set for blog post ${postId}`);
this.blogPostTagsRelations.set(postId, new Set());
}
this.blogPostTagsRelations.get(postId)!.add(tagId);
console.log(`After adding tag, blog post ${postId} has tags:`,
Array.from(this.blogPostTagsRelations.get(postId) || new Set()));
}
// Bookmark methods
async getBookmarksByUserId(userId: number): Promise<Bookmark[]> {
return Array.from(this.bookmarks.values()).filter(
bookmark => bookmark.userId === userId
);
}
async getBookmarkByUserAndWork(userId: number, workId: number): Promise<Bookmark | undefined> {
return Array.from(this.bookmarks.values()).find(
bookmark => bookmark.userId === userId && bookmark.workId === workId
);
}
async createBookmark(insertBookmark: InsertBookmark): Promise<Bookmark> {
const id = this.bookmarkId++;
const now = new Date();
const bookmark: Bookmark = {
...insertBookmark,
id,
createdAt: now
};
this.bookmarks.set(id, bookmark);
return bookmark;
}
async deleteBookmark(id: number): Promise<void> {
this.bookmarks.delete(id);
}
// Comment methods
async getCommentsByWorkId(workId: number): Promise<Comment[]> {
return Array.from(this.comments.values()).filter(
comment => comment.workId === workId
);
}
async getCommentsByTranslationId(translationId: number): Promise<Comment[]> {
return Array.from(this.comments.values()).filter(
comment => comment.translationId === translationId
);
}
async getCommentsByUserId(userId: number): Promise<Comment[]> {
return Array.from(this.comments.values()).filter(
comment => comment.userId === userId
);
}
async getCommentsByEntityId(entityType: string, entityId: number): Promise<Comment[]> {
console.log(`Looking for comments with entityType=${entityType} and entityId=${entityId}`);
// If we're using the primary entity fields (workId or translationId)
if (entityType === 'work') {
return this.getCommentsByWorkId(entityId);
} else if (entityType === 'translation') {
return this.getCommentsByTranslationId(entityId);
} else {
// For all other entity types, use the generic entityType and entityId fields
const comments = Array.from(this.comments.values()).filter(
comment =>
comment.entityType === entityType &&
comment.entityId === entityId
);
console.log(`Found ${comments.length} comments for ${entityType}:${entityId}`, comments);
return comments;
}
}
async createComment(insertComment: InsertComment): Promise<Comment> {
const id = this.commentId++;
const now = new Date();
const comment: Comment = {
...insertComment,
id,
createdAt: now
};
this.comments.set(id, comment);
return comment;
}
// Like methods
async createLike(insertLike: InsertLike): Promise<Like> {
const id = this.likeId++;
const now = new Date();
const like: Like = {
...insertLike,
id,
createdAt: now
};
this.likes.set(id, like);
return like;
}
async deleteLike(id: number): Promise<void> {
this.likes.delete(id);
}
async getLikesByEntity(entityType: string, entityId: number): Promise<Like[]> {
return Array.from(this.likes.values()).filter(
like => like.entityType === entityType && like.entityId === entityId
);
}
async getLikesByUserId(userId: number): Promise<Like[]> {
return Array.from(this.likes.values()).filter(
like => like.userId === userId
);
}
// Collection methods
async getCollections(limit: number = 100, offset: number = 0): Promise<Collection[]> {
return Array.from(this.collections.values())
.filter(collection => collection.isPublic)
.sort((a, b) => a.name.localeCompare(b.name))
.slice(offset, offset + limit);
}
async getCollectionsByUserId(userId: number): Promise<Collection[]> {
return Array.from(this.collections.values()).filter(
collection => collection.userId === userId
);
}
async getCollectionById(id: number): Promise<Collection | undefined> {
return this.collections.get(id);
}
async getCollectionBySlug(slug: string): Promise<Collection | undefined> {
return Array.from(this.collections.values()).find(
collection => collection.slug === slug
);
}
async createCollection(insertCollection: InsertCollection): Promise<Collection> {
const id = this.collectionId++;
const now = new Date();
const collection: Collection = {
...insertCollection,
id,
createdAt: now
};
this.collections.set(id, collection);
return collection;
}
async addWorkToCollection(insertCollectionItem: InsertCollectionItem): Promise<CollectionItem> {
const id = this.collectionItemId++;
const now = new Date();
const collectionItem: CollectionItem = {
...insertCollectionItem,
id,
addedAt: now
};
this.collectionItems.set(id, collectionItem);
return collectionItem;
}
async getCollectionItems(collectionId: number): Promise<CollectionItem[]> {
return Array.from(this.collectionItems.values())
.filter(item => item.collectionId === collectionId)
.sort((a, b) => a.order - b.order);
}
// Timeline events
async getTimelineEventsByAuthorId(authorId: number): Promise<TimelineEvent[]> {
return Array.from(this.timelineEvents.values())
.filter(event => event.authorId === authorId)
.sort((a, b) => a.year - b.year);
}
async createTimelineEvent(insertEvent: InsertTimelineEvent): Promise<TimelineEvent> {
const id = this.timelineEventId++;
const now = new Date();
const event: TimelineEvent = {
...insertEvent,
id,
createdAt: now
};
this.timelineEvents.set(id, event);
return event;
}
// Blog posts
async getBlogPosts(limit: number = 100, offset: number = 0): Promise<BlogPost[]> {
return Array.from(this.blogPosts.values())
.filter(post => post.publishedAt !== null)
.sort((a, b) => (b.publishedAt?.getTime() || 0) - (a.publishedAt?.getTime() || 0))
.slice(offset, offset + limit);
}
async getBlogPostById(id: number): Promise<BlogPost | undefined> {
return this.blogPosts.get(id);
}
async getBlogPostBySlug(slug: string): Promise<BlogPost | undefined> {
return Array.from(this.blogPosts.values()).find(post => post.slug === slug);
}
async createBlogPost(insertPost: InsertBlogPost): Promise<BlogPost> {
const id = this.blogPostId++;
const now = new Date();
const post: BlogPost = {
...insertPost,
id,
createdAt: now
};
this.blogPosts.set(id, post);
return post;
}
// Reading progress
async getReadingProgress(userId: number, workId: number, translationId?: number): Promise<ReadingProgress | undefined> {
const key = `${userId}-${workId}-${translationId || 0}`;
return this.readingProgresses.get(key);
}
async updateReadingProgress(insertProgress: InsertReadingProgress): Promise<ReadingProgress> {
const key = `${insertProgress.userId}-${insertProgress.workId}-${insertProgress.translationId || 0}`;
const now = new Date();
const existing = this.readingProgresses.get(key);
if (existing) {
const updated: ReadingProgress = {
...existing,
progress: insertProgress.progress,
lastReadAt: now
};
this.readingProgresses.set(key, updated);
return updated;
} else {
const id = this.readingProgressId++;
const progress: ReadingProgress = {
...insertProgress,
id,
lastReadAt: now
};
this.readingProgresses.set(key, progress);
return progress;
}
}
// Search and filter methods
async searchWorks(query: string, limit: number = 10): Promise<Work[]> {
const lowerQuery = query.toLowerCase();
return Array.from(this.works.values())
.filter(work =>
work.title.toLowerCase().includes(lowerQuery) ||
work.description?.toLowerCase().includes(lowerQuery)
)
.slice(0, limit);
}
async searchAuthors(query: string, limit: number = 10): Promise<Author[]> {
const lowerQuery = query.toLowerCase();
return Array.from(this.authors.values())
.filter(author =>
author.name.toLowerCase().includes(lowerQuery) ||
author.biography?.toLowerCase().includes(lowerQuery)
)
.slice(0, limit);
}
async filterWorks(params: {
language?: string,
type?: string,
yearStart?: number,
yearEnd?: number,
tags?: number[]
}): Promise<Work[]> {
let filtered = Array.from(this.works.values());
if (params.language) {
filtered = filtered.filter(work => work.language === params.language);
}
if (params.type) {
filtered = filtered.filter(work => work.type === params.type);
}
if (params.yearStart) {
filtered = filtered.filter(work => work.year !== undefined && work.year >= (params.yearStart || 0));
}
if (params.yearEnd) {
filtered = filtered.filter(work => work.year !== undefined && work.year <= (params.yearEnd || 3000));
}
if (params.tags && params.tags.length > 0) {
const taggedWorks = await this.getWorksByTags(params.tags);
const taggedWorkIds = new Set(taggedWorks.map(work => work.id));
filtered = filtered.filter(work => taggedWorkIds.has(work.id));
}
return filtered;
}
}
// Now using JsonStorage instead of MemStorage
import { JsonStorage } from './jsonStorage';
export const storage = new JsonStorage();