Skip to content

Commit 9bf1046

Browse files
committed
הוספת תקופה עבור כל מחבר על פי הקובץ סדר הדורות
1 parent cd824c3 commit 9bf1046

13 files changed

Lines changed: 443 additions & 6 deletions

File tree

core/src/commonMain/kotlin/io/github/kdroidfilter/seforimlibrary/core/IdResolverProvider.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ interface IdResolverProvider {
1212
fun resolveCategoryId(title: String, parentId: Long?, level: Int): Long?
1313
fun resolveSourceId(name: String): Long?
1414
fun resolveAuthorId(name: String): Long?
15+
fun resolveGenerationId(name: String): Long?
1516
fun resolveTopicId(name: String): Long?
1617
fun resolveConnectionTypeId(name: String): Long?
1718
fun resolvePubPlaceId(name: String): Long?

core/src/commonMain/kotlin/io/github/kdroidfilter/seforimlibrary/core/models/Author.kt

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,30 @@ package io.github.kdroidfilter.seforimlibrary.core.models
22

33
import kotlinx.serialization.Serializable
44

5+
/**
6+
* Represents a generation / era code (e.g. חז"ל, ראשונים, אחרונים …)
7+
*
8+
* @property id The unique identifier of the generation
9+
* @property name The name of the generation
10+
*/
11+
@Serializable
12+
data class Generation(
13+
val id: Long = 0,
14+
val name: String
15+
)
16+
517
/**
618
* Represents a book author in the library
719
*
820
* @property id The unique identifier of the author
921
* @property name The name of the author
22+
* @property generationId The identifier of the generation/era of this author (nullable)
23+
* @property generationName The display name of the generation (nullable, populated on read)
1024
*/
1125
@Serializable
1226
data class Author(
1327
val id: Long = 0,
14-
val name: String
28+
val name: String,
29+
val generationId: Long? = null,
30+
val generationName: String? = null
1531
)

dao/src/commonMain/kotlin/io/github/kdroidfilter/seforimlibrary/dao/extensions/ModelExtensions.kt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import io.github.kdroidfilter.seforimlibrary.core.models.Author
66
import io.github.kdroidfilter.seforimlibrary.core.models.Book
77
import io.github.kdroidfilter.seforimlibrary.core.models.Category
88
import io.github.kdroidfilter.seforimlibrary.core.models.ConnectionType
9+
import io.github.kdroidfilter.seforimlibrary.core.models.Generation
910
import io.github.kdroidfilter.seforimlibrary.core.models.Line
1011
import io.github.kdroidfilter.seforimlibrary.core.models.LineAltTocMapping
1112
import io.github.kdroidfilter.seforimlibrary.core.models.Link
@@ -34,6 +35,19 @@ private val logger = Logger.withTag("ModelExtensions")
3435
*/
3536
fun io.github.kdroidfilter.seforimlibrary.db.Author.toModel(): Author {
3637
return Author(
38+
id = id,
39+
name = name,
40+
generationId = generationId
41+
)
42+
}
43+
44+
/**
45+
* Converts a database Generation entity to a domain Generation model.
46+
*
47+
* @return The domain Generation model
48+
*/
49+
fun io.github.kdroidfilter.seforimlibrary.db.Generation.toModel(): Generation {
50+
return Generation(
3751
id = id,
3852
name = name
3953
)

dao/src/commonMain/kotlin/io/github/kdroidfilter/seforimlibrary/dao/repository/SeforimRepository.kt

Lines changed: 61 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import io.github.kdroidfilter.seforimlibrary.core.models.Author
1313
import io.github.kdroidfilter.seforimlibrary.core.models.Book
1414
import io.github.kdroidfilter.seforimlibrary.core.models.Category
1515
import io.github.kdroidfilter.seforimlibrary.core.models.ConnectionType
16+
import io.github.kdroidfilter.seforimlibrary.core.models.Generation
1617
import io.github.kdroidfilter.seforimlibrary.core.models.Line
1718
import io.github.kdroidfilter.seforimlibrary.core.models.LineAltTocMapping
1819
import io.github.kdroidfilter.seforimlibrary.core.models.LineTocMapping
@@ -577,19 +578,76 @@ class SeforimRepository(databasePath: String, private val driver: SqlDriver) {
577578
return@withContext author?.toModel()
578579
}
579580

581+
// ── Generation helpers ───────────────────────────────────────────
582+
583+
/**
584+
* Insert a generation (era) and return its ID.
585+
* If [explicitId] is provided, it is used as the ID (for stable ID resolution).
586+
* If the generation already exists, returns its existing ID.
587+
*/
588+
suspend fun insertGeneration(name: String, explicitId: Long? = null): Long = withContext(Dispatchers.IO) {
589+
logger.d { "Inserting generation: $name" + (explicitId?.let { " with explicit ID $it" } ?: "") }
590+
val existing = database.generationQueriesQueries.selectByName(name).executeAsOneOrNull()
591+
if (existing != null) return@withContext existing.id
592+
593+
if (explicitId != null) {
594+
database.generationQueriesQueries.insertWithId(explicitId, name)
595+
return@withContext explicitId
596+
}
597+
598+
database.generationQueriesQueries.insert(name)
599+
val id = database.generationQueriesQueries.lastInsertRowId().executeAsOne()
600+
if (id != 0L) return@withContext id
601+
602+
// fallback
603+
val retry = database.generationQueriesQueries.selectByName(name).executeAsOneOrNull()
604+
return@withContext retry?.id ?: 0L
605+
}
606+
607+
/**
608+
* Resolve a generation name to its ID, or null if not found.
609+
*/
610+
suspend fun getGenerationIdByName(name: String): Long? = withContext(Dispatchers.IO) {
611+
database.generationQueriesQueries.selectIdByName(name).executeAsOneOrNull()
612+
}
613+
614+
/**
615+
* Return all generations.
616+
*/
617+
suspend fun getAllGenerations(): List<Generation> = withContext(Dispatchers.IO) {
618+
database.generationQueriesQueries.selectAll().executeAsList().map { it.toModel() }
619+
}
620+
621+
/**
622+
* Update the generation for a given author ID.
623+
*/
624+
suspend fun updateAuthorGeneration(authorId: Long, generationId: Long?) = withContext(Dispatchers.IO) {
625+
database.authorQueriesQueries.updateGeneration(generationId, authorId)
626+
}
627+
628+
// ── Author insertion ─────────────────────────────────────────────
629+
580630
// Insert an author and return its ID
581-
suspend fun insertAuthor(name: String): Long = withContext(Dispatchers.IO) {
631+
suspend fun insertAuthor(name: String, generationId: Long? = null): Long = withContext(Dispatchers.IO) {
582632
logger.d{"Inserting author: $name"}
583633

584634
// Check if author already exists
585635
val existingAuthor = database.authorQueriesQueries.selectByName(name).executeAsOneOrNull()
586636
if (existingAuthor != null) {
637+
// If a generationId is supplied and the existing author doesn't have one, update it
638+
if (generationId != null && existingAuthor.generationId == null) {
639+
database.authorQueriesQueries.updateGeneration(generationId, existingAuthor.id)
640+
}
587641
logger.d{"Author already exists with ID: ${existingAuthor.id}"}
588642
return@withContext existingAuthor.id
589643
}
590644

591-
// Insert the author
592-
database.authorQueriesQueries.insert(name)
645+
// Insert the author (with optional generationId)
646+
if (generationId != null) {
647+
database.authorQueriesQueries.insertWithGeneration(name, generationId)
648+
} else {
649+
database.authorQueriesQueries.insert(name)
650+
}
593651

594652
// Get the ID of the inserted author
595653
val authorId = database.authorQueriesQueries.lastInsertRowId().executeAsOne()

dao/src/commonMain/sqldelight/io/github/kdroidfilter/seforimlibrary/db/AuthorQueries.sq

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,41 @@ INSERT INTO author (name)
2020
VALUES (?)
2121
ON CONFLICT (name) DO NOTHING;
2222

23+
insertWithGeneration:
24+
INSERT INTO author (name, generationId)
25+
VALUES (?, ?)
26+
ON CONFLICT (name) DO UPDATE SET generationId = COALESCE(excluded.generationId, author.generationId);
27+
2328
insertAndGetId:
2429
INSERT OR IGNORE INTO author (name)
2530
VALUES (?);
2631

2732
selectIdByName:
2833
SELECT id FROM author WHERE name = ? LIMIT 1;
2934

35+
updateGeneration:
36+
UPDATE author SET generationId = ? WHERE id = ?;
37+
38+
updateGenerationByName:
39+
UPDATE author SET generationId = ? WHERE name = ?;
40+
41+
selectByGeneration:
42+
SELECT a.* FROM author a WHERE a.generationId = ? ORDER BY a.name;
43+
44+
selectWithGeneration:
45+
SELECT a.id, a.name, a.generationId, g.name AS generationName
46+
FROM author a
47+
LEFT JOIN generation g ON a.generationId = g.id
48+
ORDER BY a.name;
49+
50+
selectWithGenerationByBookId:
51+
SELECT a.id, a.name, a.generationId, g.name AS generationName
52+
FROM author a
53+
JOIN book_author ba ON a.id = ba.authorId
54+
LEFT JOIN generation g ON a.generationId = g.id
55+
WHERE ba.bookId = ?
56+
ORDER BY a.name;
57+
3058
delete:
3159
DELETE FROM author WHERE id = ?;
3260

dao/src/commonMain/sqldelight/io/github/kdroidfilter/seforimlibrary/db/Database.sq

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,24 @@ CREATE TABLE IF NOT EXISTS category_closure (
2323
CREATE INDEX IF NOT EXISTS idx_category_closure_ancestor ON category_closure(ancestorId);
2424
CREATE INDEX IF NOT EXISTS idx_category_closure_descendant ON category_closure(descendantId);
2525

26+
-- Generations table (era / period code table)
27+
CREATE TABLE IF NOT EXISTS generation (
28+
id INTEGER PRIMARY KEY AUTOINCREMENT,
29+
name TEXT NOT NULL UNIQUE
30+
);
31+
32+
CREATE INDEX IF NOT EXISTS idx_generation_name ON generation(name);
33+
2634
-- Authors table
2735
CREATE TABLE IF NOT EXISTS author (
2836
id INTEGER PRIMARY KEY AUTOINCREMENT,
29-
name TEXT NOT NULL UNIQUE
37+
name TEXT NOT NULL UNIQUE,
38+
generationId INTEGER,
39+
FOREIGN KEY (generationId) REFERENCES generation(id) ON DELETE SET NULL
3040
);
3141

3242
CREATE INDEX IF NOT EXISTS idx_author_name ON author(name);
43+
CREATE INDEX IF NOT EXISTS idx_author_generation ON author(generationId);
3344

3445
-- Table des topics
3546
CREATE TABLE IF NOT EXISTS topic (
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
-- Queries for generations (era / period)
2+
3+
selectAll:
4+
SELECT * FROM generation ORDER BY id;
5+
6+
selectById:
7+
SELECT * FROM generation WHERE id = ?;
8+
9+
selectByName:
10+
SELECT * FROM generation WHERE name = ? LIMIT 1;
11+
12+
insert:
13+
INSERT INTO generation (name)
14+
VALUES (?)
15+
ON CONFLICT (name) DO NOTHING;
16+
17+
insertWithId:
18+
INSERT OR IGNORE INTO generation (id, name)
19+
VALUES (?, ?);
20+
21+
insertAndGetId:
22+
INSERT OR IGNORE INTO generation (name)
23+
VALUES (?);
24+
25+
selectIdByName:
26+
SELECT id FROM generation WHERE name = ? LIMIT 1;
27+
28+
delete:
29+
DELETE FROM generation WHERE id = ?;
30+
31+
countAll:
32+
SELECT COUNT(*) FROM generation;
33+
34+
lastInsertRowId:
35+
SELECT last_insert_rowid();

generator/externalbooks/build.gradle.kts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,25 @@ kotlin {
2626
}
2727
}
2828

29+
// Download books.db from Google Drive if not already present
30+
tasks.register<JavaExec>("downloadBooksDb") {
31+
group = "application"
32+
description = "Download books.db from Google Drive if it doesn't exist locally."
33+
34+
dependsOn("jvmJar")
35+
mainClass.set("io.github.kdroidfilter.seforimlibrary.externalbooks.BooksDbFetcherKt")
36+
classpath = files(tasks.named("jvmJar")) + configurations.getByName("jvmRuntimeClasspath")
37+
38+
val defaultBooksDb = layout.buildDirectory.file("books.db").get().asFile.absolutePath
39+
val booksDb = if (project.hasProperty("booksDb")) {
40+
project.property("booksDb") as String
41+
} else {
42+
defaultBooksDb
43+
}
44+
45+
args(booksDb)
46+
}
47+
2948
// Import external book metadata (HebrewBooks + OtzarHaChochma) into seforim.db
3049
tasks.register<JavaExec>("importExternalBooks") {
3150
group = "application"

0 commit comments

Comments
 (0)