Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
d168f07563
|
@@ -16,14 +16,15 @@ jobs:
|
||||
uses: https://github.com/cachix/install-nix-action@v31
|
||||
with:
|
||||
extra_nix_config: |
|
||||
experimental-features = nix-command flakes
|
||||
show-trace = true
|
||||
max-jobs = auto
|
||||
trusted-users = root
|
||||
experimental-features = nix-command flakes
|
||||
build-users-group =
|
||||
|
||||
# - name: Update database inputs
|
||||
# run: nix flake update datasources
|
||||
- name: Update database inputs
|
||||
run: nix flake update datasources
|
||||
|
||||
- name: Build database
|
||||
run: nix build .#database -L
|
||||
|
||||
Generated
+7
-24
@@ -7,11 +7,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1780302182,
|
||||
"narHash": "sha256-IfC+dpdjjlkzrWlm+p851T43GsR04wMAPqGn63jisJ4=",
|
||||
"lastModified": 1776081209,
|
||||
"narHash": "sha256-zR1115tcOPnYLk6NznSf7YslyaJLc/MGayEHShitx18=",
|
||||
"ref": "refs/heads/main",
|
||||
"rev": "c116674dd1e0b879660e6237e54904aa825d4511",
|
||||
"revCount": 29,
|
||||
"rev": "7fe3552bb16e1d315c0b27b243e5eb53cd9e86fc",
|
||||
"revCount": 13,
|
||||
"type": "git",
|
||||
"url": "https://git.pvv.ntnu.no/Mugiten/datasources.git"
|
||||
},
|
||||
@@ -20,29 +20,13 @@
|
||||
"url": "https://git.pvv.ntnu.no/Mugiten/datasources.git"
|
||||
}
|
||||
},
|
||||
"kanjivg-src": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1775218066,
|
||||
"narHash": "sha256-iYv9xakgoGt/JwwdKDUCpSAF36hBtKlX9oN7xiLowjs=",
|
||||
"ref": "refs/heads/master",
|
||||
"rev": "544d319f79348c092d567b662f27f33dacfa60cd",
|
||||
"revCount": 2215,
|
||||
"type": "git",
|
||||
"url": "https://git.pvv.ntnu.no/mugiten/kanjivg.git"
|
||||
},
|
||||
"original": {
|
||||
"type": "git",
|
||||
"url": "https://git.pvv.ntnu.no/mugiten/kanjivg.git"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1779560665,
|
||||
"narHash": "sha256-tpyBcxPpcQb8ukyNF7DoCwfSY3VPsxHoYwj00Cayv5o=",
|
||||
"lastModified": 1775423009,
|
||||
"narHash": "sha256-vPKLpjhIVWdDrfiUM8atW6YkIggCEKdSAlJPzzhkQlw=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "64c08a7ca051951c8eae34e3e3cb1e202fe36786",
|
||||
"rev": "68d8aa3d661f0e6bd5862291b5bb263b2a6595c9",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -54,7 +38,6 @@
|
||||
"root": {
|
||||
"inputs": {
|
||||
"datasources": "datasources",
|
||||
"kanjivg-src": "kanjivg-src",
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,18 +8,12 @@
|
||||
url = "git+https://git.pvv.ntnu.no/Mugiten/datasources.git";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
|
||||
kanjivg-src = {
|
||||
url = "git+https://git.pvv.ntnu.no/mugiten/kanjivg.git";
|
||||
flake = false;
|
||||
};
|
||||
};
|
||||
|
||||
outputs = {
|
||||
self,
|
||||
nixpkgs,
|
||||
datasources,
|
||||
kanjivg-src,
|
||||
}: let
|
||||
inherit (nixpkgs) lib;
|
||||
systems = [
|
||||
|
||||
@@ -45,7 +45,9 @@ class KanjiElement extends Element {
|
||||
});
|
||||
|
||||
@override
|
||||
Map<String, Object?> get sqlValue => {...super.sqlValue};
|
||||
Map<String, Object?> get sqlValue => {
|
||||
...super.sqlValue,
|
||||
};
|
||||
}
|
||||
|
||||
class ReadingElement extends Element {
|
||||
@@ -127,19 +129,6 @@ class XRefParts {
|
||||
'readingRef': readingRef,
|
||||
'senseOrderNum': senseOrderNum,
|
||||
};
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
|
||||
return other is XRefParts &&
|
||||
other.kanjiRef == kanjiRef &&
|
||||
other.readingRef == readingRef &&
|
||||
other.senseOrderNum == senseOrderNum;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(kanjiRef, readingRef, senseOrderNum);
|
||||
}
|
||||
|
||||
class XRef {
|
||||
@@ -179,7 +168,9 @@ class Sense extends SQLWritable {
|
||||
});
|
||||
|
||||
@override
|
||||
Map<String, Object?> get sqlValue => {'senseId': senseId};
|
||||
Map<String, Object?> get sqlValue => {
|
||||
'senseId': senseId,
|
||||
};
|
||||
|
||||
bool get isEmpty =>
|
||||
antonyms.isEmpty &&
|
||||
|
||||
@@ -15,12 +15,6 @@ class ResolvedXref {
|
||||
const ResolvedXref(this.entry, this.ambiguous);
|
||||
}
|
||||
|
||||
// A constant map of xref parts to jmdict id for unresolvable xrefs.
|
||||
final xrefExceptions = {
|
||||
// NOTE: see https://www.edrdg.org/jmwsgi/entr.py?svc=jmdict&g=2870981.1~2369718 for details
|
||||
XRefParts(kanjiRef: 'プレストレスト', readingRef: 'コンクリート'): 2472380,
|
||||
};
|
||||
|
||||
/// Resolves an xref (pair of kanji, optionally reading, and optionally sense number) to an a specific
|
||||
/// JMdict entry, if possible.
|
||||
///
|
||||
@@ -33,65 +27,15 @@ ResolvedXref resolveXref(
|
||||
SplayTreeMap<String, Set<Entry>> entriesByReading,
|
||||
XRefParts xref,
|
||||
) {
|
||||
late List<Entry> candidateEntries;
|
||||
|
||||
if (xrefExceptions.containsKey(xref)) {
|
||||
final exceptionEntryId = xrefExceptions[xref]!;
|
||||
// NOTE: this is slow, but we have few exceptions. Let's wait for JMdict XML-NG to be released so we can delete this :)
|
||||
final exceptionEntry =
|
||||
entriesByKanji.values
|
||||
.expand((set) => set)
|
||||
.firstWhereOrNull((entry) => entry.entryId == exceptionEntryId) ??
|
||||
entriesByReading.values
|
||||
.expand((set) => set)
|
||||
.firstWhereOrNull((entry) => entry.entryId == exceptionEntryId);
|
||||
|
||||
if (exceptionEntry != null) {
|
||||
return ResolvedXref(exceptionEntry, false);
|
||||
} else {
|
||||
throw Exception(
|
||||
'Xref $xref matches an exception entry ID $exceptionEntryId, but that entry was not found among the candidates.',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
switch ((xref.kanjiRef, xref.readingRef)) {
|
||||
case (null, null):
|
||||
throw Exception('Xref $xref has no kanji or reading reference');
|
||||
|
||||
case (final String k, null):
|
||||
if (!entriesByKanji.containsKey(k)) {
|
||||
throw Exception(
|
||||
'Xref $xref has kanji reference "$k" but no entries found with that kanji',
|
||||
);
|
||||
}
|
||||
candidateEntries = entriesByKanji[k]!.toList();
|
||||
break;
|
||||
|
||||
case (null, final String r):
|
||||
if (!entriesByReading.containsKey(r)) {
|
||||
throw Exception(
|
||||
'Xref $xref has reading reference "$r" but no entries found with that reading',
|
||||
);
|
||||
}
|
||||
candidateEntries = entriesByReading[r]!.toList();
|
||||
break;
|
||||
|
||||
case (final String k, final String r):
|
||||
if (!entriesByKanji.containsKey(k)) {
|
||||
throw Exception(
|
||||
'Xref $xref has kanji reference "$k" but no entries found with that kanji',
|
||||
);
|
||||
}
|
||||
if (!entriesByReading.containsKey(r)) {
|
||||
throw Exception(
|
||||
'Xref $xref has reading reference "$r" but no entries found with that reading',
|
||||
);
|
||||
}
|
||||
candidateEntries = entriesByKanji[k]!
|
||||
.intersection(entriesByReading[r]!)
|
||||
.toList();
|
||||
}
|
||||
List<Entry> candidateEntries = switch ((xref.kanjiRef, xref.readingRef)) {
|
||||
(null, null) => throw Exception(
|
||||
'Xref $xref has no kanji or reading reference',
|
||||
),
|
||||
(final String k, null) => entriesByKanji[k]!.toList(),
|
||||
(null, final String r) => entriesByReading[r]!.toList(),
|
||||
(final String k, final String r) =>
|
||||
entriesByKanji[k]!.intersection(entriesByReading[r]!).toList(),
|
||||
};
|
||||
|
||||
// Filter out entries that don't have the number of senses specified in the xref
|
||||
if (xref.senseOrderNum != null) {
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
import 'package:jadb/_data_ingestion/sql_writable.dart';
|
||||
|
||||
/// Enum set in the kvg:position attribute, used by `<g>` elements in the KanjiVG SVG files.
|
||||
enum KanjiPathGroupPosition {
|
||||
bottom,
|
||||
kamae,
|
||||
kamaec,
|
||||
left,
|
||||
middle,
|
||||
nyo,
|
||||
nyoc,
|
||||
right,
|
||||
tare,
|
||||
tarec,
|
||||
top,
|
||||
}
|
||||
|
||||
/// Contents of a \<g> element in the KanjiVG SVG files.
|
||||
class KanjiPathGroupTreeNode extends SQLWritable {
|
||||
final String id;
|
||||
final List<KanjiPathGroupTreeNode> children;
|
||||
final String? element;
|
||||
final String? original;
|
||||
final KanjiPathGroupPosition? position;
|
||||
final String? radical;
|
||||
final int? part;
|
||||
|
||||
KanjiPathGroupTreeNode({
|
||||
required this.id,
|
||||
this.children = const [],
|
||||
this.element,
|
||||
this.original,
|
||||
this.position,
|
||||
this.radical,
|
||||
this.part,
|
||||
});
|
||||
|
||||
@override
|
||||
Map<String, Object?> get sqlValue => {
|
||||
'id': id,
|
||||
'element': element,
|
||||
'original': original,
|
||||
'position': position?.name,
|
||||
'radical': radical,
|
||||
'part': part,
|
||||
};
|
||||
}
|
||||
|
||||
/// Contents of a `<text>` element in the StrokeNumber's group in the KanjiVG SVG files
|
||||
class KanjiStrokeNumber extends SQLWritable {
|
||||
final int num;
|
||||
final double x;
|
||||
final double y;
|
||||
|
||||
KanjiStrokeNumber(this.num, this.x, this.y);
|
||||
|
||||
@override
|
||||
Map<String, Object?> get sqlValue => {'num': num, 'x': x, 'y': y};
|
||||
}
|
||||
|
||||
/// Contents of a `<path>` element in the KanjiVG SVG files
|
||||
class KanjiVGPath extends SQLWritable {
|
||||
final String id;
|
||||
final String type;
|
||||
final String svgPath;
|
||||
|
||||
KanjiVGPath({required this.id, required this.type, required this.svgPath});
|
||||
|
||||
@override
|
||||
Map<String, Object?> get sqlValue => {
|
||||
'id': id,
|
||||
'type': type,
|
||||
'svgPath': svgPath,
|
||||
};
|
||||
}
|
||||
|
||||
class KanjiVGItem extends SQLWritable {
|
||||
final String character;
|
||||
final List<KanjiVGPath> paths;
|
||||
final List<KanjiStrokeNumber> strokeNumbers;
|
||||
final List<KanjiPathGroupTreeNode> pathGroups;
|
||||
|
||||
KanjiVGItem({
|
||||
required this.character,
|
||||
required this.paths,
|
||||
required this.strokeNumbers,
|
||||
required this.pathGroups,
|
||||
});
|
||||
|
||||
@override
|
||||
Map<String, Object?> get sqlValue => {'character': character};
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
import 'package:sqflite_common/sqflite.dart';
|
||||
|
||||
Future<void> seedKanjiVGData(Iterable<String> xmlContents, Database db) async {
|
||||
final b = db.batch();
|
||||
|
||||
await b.commit(noResult: true);
|
||||
}
|
||||
+204
-297
@@ -1,310 +1,217 @@
|
||||
import 'package:collection/collection.dart';
|
||||
|
||||
class RadkfileRadical {
|
||||
/// The formal variant of the radical
|
||||
///
|
||||
/// This is the one you want to use for searching.
|
||||
final String formalVariant;
|
||||
|
||||
/// The informal variant of the radical (if it differs from the formal one).
|
||||
///
|
||||
/// This is the one you should display to the user.
|
||||
final String? informalVariant;
|
||||
|
||||
/// The number of strokes in this radical.
|
||||
final int strokeCount;
|
||||
|
||||
const RadkfileRadical({
|
||||
required this.formalVariant,
|
||||
this.informalVariant,
|
||||
required this.strokeCount,
|
||||
});
|
||||
}
|
||||
|
||||
const Map<int, List<RadkfileRadical>> radicals = {
|
||||
1: [
|
||||
RadkfileRadical(formalVariant: '一', strokeCount: 1),
|
||||
RadkfileRadical(formalVariant: '|', strokeCount: 1),
|
||||
RadkfileRadical(formalVariant: '丶', strokeCount: 1),
|
||||
RadkfileRadical(formalVariant: 'ノ', strokeCount: 1),
|
||||
RadkfileRadical(formalVariant: '乙', strokeCount: 1),
|
||||
RadkfileRadical(formalVariant: '亅', strokeCount: 1),
|
||||
],
|
||||
const Map<int, List<String>> radicals = {
|
||||
1: ['一', '|', '丶', 'ノ', '乙', '亅'],
|
||||
2: [
|
||||
RadkfileRadical(formalVariant: '二', strokeCount: 2),
|
||||
RadkfileRadical(formalVariant: '亠', strokeCount: 2),
|
||||
RadkfileRadical(formalVariant: '人', strokeCount: 2),
|
||||
RadkfileRadical(formalVariant: '化', informalVariant: '⺅', strokeCount: 2),
|
||||
RadkfileRadical(formalVariant: '个', informalVariant: '𠆢', strokeCount: 2),
|
||||
RadkfileRadical(formalVariant: '儿', strokeCount: 2),
|
||||
RadkfileRadical(formalVariant: '入', strokeCount: 2),
|
||||
RadkfileRadical(formalVariant: 'ハ', strokeCount: 2),
|
||||
RadkfileRadical(formalVariant: '并', strokeCount: 2),
|
||||
RadkfileRadical(formalVariant: '冂', strokeCount: 2),
|
||||
RadkfileRadical(formalVariant: '冖', strokeCount: 2),
|
||||
RadkfileRadical(formalVariant: '冫', strokeCount: 2),
|
||||
RadkfileRadical(formalVariant: '几', strokeCount: 2),
|
||||
RadkfileRadical(formalVariant: '凵', strokeCount: 2),
|
||||
RadkfileRadical(formalVariant: '刀', strokeCount: 2),
|
||||
RadkfileRadical(formalVariant: '刈', strokeCount: 2),
|
||||
RadkfileRadical(formalVariant: '力', strokeCount: 2),
|
||||
RadkfileRadical(formalVariant: '勹', strokeCount: 2),
|
||||
RadkfileRadical(formalVariant: '匕', strokeCount: 2),
|
||||
RadkfileRadical(formalVariant: '匚', strokeCount: 2),
|
||||
RadkfileRadical(formalVariant: '十', strokeCount: 2),
|
||||
RadkfileRadical(formalVariant: '卜', strokeCount: 2),
|
||||
RadkfileRadical(formalVariant: '卩', strokeCount: 2),
|
||||
RadkfileRadical(formalVariant: '厂', strokeCount: 2),
|
||||
RadkfileRadical(formalVariant: '厶', strokeCount: 2),
|
||||
RadkfileRadical(formalVariant: '又', strokeCount: 2),
|
||||
RadkfileRadical(formalVariant: 'マ', strokeCount: 2),
|
||||
RadkfileRadical(formalVariant: '九', strokeCount: 2),
|
||||
RadkfileRadical(formalVariant: 'ユ', strokeCount: 2),
|
||||
RadkfileRadical(formalVariant: '乃', strokeCount: 2),
|
||||
RadkfileRadical(formalVariant: '乞', strokeCount: 2),
|
||||
'二',
|
||||
'亠',
|
||||
'人',
|
||||
'⺅',
|
||||
'𠆢',
|
||||
'儿',
|
||||
'入',
|
||||
'ハ',
|
||||
'丷',
|
||||
'冂',
|
||||
'冖',
|
||||
'冫',
|
||||
'几',
|
||||
'凵',
|
||||
'刀',
|
||||
'⺉',
|
||||
'力',
|
||||
'勹',
|
||||
'匕',
|
||||
'匚',
|
||||
'十',
|
||||
'卜',
|
||||
'卩',
|
||||
'厂',
|
||||
'厶',
|
||||
'又',
|
||||
'マ',
|
||||
'九',
|
||||
'ユ',
|
||||
'乃',
|
||||
'𠂉',
|
||||
],
|
||||
3: [
|
||||
RadkfileRadical(formalVariant: '込', informalVariant: '⻌', strokeCount: 3),
|
||||
RadkfileRadical(formalVariant: '口', strokeCount: 3),
|
||||
RadkfileRadical(formalVariant: '囗', strokeCount: 3),
|
||||
RadkfileRadical(formalVariant: '土', strokeCount: 3),
|
||||
RadkfileRadical(formalVariant: '士', strokeCount: 3),
|
||||
RadkfileRadical(formalVariant: '夂', strokeCount: 3),
|
||||
RadkfileRadical(formalVariant: '夕', strokeCount: 3),
|
||||
RadkfileRadical(formalVariant: '大', strokeCount: 3),
|
||||
RadkfileRadical(formalVariant: '女', strokeCount: 3),
|
||||
RadkfileRadical(formalVariant: '子', strokeCount: 3),
|
||||
RadkfileRadical(formalVariant: '宀', strokeCount: 3),
|
||||
RadkfileRadical(formalVariant: '寸', strokeCount: 3),
|
||||
RadkfileRadical(formalVariant: '小', strokeCount: 3),
|
||||
RadkfileRadical(formalVariant: '尚', informalVariant: '⺌', strokeCount: 3),
|
||||
RadkfileRadical(formalVariant: '尢', strokeCount: 3),
|
||||
RadkfileRadical(formalVariant: '尸', strokeCount: 3),
|
||||
RadkfileRadical(formalVariant: '屮', strokeCount: 3),
|
||||
RadkfileRadical(formalVariant: '山', strokeCount: 3),
|
||||
RadkfileRadical(formalVariant: '川', strokeCount: 3),
|
||||
RadkfileRadical(formalVariant: '巛', strokeCount: 3),
|
||||
RadkfileRadical(formalVariant: '工', strokeCount: 3),
|
||||
RadkfileRadical(formalVariant: '已', strokeCount: 3),
|
||||
RadkfileRadical(formalVariant: '巾', strokeCount: 3),
|
||||
RadkfileRadical(formalVariant: '干', strokeCount: 3),
|
||||
RadkfileRadical(formalVariant: '幺', strokeCount: 3),
|
||||
RadkfileRadical(formalVariant: '广', strokeCount: 3),
|
||||
RadkfileRadical(formalVariant: '廴', strokeCount: 3),
|
||||
RadkfileRadical(formalVariant: '廾', strokeCount: 3),
|
||||
RadkfileRadical(formalVariant: '弋', strokeCount: 3),
|
||||
RadkfileRadical(formalVariant: '弓', strokeCount: 3),
|
||||
RadkfileRadical(formalVariant: 'ヨ', strokeCount: 3),
|
||||
RadkfileRadical(formalVariant: '彑', strokeCount: 3),
|
||||
RadkfileRadical(formalVariant: '彡', strokeCount: 3),
|
||||
RadkfileRadical(formalVariant: '彳', strokeCount: 3),
|
||||
RadkfileRadical(formalVariant: '忙', informalVariant: '⺖', strokeCount: 3),
|
||||
RadkfileRadical(formalVariant: '扎', informalVariant: '⺘', strokeCount: 3),
|
||||
RadkfileRadical(formalVariant: '汁', informalVariant: '⺡', strokeCount: 3),
|
||||
RadkfileRadical(formalVariant: '犯', informalVariant: '⺨', strokeCount: 3),
|
||||
RadkfileRadical(formalVariant: '艾', informalVariant: '⺾', strokeCount: 3),
|
||||
RadkfileRadical(formalVariant: '邦', informalVariant: '⻏', strokeCount: 3),
|
||||
RadkfileRadical(formalVariant: '阡', informalVariant: '⻖', strokeCount: 3),
|
||||
RadkfileRadical(formalVariant: '也', strokeCount: 3),
|
||||
RadkfileRadical(formalVariant: '亡', strokeCount: 3),
|
||||
RadkfileRadical(formalVariant: '及', strokeCount: 3),
|
||||
RadkfileRadical(formalVariant: '久', strokeCount: 3),
|
||||
'⻌',
|
||||
'口',
|
||||
'囗',
|
||||
'土',
|
||||
'士',
|
||||
'夂',
|
||||
'夕',
|
||||
'大',
|
||||
'女',
|
||||
'子',
|
||||
'宀',
|
||||
'寸',
|
||||
'小',
|
||||
'⺌',
|
||||
'尢',
|
||||
'尸',
|
||||
'屮',
|
||||
'山',
|
||||
'川',
|
||||
'巛',
|
||||
'工',
|
||||
'已',
|
||||
'巾',
|
||||
'干',
|
||||
'幺',
|
||||
'广',
|
||||
'廴',
|
||||
'廾',
|
||||
'弋',
|
||||
'弓',
|
||||
'ヨ',
|
||||
'彑',
|
||||
'彡',
|
||||
'彳',
|
||||
'⺖',
|
||||
'⺘',
|
||||
'⺡',
|
||||
'⺨',
|
||||
'⺾',
|
||||
'⻏',
|
||||
'⻖',
|
||||
'也',
|
||||
'亡',
|
||||
'及',
|
||||
'久',
|
||||
],
|
||||
4: [
|
||||
RadkfileRadical(formalVariant: '老', informalVariant: '⺹', strokeCount: 4),
|
||||
RadkfileRadical(formalVariant: '心', strokeCount: 4),
|
||||
RadkfileRadical(formalVariant: '戈', strokeCount: 4),
|
||||
RadkfileRadical(formalVariant: '戸', strokeCount: 4),
|
||||
RadkfileRadical(formalVariant: '手', strokeCount: 4),
|
||||
RadkfileRadical(formalVariant: '支', strokeCount: 4),
|
||||
RadkfileRadical(formalVariant: '攵', strokeCount: 4),
|
||||
RadkfileRadical(formalVariant: '文', strokeCount: 4),
|
||||
RadkfileRadical(formalVariant: '斗', strokeCount: 4),
|
||||
RadkfileRadical(formalVariant: '斤', strokeCount: 4),
|
||||
RadkfileRadical(formalVariant: '方', strokeCount: 4),
|
||||
RadkfileRadical(formalVariant: '无', strokeCount: 4),
|
||||
RadkfileRadical(formalVariant: '日', strokeCount: 4),
|
||||
RadkfileRadical(formalVariant: '曰', strokeCount: 4),
|
||||
RadkfileRadical(formalVariant: '月', strokeCount: 4),
|
||||
RadkfileRadical(formalVariant: '木', strokeCount: 4),
|
||||
RadkfileRadical(formalVariant: '欠', strokeCount: 4),
|
||||
RadkfileRadical(formalVariant: '止', strokeCount: 4),
|
||||
RadkfileRadical(formalVariant: '歹', strokeCount: 4),
|
||||
RadkfileRadical(formalVariant: '殳', strokeCount: 4),
|
||||
RadkfileRadical(formalVariant: '比', strokeCount: 4),
|
||||
RadkfileRadical(formalVariant: '毛', strokeCount: 4),
|
||||
RadkfileRadical(formalVariant: '氏', strokeCount: 4),
|
||||
RadkfileRadical(formalVariant: '气', strokeCount: 4),
|
||||
RadkfileRadical(formalVariant: '水', strokeCount: 4),
|
||||
RadkfileRadical(formalVariant: '火', strokeCount: 4),
|
||||
RadkfileRadical(formalVariant: '杰', strokeCount: 4),
|
||||
RadkfileRadical(formalVariant: '爪', strokeCount: 4),
|
||||
RadkfileRadical(formalVariant: '父', strokeCount: 4),
|
||||
RadkfileRadical(formalVariant: '爻', strokeCount: 4),
|
||||
RadkfileRadical(formalVariant: '爿', strokeCount: 4),
|
||||
RadkfileRadical(formalVariant: '片', strokeCount: 4),
|
||||
RadkfileRadical(formalVariant: '牛', strokeCount: 4),
|
||||
RadkfileRadical(formalVariant: '犬', strokeCount: 4),
|
||||
RadkfileRadical(formalVariant: '礼', strokeCount: 4),
|
||||
RadkfileRadical(formalVariant: '王', strokeCount: 4),
|
||||
RadkfileRadical(formalVariant: '元', strokeCount: 4),
|
||||
RadkfileRadical(formalVariant: '井', strokeCount: 4),
|
||||
RadkfileRadical(formalVariant: '勿', strokeCount: 4),
|
||||
RadkfileRadical(formalVariant: '尤', strokeCount: 4),
|
||||
RadkfileRadical(formalVariant: '五', strokeCount: 4),
|
||||
RadkfileRadical(formalVariant: '屯', strokeCount: 4),
|
||||
RadkfileRadical(formalVariant: '巴', strokeCount: 4),
|
||||
RadkfileRadical(formalVariant: '毋', strokeCount: 4),
|
||||
'⺹',
|
||||
'心',
|
||||
'戈',
|
||||
'戸',
|
||||
'手',
|
||||
'支',
|
||||
'攵',
|
||||
'文',
|
||||
'斗',
|
||||
'斤',
|
||||
'方',
|
||||
'无',
|
||||
'日',
|
||||
'曰',
|
||||
'月',
|
||||
'木',
|
||||
'欠',
|
||||
'止',
|
||||
'歹',
|
||||
'殳',
|
||||
'比',
|
||||
'毛',
|
||||
'氏',
|
||||
'气',
|
||||
'水',
|
||||
'火',
|
||||
'⺣',
|
||||
'爪',
|
||||
'父',
|
||||
'爻',
|
||||
'爿',
|
||||
'片',
|
||||
'牛',
|
||||
'犬',
|
||||
'⺭',
|
||||
'王',
|
||||
'元',
|
||||
'井',
|
||||
'勿',
|
||||
'尤',
|
||||
'五',
|
||||
'屯',
|
||||
'巴',
|
||||
'毋',
|
||||
],
|
||||
5: [
|
||||
RadkfileRadical(formalVariant: '玄', strokeCount: 5),
|
||||
RadkfileRadical(formalVariant: '瓦', strokeCount: 5),
|
||||
RadkfileRadical(formalVariant: '甘', strokeCount: 5),
|
||||
RadkfileRadical(formalVariant: '生', strokeCount: 5),
|
||||
RadkfileRadical(formalVariant: '用', strokeCount: 5),
|
||||
RadkfileRadical(formalVariant: '田', strokeCount: 5),
|
||||
RadkfileRadical(formalVariant: '疋', strokeCount: 5),
|
||||
RadkfileRadical(formalVariant: '疔', strokeCount: 5),
|
||||
RadkfileRadical(formalVariant: '癶', strokeCount: 5),
|
||||
RadkfileRadical(formalVariant: '白', strokeCount: 5),
|
||||
RadkfileRadical(formalVariant: '皮', strokeCount: 5),
|
||||
RadkfileRadical(formalVariant: '皿', strokeCount: 5),
|
||||
RadkfileRadical(formalVariant: '目', strokeCount: 5),
|
||||
RadkfileRadical(formalVariant: '矛', strokeCount: 5),
|
||||
RadkfileRadical(formalVariant: '矢', strokeCount: 5),
|
||||
RadkfileRadical(formalVariant: '石', strokeCount: 5),
|
||||
RadkfileRadical(formalVariant: '示', strokeCount: 5),
|
||||
RadkfileRadical(formalVariant: '禹', strokeCount: 5),
|
||||
RadkfileRadical(formalVariant: '禾', strokeCount: 5),
|
||||
RadkfileRadical(formalVariant: '穴', strokeCount: 5),
|
||||
RadkfileRadical(formalVariant: '立', strokeCount: 5),
|
||||
RadkfileRadical(formalVariant: '初', strokeCount: 5),
|
||||
RadkfileRadical(formalVariant: '世', strokeCount: 5),
|
||||
RadkfileRadical(formalVariant: '巨', strokeCount: 5),
|
||||
RadkfileRadical(formalVariant: '冊', strokeCount: 5),
|
||||
RadkfileRadical(formalVariant: '母', strokeCount: 5),
|
||||
RadkfileRadical(formalVariant: '買', strokeCount: 5),
|
||||
RadkfileRadical(formalVariant: '牙', strokeCount: 5),
|
||||
'玄',
|
||||
'瓦',
|
||||
'甘',
|
||||
'生',
|
||||
'用',
|
||||
'田',
|
||||
'疋',
|
||||
'疒',
|
||||
'癶',
|
||||
'白',
|
||||
'皮',
|
||||
'皿',
|
||||
'目',
|
||||
'矛',
|
||||
'矢',
|
||||
'石',
|
||||
'示',
|
||||
'禸',
|
||||
'禾',
|
||||
'穴',
|
||||
'立',
|
||||
'⻂',
|
||||
'世',
|
||||
'巨',
|
||||
'冊',
|
||||
'母',
|
||||
'⺲',
|
||||
'牙',
|
||||
],
|
||||
6: [
|
||||
RadkfileRadical(formalVariant: '瓜', strokeCount: 6),
|
||||
RadkfileRadical(formalVariant: '竹', strokeCount: 6),
|
||||
RadkfileRadical(formalVariant: '米', strokeCount: 6),
|
||||
RadkfileRadical(formalVariant: '糸', strokeCount: 6),
|
||||
RadkfileRadical(formalVariant: '缶', strokeCount: 6),
|
||||
RadkfileRadical(formalVariant: '羊', strokeCount: 6),
|
||||
RadkfileRadical(formalVariant: '羽', strokeCount: 6),
|
||||
RadkfileRadical(formalVariant: '而', strokeCount: 6),
|
||||
RadkfileRadical(formalVariant: '耒', strokeCount: 6),
|
||||
RadkfileRadical(formalVariant: '耳', strokeCount: 6),
|
||||
RadkfileRadical(formalVariant: '聿', strokeCount: 6),
|
||||
RadkfileRadical(formalVariant: '肉', strokeCount: 6),
|
||||
RadkfileRadical(formalVariant: '自', strokeCount: 6),
|
||||
RadkfileRadical(formalVariant: '至', strokeCount: 6),
|
||||
RadkfileRadical(formalVariant: '臼', strokeCount: 6),
|
||||
RadkfileRadical(formalVariant: '舌', strokeCount: 6),
|
||||
RadkfileRadical(formalVariant: '舟', strokeCount: 6),
|
||||
RadkfileRadical(formalVariant: '艮', strokeCount: 6),
|
||||
RadkfileRadical(formalVariant: '色', strokeCount: 6),
|
||||
RadkfileRadical(formalVariant: '虍', strokeCount: 6),
|
||||
RadkfileRadical(formalVariant: '虫', strokeCount: 6),
|
||||
RadkfileRadical(formalVariant: '血', strokeCount: 6),
|
||||
RadkfileRadical(formalVariant: '行', strokeCount: 6),
|
||||
RadkfileRadical(formalVariant: '衣', strokeCount: 6),
|
||||
RadkfileRadical(formalVariant: '西', strokeCount: 6),
|
||||
'瓜',
|
||||
'竹',
|
||||
'米',
|
||||
'糸',
|
||||
'缶',
|
||||
'羊',
|
||||
'羽',
|
||||
'而',
|
||||
'耒',
|
||||
'耳',
|
||||
'聿',
|
||||
'肉',
|
||||
'自',
|
||||
'至',
|
||||
'臼',
|
||||
'舌',
|
||||
'舟',
|
||||
'艮',
|
||||
'色',
|
||||
'虍',
|
||||
'虫',
|
||||
'血',
|
||||
'行',
|
||||
'衣',
|
||||
'西',
|
||||
],
|
||||
7: [
|
||||
RadkfileRadical(formalVariant: '臣', strokeCount: 7),
|
||||
RadkfileRadical(formalVariant: '見', strokeCount: 7),
|
||||
RadkfileRadical(formalVariant: '角', strokeCount: 7),
|
||||
RadkfileRadical(formalVariant: '言', strokeCount: 7),
|
||||
RadkfileRadical(formalVariant: '谷', strokeCount: 7),
|
||||
RadkfileRadical(formalVariant: '豆', strokeCount: 7),
|
||||
RadkfileRadical(formalVariant: '豕', strokeCount: 7),
|
||||
RadkfileRadical(formalVariant: '豸', strokeCount: 7),
|
||||
RadkfileRadical(formalVariant: '貝', strokeCount: 7),
|
||||
RadkfileRadical(formalVariant: '赤', strokeCount: 7),
|
||||
RadkfileRadical(formalVariant: '走', strokeCount: 7),
|
||||
RadkfileRadical(formalVariant: '足', strokeCount: 7),
|
||||
RadkfileRadical(formalVariant: '身', strokeCount: 7),
|
||||
RadkfileRadical(formalVariant: '車', strokeCount: 7),
|
||||
RadkfileRadical(formalVariant: '辛', strokeCount: 7),
|
||||
RadkfileRadical(formalVariant: '辰', strokeCount: 7),
|
||||
RadkfileRadical(formalVariant: '酉', strokeCount: 7),
|
||||
RadkfileRadical(formalVariant: '釆', strokeCount: 7),
|
||||
RadkfileRadical(formalVariant: '里', strokeCount: 7),
|
||||
RadkfileRadical(formalVariant: '舛', strokeCount: 7),
|
||||
RadkfileRadical(formalVariant: '麦', strokeCount: 7),
|
||||
'臣',
|
||||
'見',
|
||||
'角',
|
||||
'言',
|
||||
'谷',
|
||||
'豆',
|
||||
'豕',
|
||||
'豸',
|
||||
'貝',
|
||||
'赤',
|
||||
'走',
|
||||
'足',
|
||||
'身',
|
||||
'車',
|
||||
'辛',
|
||||
'辰',
|
||||
'酉',
|
||||
'釆',
|
||||
'里',
|
||||
'舛',
|
||||
'麦',
|
||||
],
|
||||
8: [
|
||||
RadkfileRadical(formalVariant: '金', strokeCount: 8),
|
||||
RadkfileRadical(formalVariant: '長', strokeCount: 8),
|
||||
RadkfileRadical(formalVariant: '門', strokeCount: 8),
|
||||
RadkfileRadical(formalVariant: '隶', strokeCount: 8),
|
||||
RadkfileRadical(formalVariant: '隹', strokeCount: 8),
|
||||
RadkfileRadical(formalVariant: '雨', strokeCount: 8),
|
||||
RadkfileRadical(formalVariant: '青', strokeCount: 8),
|
||||
RadkfileRadical(formalVariant: '非', strokeCount: 8),
|
||||
RadkfileRadical(formalVariant: '奄', strokeCount: 8),
|
||||
RadkfileRadical(formalVariant: '岡', strokeCount: 8),
|
||||
RadkfileRadical(formalVariant: '免', strokeCount: 8),
|
||||
RadkfileRadical(formalVariant: '斉', strokeCount: 8),
|
||||
],
|
||||
9: [
|
||||
RadkfileRadical(formalVariant: '面', strokeCount: 9),
|
||||
RadkfileRadical(formalVariant: '革', strokeCount: 9),
|
||||
RadkfileRadical(formalVariant: '韭', strokeCount: 9),
|
||||
RadkfileRadical(formalVariant: '音', strokeCount: 9),
|
||||
RadkfileRadical(formalVariant: '頁', strokeCount: 9),
|
||||
RadkfileRadical(formalVariant: '風', strokeCount: 9),
|
||||
RadkfileRadical(formalVariant: '飛', strokeCount: 9),
|
||||
RadkfileRadical(formalVariant: '食', strokeCount: 9),
|
||||
RadkfileRadical(formalVariant: '首', strokeCount: 9),
|
||||
RadkfileRadical(formalVariant: '香', strokeCount: 9),
|
||||
RadkfileRadical(formalVariant: '品', strokeCount: 9),
|
||||
],
|
||||
10: [
|
||||
RadkfileRadical(formalVariant: '馬', strokeCount: 10),
|
||||
RadkfileRadical(formalVariant: '骨', strokeCount: 10),
|
||||
RadkfileRadical(formalVariant: '高', strokeCount: 10),
|
||||
RadkfileRadical(formalVariant: '髟', strokeCount: 10),
|
||||
RadkfileRadical(formalVariant: '鬥', strokeCount: 10),
|
||||
RadkfileRadical(formalVariant: '鬯', strokeCount: 10),
|
||||
RadkfileRadical(formalVariant: '鬲', strokeCount: 10),
|
||||
RadkfileRadical(formalVariant: '鬼', strokeCount: 10),
|
||||
RadkfileRadical(formalVariant: '竜', strokeCount: 10),
|
||||
RadkfileRadical(formalVariant: '韋', strokeCount: 10),
|
||||
],
|
||||
11: [
|
||||
RadkfileRadical(formalVariant: '魚', strokeCount: 11),
|
||||
RadkfileRadical(formalVariant: '鳥', strokeCount: 11),
|
||||
RadkfileRadical(formalVariant: '鹵', strokeCount: 11),
|
||||
RadkfileRadical(formalVariant: '鹿', strokeCount: 11),
|
||||
RadkfileRadical(formalVariant: '麻', strokeCount: 11),
|
||||
RadkfileRadical(formalVariant: '亀', strokeCount: 11),
|
||||
RadkfileRadical(formalVariant: '滴', strokeCount: 11),
|
||||
RadkfileRadical(formalVariant: '黄', strokeCount: 11),
|
||||
RadkfileRadical(formalVariant: '黒', strokeCount: 11),
|
||||
],
|
||||
12: [
|
||||
RadkfileRadical(formalVariant: '黍', strokeCount: 12),
|
||||
RadkfileRadical(formalVariant: '黹', strokeCount: 12),
|
||||
RadkfileRadical(formalVariant: '無', strokeCount: 12),
|
||||
RadkfileRadical(formalVariant: '歯', strokeCount: 12),
|
||||
],
|
||||
13: [
|
||||
RadkfileRadical(formalVariant: '黽', strokeCount: 13),
|
||||
RadkfileRadical(formalVariant: '鼎', strokeCount: 13),
|
||||
RadkfileRadical(formalVariant: '鼓', strokeCount: 13),
|
||||
RadkfileRadical(formalVariant: '鼠', strokeCount: 13),
|
||||
],
|
||||
14: [
|
||||
RadkfileRadical(formalVariant: '鼻', strokeCount: 14),
|
||||
RadkfileRadical(formalVariant: '齊', strokeCount: 14),
|
||||
],
|
||||
17: [RadkfileRadical(formalVariant: '龠', strokeCount: 17)],
|
||||
};
|
||||
|
||||
final Map<String, RadkfileRadical> radicalsByFormalVariant = {
|
||||
for (final r in radicals.values.flattened) r.formalVariant: r,
|
||||
8: ['金', '長', '門', '隶', '隹', '雨', '青', '非', '奄', '岡', '免', '斉'],
|
||||
9: ['面', '革', '韭', '音', '頁', '風', '飛', '食', '首', '香', '品'],
|
||||
10: ['馬', '骨', '高', '髟', '鬥', '鬯', '鬲', '鬼', '竜', '韋'],
|
||||
11: ['魚', '鳥', '鹵', '鹿', '麻', '亀', '啇', '黄', '黒'],
|
||||
12: ['黍', '黹', '無', '歯'],
|
||||
13: ['黽', '鼎', '鼓', '鼠'],
|
||||
14: ['鼻', '齊'],
|
||||
17: ['龠'],
|
||||
};
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import 'package:jadb/table_names/jmdict.dart';
|
||||
import 'package:jadb/table_names/kanjidic.dart';
|
||||
import 'package:jadb/table_names/kanjivg.dart';
|
||||
import 'package:jadb/table_names/radkfile.dart';
|
||||
import 'package:jadb/table_names/tanos_jlpt.dart';
|
||||
import 'package:sqflite_common/sqlite_api.dart';
|
||||
@@ -22,7 +21,6 @@ Future<void> verifyTablesWithDbConnection(DatabaseExecutor db) async {
|
||||
...KANJIDICTableNames.allTables,
|
||||
...RADKFILETableNames.allTables,
|
||||
...TanosJLPTTableNames.allTables,
|
||||
...KanjiVGTableNames.allTables,
|
||||
};
|
||||
|
||||
final missingTables = expectedTables.difference(tables);
|
||||
|
||||
@@ -33,7 +33,6 @@ class WordSearchMatchSpan {
|
||||
Map<String, Object?> toJson() => {
|
||||
'spanType': spanType.toString().split('.').last,
|
||||
'index': index,
|
||||
'subIndex': subIndex,
|
||||
'start': start,
|
||||
'end': end,
|
||||
};
|
||||
@@ -44,13 +43,12 @@ class WordSearchMatchSpan {
|
||||
(e) => e.toString().split('.').last == json['spanType'],
|
||||
),
|
||||
index: json['index'] as int,
|
||||
subIndex: json['subIndex'] as int? ?? 0,
|
||||
start: json['start'] as int,
|
||||
end: json['end'] as int,
|
||||
);
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(spanType, index, subIndex, start, end);
|
||||
int get hashCode => Object.hash(spanType, index, start, end);
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
@@ -58,7 +56,6 @@ class WordSearchMatchSpan {
|
||||
return other is WordSearchMatchSpan &&
|
||||
other.spanType == spanType &&
|
||||
other.index == index &&
|
||||
other.subIndex == subIndex &&
|
||||
other.start == start &&
|
||||
other.end == end;
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ class WordSearchSense {
|
||||
'dialects': dialects.map((e) => e.toJson()).toList(),
|
||||
'misc': misc.map((e) => e.toJson()).toList(),
|
||||
'info': info,
|
||||
'languageSource': languageSource.map((e) => e.toJson()).toList(),
|
||||
'languageSource': languageSource,
|
||||
};
|
||||
|
||||
factory WordSearchSense.fromJson(Map<String, dynamic> json) =>
|
||||
|
||||
@@ -16,8 +16,6 @@ class WordSearchXrefEntry {
|
||||
final bool ambiguous;
|
||||
|
||||
/// The result of the cross-reference, may or may not be included in the query.
|
||||
///
|
||||
/// Be careful not to introduce circular references when using this field.
|
||||
final WordSearchResult? xrefResult;
|
||||
|
||||
const WordSearchXrefEntry({
|
||||
@@ -42,10 +40,6 @@ class WordSearchXrefEntry {
|
||||
ambiguous: json['ambiguous'] as bool,
|
||||
baseWord: json['baseWord'] as String,
|
||||
furigana: json['furigana'] as String?,
|
||||
xrefResult: json['xrefResult'] != null
|
||||
? WordSearchResult.fromJson(
|
||||
Map<String, dynamic>.from(json['xrefResult'] as Map),
|
||||
)
|
||||
: null,
|
||||
xrefResult: null,
|
||||
);
|
||||
}
|
||||
|
||||
+7
-16
@@ -1,11 +1,9 @@
|
||||
import 'package:jadb/const_data/radicals.dart';
|
||||
import 'package:jadb/models/kanji_search/kanji_search_result.dart';
|
||||
import 'package:jadb/models/verify_tables.dart';
|
||||
import 'package:jadb/models/word_search/word_search_result.dart';
|
||||
import 'package:jadb/search/filter_kanji.dart';
|
||||
import 'package:jadb/search/kanji_search.dart';
|
||||
import 'package:jadb/search/radical_search.dart';
|
||||
import 'package:jadb/search/versions.dart';
|
||||
import 'package:jadb/search/word_search/word_search.dart';
|
||||
import 'package:sqflite_common/sqlite_api.dart';
|
||||
|
||||
@@ -20,13 +18,12 @@ extension JaDBConnection on DatabaseExecutor {
|
||||
searchKanjiWithDbConnection(this, kanji);
|
||||
|
||||
/// Search for a kanji in the database.
|
||||
Future<Map<String, KanjiSearchResult>> jadbGetManyKanji(
|
||||
Iterable<String> kanji,
|
||||
) => searchManyKanjiWithDbConnection(this, kanji);
|
||||
Future<Map<String, KanjiSearchResult>> jadbGetManyKanji(Set<String> kanji) =>
|
||||
searchManyKanjiWithDbConnection(this, kanji);
|
||||
|
||||
/// Filter a list of characters, and return the ones that are listed in the kanji dictionary.
|
||||
Future<List<String>> filterKanji(
|
||||
Iterable<String> kanji, {
|
||||
List<String> kanji, {
|
||||
bool deduplicate = false,
|
||||
}) => filterKanjiWithDbConnection(this, kanji, deduplicate);
|
||||
|
||||
@@ -64,17 +61,11 @@ extension JaDBConnection on DatabaseExecutor {
|
||||
/// of the radicals, find their other radicals, and return those.
|
||||
/// This is used to figure out which remaining combinations of radicals
|
||||
/// the user can search for without getting zero results.
|
||||
Future<List<RadkfileRadical>> jadbSearchRemainingRadicals(
|
||||
List<RadkfileRadical> radicals,
|
||||
) => searchRemainingRadicalsWithDbConnection(this, radicals);
|
||||
Future<List<String>> jadbSearchRemainingRadicals(List<String> radicals) =>
|
||||
searchRemainingRadicalsWithDbConnection(this, radicals);
|
||||
|
||||
/// Given a list of radicals, search which kanji contains all
|
||||
/// of the radicals, and return those.
|
||||
Future<List<String>> jadbSearchKanjiByRadicals(
|
||||
List<RadkfileRadical> radicals,
|
||||
) => searchKanjiByRadicalsWithDbConnection(this, radicals);
|
||||
|
||||
/// Retrieve the version information for all datasources in the database.
|
||||
Future<DatasourceVersions> jadbGetDatasourceVersions() =>
|
||||
getDatasourceVersions(this);
|
||||
Future<List<String>> jadbSearchKanjiByRadicals(List<String> radicals) =>
|
||||
searchKanjiByRadicalsWithDbConnection(this, radicals);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import 'package:sqflite_common/sqflite.dart';
|
||||
/// If [deduplicate] is true, the returned list will deduplicate the input kanji list before returning the filtered results.
|
||||
Future<List<String>> filterKanjiWithDbConnection(
|
||||
DatabaseExecutor connection,
|
||||
Iterable<String> kanji,
|
||||
List<String> kanji,
|
||||
bool deduplicate,
|
||||
) async {
|
||||
final Set<String> filteredKanji = await connection
|
||||
@@ -14,7 +14,7 @@ Future<List<String>> filterKanjiWithDbConnection(
|
||||
SELECT "literal"
|
||||
FROM "${KANJIDICTableNames.character}"
|
||||
WHERE "literal" IN (${kanji.map((_) => '?').join(',')})
|
||||
''', kanji.toList())
|
||||
''', kanji)
|
||||
.then((value) => value.map((e) => e['literal'] as String).toSet());
|
||||
|
||||
if (deduplicate) {
|
||||
|
||||
@@ -274,7 +274,7 @@ Future<KanjiSearchResult?> searchKanjiWithDbConnection(
|
||||
/// Searches for multiple kanji at once, returning a map of kanji to their search results.
|
||||
Future<Map<String, KanjiSearchResult>> searchManyKanjiWithDbConnection(
|
||||
DatabaseExecutor connection,
|
||||
Iterable<String> kanji,
|
||||
Set<String> kanji,
|
||||
) async {
|
||||
if (kanji.isEmpty) {
|
||||
return {};
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import 'package:jadb/const_data/radicals.dart';
|
||||
import 'package:jadb/table_names/radkfile.dart';
|
||||
import 'package:sqflite_common/sqlite_api.dart';
|
||||
|
||||
@@ -8,9 +7,9 @@ import 'package:sqflite_common/sqlite_api.dart';
|
||||
///
|
||||
/// This can be used to limit the choices of additional radicals provided to a user,
|
||||
/// so that any choice they make will still yield at least one kanji.
|
||||
Future<List<RadkfileRadical>> searchRemainingRadicalsWithDbConnection(
|
||||
Future<List<String>> searchRemainingRadicalsWithDbConnection(
|
||||
DatabaseExecutor connection,
|
||||
List<RadkfileRadical> radicals,
|
||||
List<String> radicals,
|
||||
) async {
|
||||
final distinctRadicals = radicals.toSet();
|
||||
|
||||
@@ -26,14 +25,11 @@ Future<List<RadkfileRadical>> searchRemainingRadicalsWithDbConnection(
|
||||
HAVING COUNT(DISTINCT "radical") = ?
|
||||
)
|
||||
''',
|
||||
[...distinctRadicals.map((r) => r.formalVariant), distinctRadicals.length],
|
||||
[...distinctRadicals, distinctRadicals.length],
|
||||
);
|
||||
|
||||
final remainingRadicals = queryResult
|
||||
.map((row) => row['radical'] as String)
|
||||
// TODO: maybe we should do some runtime checking here, just to throw a sensible error
|
||||
// if something goes horribly wrong?
|
||||
.map((r) => radicalsByFormalVariant[r]!)
|
||||
.toList();
|
||||
|
||||
return remainingRadicals;
|
||||
@@ -42,7 +38,7 @@ Future<List<RadkfileRadical>> searchRemainingRadicalsWithDbConnection(
|
||||
/// Returns a list of kanji that contain all of the input radicals.
|
||||
Future<List<String>> searchKanjiByRadicalsWithDbConnection(
|
||||
DatabaseExecutor connection,
|
||||
List<RadkfileRadical> radicals,
|
||||
List<String> radicals,
|
||||
) async {
|
||||
final distinctRadicals = radicals.toSet();
|
||||
|
||||
@@ -54,7 +50,7 @@ Future<List<String>> searchKanjiByRadicalsWithDbConnection(
|
||||
GROUP BY "kanji"
|
||||
HAVING COUNT(DISTINCT "radical") = ?
|
||||
''',
|
||||
[...distinctRadicals.map((r) => r.formalVariant), distinctRadicals.length],
|
||||
[...distinctRadicals, distinctRadicals.length],
|
||||
);
|
||||
|
||||
final kanji = queryResult.map((row) => row['kanji'] as String).toList();
|
||||
|
||||
@@ -1,84 +0,0 @@
|
||||
import 'package:jadb/table_names/jmdict.dart';
|
||||
import 'package:jadb/table_names/kanjidic.dart';
|
||||
import 'package:jadb/table_names/radkfile.dart';
|
||||
import 'package:jadb/table_names/tanos_jlpt.dart';
|
||||
import 'package:sqflite_common/sqlite_api.dart';
|
||||
|
||||
class DatasourceVersions {
|
||||
final String jmdictVersion;
|
||||
final DateTime jmdictDate;
|
||||
final String jmdictHash;
|
||||
|
||||
final String kanjidic2Version;
|
||||
final DateTime kanjidic2Date;
|
||||
final String kanjidic2Hash;
|
||||
|
||||
final String radkfileVersion;
|
||||
final DateTime radkfileDate;
|
||||
final String radkfileHash;
|
||||
|
||||
final String tanosJlptVersion;
|
||||
final DateTime tanosJlptDate;
|
||||
final String tanosJlptHash;
|
||||
|
||||
const DatasourceVersions({
|
||||
required this.jmdictVersion,
|
||||
required this.jmdictDate,
|
||||
required this.jmdictHash,
|
||||
required this.kanjidic2Version,
|
||||
required this.kanjidic2Date,
|
||||
required this.kanjidic2Hash,
|
||||
required this.radkfileVersion,
|
||||
required this.radkfileDate,
|
||||
required this.radkfileHash,
|
||||
required this.tanosJlptVersion,
|
||||
required this.tanosJlptDate,
|
||||
required this.tanosJlptHash,
|
||||
});
|
||||
}
|
||||
|
||||
DateTime _parseDateTime(String dateString) {
|
||||
try {
|
||||
return DateTime.parse(dateString);
|
||||
} catch (e) {
|
||||
if (RegExp(r'^\d{4}-\d{2}$').hasMatch(dateString)) {
|
||||
return DateTime.parse('$dateString-01');
|
||||
} else if (RegExp(r'^\d{4}$').hasMatch(dateString)) {
|
||||
return DateTime.parse('$dateString-01-01');
|
||||
} else {
|
||||
throw FormatException('Invalid date format: $dateString');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<DatasourceVersions> getDatasourceVersions(
|
||||
final DatabaseExecutor connection,
|
||||
) async {
|
||||
final jmdictVersion = await connection
|
||||
.query(JMdictTableNames.version)
|
||||
.then((rows) => rows.first);
|
||||
final kanjidic2Version = await connection
|
||||
.query(KANJIDICTableNames.version)
|
||||
.then((rows) => rows.first);
|
||||
final radkfileVersion = await connection
|
||||
.query(RADKFILETableNames.version)
|
||||
.then((rows) => rows.first);
|
||||
final tanosJlptVersion = await connection
|
||||
.query(TanosJLPTTableNames.version)
|
||||
.then((rows) => rows.first);
|
||||
|
||||
return DatasourceVersions(
|
||||
jmdictVersion: jmdictVersion['version'] as String,
|
||||
jmdictDate: _parseDateTime(jmdictVersion['date'].toString()),
|
||||
jmdictHash: jmdictVersion['hash'] as String,
|
||||
kanjidic2Version: kanjidic2Version['version'] as String,
|
||||
kanjidic2Date: _parseDateTime(kanjidic2Version['date'].toString()),
|
||||
kanjidic2Hash: kanjidic2Version['hash'] as String,
|
||||
radkfileVersion: radkfileVersion['version'] as String,
|
||||
radkfileDate: _parseDateTime(radkfileVersion['date'].toString()),
|
||||
radkfileHash: radkfileVersion['hash'] as String,
|
||||
tanosJlptVersion: tanosJlptVersion['version'] as String,
|
||||
tanosJlptDate: _parseDateTime(tanosJlptVersion['date'].toString()),
|
||||
tanosJlptHash: tanosJlptVersion['hash'] as String,
|
||||
);
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
abstract class KanjiVGTableNames {
|
||||
static const String version = 'KanjiVG_Version';
|
||||
static const String entry = 'KanjiVG_Entry';
|
||||
static const String path = 'KanjiVG_Path';
|
||||
static const String strokeNumber = 'KanjiVG_StrokeNumber';
|
||||
static const String pathGroup = 'KanjiVG_PathGroup';
|
||||
|
||||
static Set<String> get allTables => {version, entry, path, strokeNumber, pathGroup};
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
const int jadbSchemaVersion = 1;
|
||||
@@ -229,7 +229,7 @@ CREATE TABLE "JMdict_SenseGlossary" (
|
||||
PRIMARY KEY ("senseId", "phrase")
|
||||
) WITHOUT ROWID;
|
||||
|
||||
CREATE INDEX "JMdict_SenseGlossary_byPhrase" ON JMdict_SenseGlossary("phrase");
|
||||
-- CREATE INDEX "JMdict_SenseGlossary_byPhrase" ON JMdict_SenseGlossary("phrase");
|
||||
|
||||
CREATE TABLE "JMdict_SenseGlossaryType" (
|
||||
"senseId" INTEGER NOT NULL REFERENCES "JMdict_Sense"("senseId"),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
CREATE TABLE "JMdict_EntryScore" (
|
||||
"elementId" INTEGER PRIMARY KEY,
|
||||
"score" INTEGER NOT NULL,
|
||||
"score" INTEGER NOT NULL DEFAULT 0,
|
||||
"common" BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
|
||||
"entryId" INTEGER NOT NULL GENERATED ALWAYS AS (("elementId" / 100) % 10000000) STORED,
|
||||
@@ -15,7 +15,8 @@ CREATE TABLE "JMdict_EntryScore" (
|
||||
CREATE INDEX "JMdict_EntryScore_byElementId_byScore" ON "JMdict_EntryScore"("elementId", "score");
|
||||
CREATE INDEX "JMdict_EntryScore_byScore" ON "JMdict_EntryScore"("score");
|
||||
|
||||
CREATE INDEX "JMdict_EntryScore_byCommon" ON "JMdict_EntryScore"("common") WHERE "common" = TRUE;
|
||||
CREATE INDEX "JMdict_EntryScore_byElementId_byCommon" ON "JMdict_EntryScore"("elementId", "common");
|
||||
CREATE INDEX "JMdict_EntryScore_byCommon" ON "JMdict_EntryScore"("common");
|
||||
|
||||
-- NOTE: these views are deduplicated in order not to perform an unnecessary
|
||||
-- UNION on every trigger
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
CREATE TABLE "KanjiVG_Version" (
|
||||
"version" VARCHAR(10) PRIMARY KEY NOT NULL,
|
||||
"date" DATE NOT NULL,
|
||||
"hash" VARCHAR(64) NOT NULL
|
||||
) WITHOUT ROWID;
|
||||
|
||||
CREATE TRIGGER "KanjiVG_Version_SingleRow"
|
||||
BEFORE INSERT ON "KanjiVG_Version"
|
||||
WHEN (SELECT COUNT(*) FROM "KanjiVG_Version") >= 1
|
||||
BEGIN
|
||||
SELECT RAISE(FAIL, 'Only one row allowed in KanjiVG_Version');
|
||||
END;
|
||||
|
||||
CREATE TABLE "KanjiVG_Entry" (
|
||||
"character" CHAR(1) PRIMARY KEY NOT NULL
|
||||
) WITHOUT ROWID;
|
||||
|
||||
CREATE TABLE "KanjiVG_StrokeNumber" (
|
||||
"character" CHAR(1) NOT NULL REFERENCES "KanjiVG_Entry"("character"),
|
||||
"strokeNum" INTEGER NOT NULL,
|
||||
"x" REAL NOT NULL,
|
||||
"y" REAL NOT NULL,
|
||||
PRIMARY KEY ("character", "strokeNum")
|
||||
) WITHOUT ROWID;
|
||||
|
||||
CREATE TABLE "KanjiVG_Path" (
|
||||
"character" CHAR(1) NOT NULL REFERENCES "KanjiVG_Entry"("character"),
|
||||
"pathId" TEXT NOT NULL,
|
||||
"type" VARCHAR(10) NOT NULL,
|
||||
"svgPath" TEXT NOT NULL,
|
||||
PRIMARY KEY ("character", "pathId")
|
||||
) WITHOUT ROWID;
|
||||
|
||||
CREATE TABLE "KanjiVG_PathGroup" (
|
||||
"character" CHAR(1) NOT NULL REFERENCES "KanjiVG_Entry"("character"),
|
||||
"groupId" TEXT NOT NULL,
|
||||
"parentGroupId" TEXT REFERENCES "KanjiVG_PathGroup"("groupId"),
|
||||
"element" TEXT,
|
||||
"original" TEXT,
|
||||
"position" VARCHAR(10),
|
||||
"radical" TEXT,
|
||||
"part" INTEGER,
|
||||
PRIMARY KEY ("character", "groupId"),
|
||||
CHECK ("position" IN ('bottom', 'kamae', 'kamaec', 'left', 'middle', 'nyo', 'nyoc', 'right', 'tare', 'tarec', 'top') OR "position" IS NULL)
|
||||
) WITHOUT ROWID;
|
||||
+31
-39
@@ -5,18 +5,18 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: _fe_analyzer_shared
|
||||
sha256: cd6add6f846f35fb79f3c315296703c1a24f3cfd7f4739d91a74961c1c7e9f1b
|
||||
sha256: "8d718c5c58904f9937290fd5dbf2d6a0e02456867706bfb6cd7b81d394e738d5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "100.0.0"
|
||||
version: "98.0.0"
|
||||
analyzer:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: analyzer
|
||||
sha256: "6ba98576948803398b69e3a444df24eacdbe12ed699c7014e120ea38552debbf"
|
||||
sha256: "6141ad5d092d1e1d13929c0504658bbeccc1703505830d7c26e859908f5efc88"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "13.0.0"
|
||||
version: "12.0.0"
|
||||
args:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -61,10 +61,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: code_assets
|
||||
sha256: bf394f466ba9205f1812a0433b392d6af280f155f56651eda7c18cc32ed493b8
|
||||
sha256: "83ccdaa064c980b5596c35dd64a8d3ecc68620174ab9b90b6343b753aa721687"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.1"
|
||||
version: "1.0.0"
|
||||
collection:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -149,10 +149,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: hooks
|
||||
sha256: "62ae9bb76d02526c7c2110a19b6e6ad788fe28d35e553e35efb02a41a46ab43a"
|
||||
sha256: e79ed1e8e1929bc6ecb6ec85f0cb519c887aa5b423705ded0d0f2d9226def388
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
version: "1.0.2"
|
||||
http_multi_server:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -197,10 +197,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
sha256: "31bd099b47c10cd1aeb55146a2d46ce0277630ecef3f7dae54ad7873f36696cd"
|
||||
sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.12.20"
|
||||
version: "0.12.19"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -221,10 +221,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: native_toolchain_c
|
||||
sha256: f59351d28f49520cd3a74eb1f41c5f19ae15e53c65a3231d14af672e46510a96
|
||||
sha256: "6ba77bb18063eebe9de401f5e6437e95e1438af0a87a3a39084fbd37c90df572"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.19.1"
|
||||
version: "0.17.6"
|
||||
node_preamble:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -273,14 +273,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
record_use:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: record_use
|
||||
sha256: "2551bd8eecfe95d14ae75f6021ad0248be5c27f138c2ec12fcb52b500b3ba1ed"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.0"
|
||||
shelf:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -341,26 +333,26 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: sqflite_common
|
||||
sha256: "1581ffbf7a0e333b380d6a30737d78516b826cb35beb7fb0bf8a3ea0c678b465"
|
||||
sha256: "6ef422a4525ecc601db6c0a2233ff448c731307906e92cabc9ba292afaae16a6"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.8"
|
||||
version: "2.5.6"
|
||||
sqflite_common_ffi:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: sqflite_common_ffi
|
||||
sha256: cd0c7f7de39a08f2d54ef144d9058c46eca8461879aaa648025643455c1e5a20
|
||||
sha256: c59fcdc143839a77581f7a7c4de018e53682408903a0a0800b95ef2dc4033eff
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.0+3"
|
||||
version: "2.4.0+2"
|
||||
sqlite3:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: sqlite3
|
||||
sha256: "9488c7d2cdb1091c91cacf7e207cff81b28bff8e366f042bad3afe7d34afe189"
|
||||
sha256: caa693ad15a587a2b4fde093b728131a1827903872171089dedb16f7665d3a91
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.3.2"
|
||||
version: "3.2.0"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -389,10 +381,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: synchronized
|
||||
sha256: "63896c27e81b28f8cb4e69ead0d3e8f03f1d1e5fc531a3e579cabed6a2c7c9e5"
|
||||
sha256: c254ade258ec8282947a0acbbc90b9575b4f19673533ee46f2f6e9b3aeefd7c0
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.4.0+1"
|
||||
version: "3.4.0"
|
||||
term_glyph:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -405,26 +397,26 @@ packages:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: test
|
||||
sha256: ca578dc12bb8b2f40b67b7d3bd2fac4f31c01a6ff7130a14e2597b919934507f
|
||||
sha256: "8d9ceddbab833f180fbefed08afa76d7c03513dfdba87ffcec2718b02bbcbf20"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.31.1"
|
||||
version: "1.31.0"
|
||||
test_api:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: "2a122cbe059f8b610d3a5415f42e255b6c17b1f21eee1d960f31080237fb4f11"
|
||||
sha256: "949a932224383300f01be9221c39180316445ecb8e7547f70a41a35bf421fb9e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.12"
|
||||
version: "0.7.11"
|
||||
test_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_core
|
||||
sha256: d2e98ec12998368dc59ddd47ab709f2cd55acd6b66dc7db764455a44082f4bc5
|
||||
sha256: "1991d4cfe85d5043241acac92962c3977c8d2f2add1ee73130c7b286417d1d34"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.18"
|
||||
version: "0.6.17"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -437,10 +429,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vm_service
|
||||
sha256: "0016aef94fc66495ac78af5859181e3f3bf2026bd8eecc72b9565601e19ab360"
|
||||
sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "15.2.0"
|
||||
version: "15.0.2"
|
||||
watcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -485,10 +477,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: xml
|
||||
sha256: "67f0aff7be013d107995e9b75bf4e7f2c3ef2dfdb2c8e68024bba0a7fd5756a4"
|
||||
sha256: "971043b3a0d3da28727e40ed3e0b5d18b742fa5a68665cca88e74b7876d5e025"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.0.1"
|
||||
version: "6.6.1"
|
||||
yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -498,4 +490,4 @@ packages:
|
||||
source: hosted
|
||||
version: "3.1.3"
|
||||
sdks:
|
||||
dart: ">=3.11.0 <4.0.0"
|
||||
dart: ">=3.10.1 <4.0.0"
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ dependencies:
|
||||
sqflite_common: ^2.5.0
|
||||
sqflite_common_ffi: ^2.3.0
|
||||
sqlite3: ^3.1.6
|
||||
xml: '>=6.0.0 < 8.0.0'
|
||||
xml: ^6.5.0
|
||||
|
||||
dev_dependencies:
|
||||
benchmark_harness: ^2.4.0
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:jadb/models/kanji_search/kanji_search_radical.dart';
|
||||
import 'package:jadb/models/kanji_search/kanji_search_result.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
Object? _roundTripJson(Object? value) => jsonDecode(jsonEncode(value));
|
||||
|
||||
Map<String, dynamic> _roundTripMap(Object? json) =>
|
||||
Map<String, dynamic>.from(_roundTripJson(json) as Map);
|
||||
|
||||
void main() {
|
||||
test('KanjiSearchRadical JSON serialization roundtrip', () {
|
||||
const radical = KanjiSearchRadical(
|
||||
symbol: '人',
|
||||
names: ['ひと', 'にんべん'],
|
||||
forms: ['亻'],
|
||||
meanings: ['person', 'human'],
|
||||
);
|
||||
|
||||
final restored = KanjiSearchRadical.fromJson(
|
||||
_roundTripMap(radical.toJson()),
|
||||
);
|
||||
|
||||
expect(restored, equals(radical));
|
||||
});
|
||||
|
||||
test('KanjiSearchResult JSON serialization roundtrip', () {
|
||||
const result = KanjiSearchResult(
|
||||
kanji: '休',
|
||||
taughtIn: 1,
|
||||
jlptLevel: 'N5',
|
||||
newspaperFrequencyRank: 1234,
|
||||
strokeCount: 6,
|
||||
meanings: ['rest', 'day off'],
|
||||
kunyomi: ['やす.む', 'やす.まる'],
|
||||
onyomi: ['キュウ'],
|
||||
radical: KanjiSearchRadical(
|
||||
symbol: '人',
|
||||
names: ['ひと', 'にんべん'],
|
||||
forms: ['亻'],
|
||||
meanings: ['person', 'human'],
|
||||
),
|
||||
parts: ['亻', '木'],
|
||||
codepoints: {'ucs': '4F11', 'jis208': '1-22-57'},
|
||||
nanori: ['やす'],
|
||||
alternativeLanguageReadings: {
|
||||
'korean': ['휴'],
|
||||
'pinyin': ['xiū'],
|
||||
},
|
||||
strokeMiscounts: [5, 7],
|
||||
queryCodes: {
|
||||
'skip': ['1-2-4'],
|
||||
'fourCorner': ['2429.0'],
|
||||
},
|
||||
dictionaryReferences: {'nelson_c': '122', 'heisig': '457'},
|
||||
);
|
||||
|
||||
final restored = KanjiSearchResult.fromJson(_roundTripMap(result.toJson()));
|
||||
|
||||
expect(restored, equals(result));
|
||||
});
|
||||
|
||||
test(
|
||||
'KanjiSearchResult JSON serialization roundtrip - nullable and empty fields',
|
||||
() {
|
||||
const result = KanjiSearchResult(
|
||||
kanji: '々',
|
||||
taughtIn: null,
|
||||
jlptLevel: null,
|
||||
newspaperFrequencyRank: null,
|
||||
strokeCount: 3,
|
||||
meanings: ['iteration mark'],
|
||||
kunyomi: [],
|
||||
onyomi: [],
|
||||
radical: null,
|
||||
parts: [],
|
||||
codepoints: {'ucs': '3005'},
|
||||
nanori: [],
|
||||
alternativeLanguageReadings: {},
|
||||
strokeMiscounts: [],
|
||||
queryCodes: {},
|
||||
dictionaryReferences: {},
|
||||
);
|
||||
|
||||
final restored = KanjiSearchResult.fromJson(
|
||||
_roundTripMap(result.toJson()),
|
||||
);
|
||||
|
||||
expect(restored, equals(result));
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -1,282 +0,0 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:jadb/models/common/jlpt_level.dart';
|
||||
import 'package:jadb/models/jmdict/jmdict_dialect.dart';
|
||||
import 'package:jadb/models/jmdict/jmdict_field.dart';
|
||||
import 'package:jadb/models/jmdict/jmdict_kanji_info.dart';
|
||||
import 'package:jadb/models/jmdict/jmdict_misc.dart';
|
||||
import 'package:jadb/models/jmdict/jmdict_pos.dart';
|
||||
import 'package:jadb/models/jmdict/jmdict_reading_info.dart';
|
||||
import 'package:jadb/models/word_search/word_search_match_span.dart';
|
||||
import 'package:jadb/models/word_search/word_search_result.dart';
|
||||
import 'package:jadb/models/word_search/word_search_ruby.dart';
|
||||
import 'package:jadb/models/word_search/word_search_sense.dart';
|
||||
import 'package:jadb/models/word_search/word_search_sense_language_source.dart';
|
||||
import 'package:jadb/models/word_search/word_search_sources.dart';
|
||||
import 'package:jadb/models/word_search/word_search_xref_entry.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
Object? _roundTripJson(Object? value) => jsonDecode(jsonEncode(value));
|
||||
|
||||
Map<String, dynamic> _roundTripMap(Object? json) =>
|
||||
Map<String, dynamic>.from(_roundTripJson(json) as Map);
|
||||
|
||||
Map<String, Object?> _roundTripObjectMap(Object? json) =>
|
||||
Map<String, Object?>.from(_roundTripJson(json) as Map);
|
||||
|
||||
void _expectScalarRoundTrip<T>({
|
||||
required Iterable<T> values,
|
||||
required Object? Function(T value) toJson,
|
||||
required T Function(Object? json) fromJson,
|
||||
}) {
|
||||
for (final value in values) {
|
||||
expect(
|
||||
fromJson(_roundTripJson(toJson(value))),
|
||||
equals(value),
|
||||
reason: 'Roundtrip failed for $value',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void _expectMapRoundTrip<T>({
|
||||
required Iterable<T> values,
|
||||
required Map<String, Object?> Function(T value) toJson,
|
||||
required T Function(Map<String, Object?> json) fromJson,
|
||||
}) {
|
||||
for (final value in values) {
|
||||
expect(
|
||||
fromJson(_roundTripObjectMap(toJson(value))),
|
||||
equals(value),
|
||||
reason: 'Roundtrip failed for $value',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
WordSearchResult _buildNestedXrefResult() => WordSearchResult(
|
||||
score: 7,
|
||||
entryId: 300,
|
||||
isCommon: false,
|
||||
japanese: [WordSearchRuby(base: '補助', furigana: 'ほじょ')],
|
||||
kanjiInfo: const {},
|
||||
readingInfo: const {},
|
||||
senses: const [],
|
||||
jlptLevel: JlptLevel.none,
|
||||
sources: const WordSearchSources(jmdict: true, jmnedict: false),
|
||||
);
|
||||
|
||||
WordSearchSense _buildSense() => WordSearchSense(
|
||||
englishDefinitions: ['kana', 'syllabary'],
|
||||
partsOfSpeech: [JMdictPOS.n],
|
||||
seeAlso: [
|
||||
WordSearchXrefEntry(
|
||||
entryId: 300,
|
||||
ambiguous: false,
|
||||
baseWord: '仮名遣い',
|
||||
furigana: 'かなづかい',
|
||||
xrefResult: _buildNestedXrefResult(),
|
||||
),
|
||||
],
|
||||
antonyms: const [
|
||||
WordSearchXrefEntry(
|
||||
entryId: 301,
|
||||
ambiguous: true,
|
||||
baseWord: '漢字',
|
||||
furigana: 'かんじ',
|
||||
xrefResult: null,
|
||||
),
|
||||
],
|
||||
restrictedToReading: ['かな'],
|
||||
restrictedToKanji: ['仮名'],
|
||||
fields: [JMdictField.linguistics],
|
||||
dialects: [JMdictDialect.kansai],
|
||||
misc: [JMdictMisc.onlyKana, JMdictMisc.rare],
|
||||
info: ['Typically written using kana alone.'],
|
||||
languageSource: const [
|
||||
WordSearchSenseLanguageSource(
|
||||
language: 'por',
|
||||
phrase: 'canoa',
|
||||
fullyDescribesSense: false,
|
||||
constructedFromSmallerWords: true,
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
WordSearchResult _buildWordSearchResult({
|
||||
List<WordSearchMatchSpan>? matchSpans,
|
||||
}) => WordSearchResult(
|
||||
score: 42,
|
||||
entryId: 123,
|
||||
isCommon: true,
|
||||
japanese: [
|
||||
WordSearchRuby(base: '仮名', furigana: 'かな'),
|
||||
WordSearchRuby(base: 'かな'),
|
||||
],
|
||||
kanjiInfo: {'仮名': JMdictKanjiInfo.rK, '仮': JMdictKanjiInfo.ateji},
|
||||
readingInfo: {'かな': JMdictReadingInfo.gikun, 'カナ': JMdictReadingInfo.rk},
|
||||
senses: [_buildSense()],
|
||||
jlptLevel: JlptLevel.n5,
|
||||
sources: const WordSearchSources(jmdict: true, jmnedict: true),
|
||||
matchSpans: matchSpans,
|
||||
);
|
||||
|
||||
void main() {
|
||||
test('JlptLevel JSON serialization roundtrip', () {
|
||||
_expectScalarRoundTrip<JlptLevel>(
|
||||
values: JlptLevel.values,
|
||||
toJson: (value) => value.toJson(),
|
||||
fromJson: JlptLevel.fromJson,
|
||||
);
|
||||
});
|
||||
|
||||
test('JMdictDialect JSON serialization roundtrip', () {
|
||||
_expectMapRoundTrip<JMdictDialect>(
|
||||
values: JMdictDialect.values,
|
||||
toJson: (value) => value.toJson(),
|
||||
fromJson: JMdictDialect.fromJson,
|
||||
);
|
||||
});
|
||||
|
||||
test('JMdictField JSON serialization roundtrip', () {
|
||||
_expectMapRoundTrip<JMdictField>(
|
||||
values: JMdictField.values,
|
||||
toJson: (value) => value.toJson(),
|
||||
fromJson: JMdictField.fromJson,
|
||||
);
|
||||
});
|
||||
|
||||
test('JMdictKanjiInfo JSON serialization roundtrip', () {
|
||||
_expectMapRoundTrip<JMdictKanjiInfo>(
|
||||
values: JMdictKanjiInfo.values,
|
||||
toJson: (value) => value.toJson(),
|
||||
fromJson: JMdictKanjiInfo.fromJson,
|
||||
);
|
||||
});
|
||||
|
||||
test('JMdictMisc JSON serialization roundtrip', () {
|
||||
_expectMapRoundTrip<JMdictMisc>(
|
||||
values: JMdictMisc.values,
|
||||
toJson: (value) => value.toJson(),
|
||||
fromJson: JMdictMisc.fromJson,
|
||||
);
|
||||
});
|
||||
|
||||
test('JMdictPOS JSON serialization roundtrip', () {
|
||||
_expectMapRoundTrip<JMdictPOS>(
|
||||
values: JMdictPOS.values,
|
||||
toJson: (value) => value.toJson(),
|
||||
fromJson: JMdictPOS.fromJson,
|
||||
);
|
||||
});
|
||||
|
||||
test('JMdictReadingInfo JSON serialization roundtrip', () {
|
||||
_expectMapRoundTrip<JMdictReadingInfo>(
|
||||
values: JMdictReadingInfo.values,
|
||||
toJson: (value) => value.toJson(),
|
||||
fromJson: JMdictReadingInfo.fromJson,
|
||||
);
|
||||
});
|
||||
|
||||
test('WordSearchMatchSpan JSON serialization roundtrip', () {
|
||||
final span = WordSearchMatchSpan(
|
||||
spanType: WordSearchMatchSpanType.sense,
|
||||
index: 2,
|
||||
subIndex: 3,
|
||||
start: 4,
|
||||
end: 8,
|
||||
);
|
||||
|
||||
final restored = WordSearchMatchSpan.fromJson(_roundTripMap(span.toJson()));
|
||||
|
||||
expect(restored, equals(span));
|
||||
expect(restored.subIndex, equals(span.subIndex));
|
||||
});
|
||||
|
||||
test('WordSearchRuby JSON serialization roundtrip', () {
|
||||
final ruby = WordSearchRuby(base: '仮名', furigana: 'かな');
|
||||
|
||||
final restored = WordSearchRuby.fromJson(_roundTripMap(ruby.toJson()));
|
||||
|
||||
expect(restored.toJson(), equals(ruby.toJson()));
|
||||
});
|
||||
|
||||
test('WordSearchSenseLanguageSource JSON serialization roundtrip', () {
|
||||
const source = WordSearchSenseLanguageSource(
|
||||
language: 'por',
|
||||
phrase: 'canoa',
|
||||
fullyDescribesSense: false,
|
||||
constructedFromSmallerWords: true,
|
||||
);
|
||||
|
||||
final restored = WordSearchSenseLanguageSource.fromJson(
|
||||
_roundTripMap(source.toJson()),
|
||||
);
|
||||
|
||||
expect(restored.toJson(), equals(source.toJson()));
|
||||
});
|
||||
|
||||
test('WordSearchSources JSON serialization roundtrip', () {
|
||||
const sources = WordSearchSources(jmdict: false, jmnedict: true);
|
||||
|
||||
final restored = WordSearchSources.fromJson(
|
||||
_roundTripMap(sources.toJson()),
|
||||
);
|
||||
|
||||
expect(restored.toJson(), equals(sources.toJson()));
|
||||
expect(restored.sqlValue, equals(sources.sqlValue));
|
||||
});
|
||||
|
||||
test('WordSearchXrefEntry JSON serialization roundtrip', () {
|
||||
final entry = WordSearchXrefEntry(
|
||||
entryId: 300,
|
||||
ambiguous: false,
|
||||
baseWord: '仮名遣い',
|
||||
furigana: 'かなづかい',
|
||||
xrefResult: _buildNestedXrefResult(),
|
||||
);
|
||||
|
||||
final restored = WordSearchXrefEntry.fromJson(
|
||||
_roundTripMap(entry.toJson()),
|
||||
);
|
||||
|
||||
expect(restored.toJson(), equals(entry.toJson()));
|
||||
expect(restored.xrefResult?.toJson(), equals(entry.xrefResult?.toJson()));
|
||||
});
|
||||
|
||||
test('WordSearchSense JSON serialization roundtrip', () {
|
||||
final sense = _buildSense();
|
||||
|
||||
final restored = WordSearchSense.fromJson(_roundTripMap(sense.toJson()));
|
||||
|
||||
expect(restored.toJson(), equals(sense.toJson()));
|
||||
});
|
||||
|
||||
test('WordSearchResult JSON serialization roundtrip', () {
|
||||
final result = _buildWordSearchResult();
|
||||
|
||||
final restored = WordSearchResult.fromJson(_roundTripMap(result.toJson()));
|
||||
|
||||
expect(restored.toJson(), equals(result.toJson()));
|
||||
expect(restored.matchSpans, isNull);
|
||||
});
|
||||
|
||||
test('WordSearchResult leaves matchSpans out of JSON', () {
|
||||
final result = _buildWordSearchResult(
|
||||
matchSpans: [
|
||||
WordSearchMatchSpan(
|
||||
spanType: WordSearchMatchSpanType.sense,
|
||||
index: 0,
|
||||
subIndex: 1,
|
||||
start: 0,
|
||||
end: 4,
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
final json = _roundTripMap(result.toJson());
|
||||
final restored = WordSearchResult.fromJson(json);
|
||||
|
||||
expect(json.containsKey('matchSpans'), isFalse);
|
||||
expect(restored.matchSpans, isNull);
|
||||
expect(restored.toJson(), equals(result.toJson()));
|
||||
});
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
import 'package:jadb/search.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'setup_database_connection.dart';
|
||||
|
||||
void main() {
|
||||
test('Retrieve datasource versions', () async {
|
||||
final connection = await setupDatabaseConnection();
|
||||
final result = await connection.jadbGetDatasourceVersions();
|
||||
expect(result, isNotNull);
|
||||
});
|
||||
}
|
||||
@@ -7,29 +7,32 @@ import 'package:test/test.dart';
|
||||
import 'setup_database_connection.dart';
|
||||
|
||||
void main() {
|
||||
test('All constant radicals should exist in the database', () async {
|
||||
final connection = await setupDatabaseConnection();
|
||||
final allRadicalsInDb = await connection.query(
|
||||
RADKFILETableNames.radkfile,
|
||||
columns: ['radical'],
|
||||
distinct: true,
|
||||
);
|
||||
test(
|
||||
'All constant radicals should exist in the database',
|
||||
() async {
|
||||
final connection = await setupDatabaseConnection();
|
||||
final allRadicalsInDb = await connection.query(
|
||||
RADKFILETableNames.radkfile,
|
||||
columns: ['radical'],
|
||||
distinct: true,
|
||||
);
|
||||
|
||||
final radicalsInDb = allRadicalsInDb
|
||||
.map((e) => e['radical'] as String)
|
||||
.toSet();
|
||||
final radicalsInDb = allRadicalsInDb
|
||||
.map((e) => e['radical'] as String)
|
||||
.toSet();
|
||||
|
||||
final missingRadicals = radicals.values.flattenedToSet
|
||||
.map((e) => e.formalVariant)
|
||||
.toSet()
|
||||
.difference(radicalsInDb);
|
||||
final missingRadicals = radicals.values.flattenedToSet.difference(
|
||||
radicalsInDb,
|
||||
);
|
||||
|
||||
expect(
|
||||
missingRadicals,
|
||||
isEmpty,
|
||||
reason: 'Missing radicals in database: $missingRadicals',
|
||||
);
|
||||
});
|
||||
expect(
|
||||
missingRadicals,
|
||||
isEmpty,
|
||||
reason: 'Missing radicals in database: $missingRadicals',
|
||||
);
|
||||
},
|
||||
skip: 'Test is valid, code is broken, fix me',
|
||||
);
|
||||
|
||||
test(
|
||||
'All radicals in database should be in the constant radical list',
|
||||
@@ -46,7 +49,7 @@ void main() {
|
||||
.toSet();
|
||||
|
||||
final extraRadicals = radicalsInDb.difference(
|
||||
radicals.values.flattenedToSet.map((e) => e.formalVariant).toSet(),
|
||||
radicals.values.flattenedToSet,
|
||||
);
|
||||
|
||||
expect(
|
||||
@@ -56,30 +59,21 @@ void main() {
|
||||
'Extra radicals in database missing in the constant list: $extraRadicals',
|
||||
);
|
||||
},
|
||||
skip: 'Test is valid, code is broken, fix me',
|
||||
);
|
||||
|
||||
test('All constant radicals are located in the correct stroke count group', () {
|
||||
for (final mapEntry in radicals.entries) {
|
||||
final strokeCount = mapEntry.key;
|
||||
final radicalsInGroup = mapEntry.value;
|
||||
for (final radical in radicalsInGroup) {
|
||||
expect(
|
||||
strokeCount,
|
||||
radical.strokeCount,
|
||||
reason:
|
||||
'Radical ${radical.formalVariant} should have stroke count $strokeCount but has ${radical.strokeCount}',
|
||||
);
|
||||
group(
|
||||
'All radicals should return results',
|
||||
() {
|
||||
for (final radical in radicals.values.flattened) {
|
||||
test(' - $radical', () async {
|
||||
final connection = await setupDatabaseConnection();
|
||||
final result = await connection.jadbSearchKanjiByRadicals([radical]);
|
||||
expect(result, isNotEmpty);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
group('All radicals should return results', () {
|
||||
for (final radical in radicals.values.flattened) {
|
||||
test(' - $radical', () async {
|
||||
final connection = await setupDatabaseConnection();
|
||||
final result = await connection.jadbSearchKanjiByRadicals([radical]);
|
||||
expect(result, isNotEmpty);
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
skip:
|
||||
'These will be automatically fixed once the other radical tests are passing',
|
||||
);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ void main() {
|
||||
expect(result, 'かたまり');
|
||||
});
|
||||
|
||||
test('Basic test with dakuten', () {
|
||||
test('Basic test with diacritics', () {
|
||||
final result = transliterateLatinToHiragana('gadamari');
|
||||
expect(result, 'がだまり');
|
||||
});
|
||||
@@ -54,7 +54,7 @@ void main() {
|
||||
|
||||
test('Basic test', expectSpans('katamari', ['か', 'た', 'ま', 'り']));
|
||||
test(
|
||||
'Basic test with dakuten',
|
||||
'Basic test with diacritics',
|
||||
expectSpans('gadamari', ['が', 'だ', 'ま', 'り']),
|
||||
);
|
||||
test('wi and we', expectSpans('wiwe', ['うぃ', 'うぇ']));
|
||||
@@ -72,7 +72,7 @@ void main() {
|
||||
expect(result, 'katamari');
|
||||
});
|
||||
|
||||
test('Basic test with dakuten', () {
|
||||
test('Basic test with diacritics', () {
|
||||
final result = transliterateHiraganaToLatin('がだまり');
|
||||
expect(result, 'gadamari');
|
||||
});
|
||||
@@ -91,21 +91,6 @@ void main() {
|
||||
final result = transliterateHiraganaToLatin('かっぱ');
|
||||
expect(result, 'kappa');
|
||||
});
|
||||
|
||||
test('Iteration mark', () {
|
||||
final result = transliterateHiraganaToLatin('さゝき');
|
||||
expect(result, 'sasaki');
|
||||
}, skip: 'Not yet implemented');
|
||||
|
||||
test('Iteration mark with dakuten', () {
|
||||
final result = transliterateHiraganaToLatin('あひゞき');
|
||||
expect(result, 'ahibiki');
|
||||
}, skip: 'Not yet implemented');
|
||||
|
||||
test('Yori', () {
|
||||
final result = transliterateHiraganaToLatin('ゟ');
|
||||
expect(result, 'yori');
|
||||
}, skip: 'Not yet implemented');
|
||||
});
|
||||
|
||||
group('Hiragana -> Romaji Spans', () {
|
||||
@@ -125,7 +110,7 @@ void main() {
|
||||
|
||||
test('Basic test', expectSpans('かたまり', ['ka', 'ta', 'ma', 'ri']));
|
||||
test(
|
||||
'Basic test with dakuten',
|
||||
'Basic test with diacritics',
|
||||
expectSpans('がだまり', ['ga', 'da', 'ma', 'ri']),
|
||||
);
|
||||
test('wi and we', expectSpans('うぃうぇ', ['whi', 'whe']));
|
||||
@@ -133,17 +118,5 @@ void main() {
|
||||
|
||||
// TODO: fix the implementation
|
||||
// test('Double consonant', expectSpans('かっぱ', ['ka', 'ppa']));
|
||||
|
||||
test(
|
||||
'Iteration mark',
|
||||
expectSpans('さゝき', ['sa', 'sa', 'ki']),
|
||||
skip: 'Not yet implemented',
|
||||
);
|
||||
test(
|
||||
'Iteration mark with dakuten',
|
||||
expectSpans('あひゞき', ['a', 'hi', 'bi', 'ki']),
|
||||
skip: 'Not yet implemented',
|
||||
);
|
||||
test('Yori', expectSpans('ゟ', ['yori']), skip: 'Not yet implemented');
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user