lib: implement basic kanji search
This commit is contained in:
40
lib/models/kanjidic/kanji_search_radical.dart
Normal file
40
lib/models/kanjidic/kanji_search_radical.dart
Normal file
@@ -0,0 +1,40 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
class KanjiSearchRadical extends Equatable {
|
||||
/// The radical symbol.
|
||||
final String symbol;
|
||||
|
||||
/// The radical forms used in this kanji.
|
||||
final List<String> forms;
|
||||
|
||||
/// The meaning of the radical.
|
||||
final String meaning;
|
||||
|
||||
// ignore: public_member_api_docs
|
||||
const KanjiSearchRadical({
|
||||
required this.symbol,
|
||||
this.forms = const [],
|
||||
required this.meaning,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object> get props => [
|
||||
symbol,
|
||||
forms,
|
||||
meaning,
|
||||
];
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'symbol': symbol,
|
||||
'forms': forms,
|
||||
'meaning': meaning,
|
||||
};
|
||||
|
||||
factory KanjiSearchRadical.fromJson(Map<String, dynamic> json) {
|
||||
return KanjiSearchRadical(
|
||||
symbol: json['symbol'] as String,
|
||||
forms: (json['forms'] as List).map((e) => e as String).toList(),
|
||||
meaning: json['meaning'] as String,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,117 @@
|
||||
class KanjiSearchResult {
|
||||
// TODO: implement me
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
import 'kanji_search_radical.dart';
|
||||
|
||||
class KanjiSearchResult extends Equatable {
|
||||
/// The kanji symbol
|
||||
final String kanji;
|
||||
|
||||
/// The school level that the kanji is taught in, if applicable.
|
||||
final String? taughtIn;
|
||||
|
||||
/// The lowest JLPT exam that this kanji is likely to appear in, if applicable.
|
||||
///
|
||||
/// 'N5' or 'N4' or 'N3' or 'N2' or 'N1'.
|
||||
final String? jlptLevel;
|
||||
|
||||
/// A number representing this kanji's frequency rank in newspapers, if applicable.
|
||||
final int? newspaperFrequencyRank;
|
||||
|
||||
/// How many strokes this kanji is typically drawn in.
|
||||
final int strokeCount;
|
||||
|
||||
/// The meanings of the kanji.
|
||||
final List<String> meanings;
|
||||
|
||||
/// This character's kunyomi.
|
||||
final List<String> kunyomi;
|
||||
|
||||
/// This character's onyomi.
|
||||
final List<String> onyomi;
|
||||
|
||||
// /// Examples of this character's kunyomi being used.
|
||||
// final List<YomiExample> kunyomiExamples;
|
||||
|
||||
// /// Examples of this character's onyomi being used.
|
||||
// final List<YomiExample> onyomiExamples;
|
||||
|
||||
/// Information about this character's radical, if applicable.
|
||||
final KanjiSearchRadical? radical;
|
||||
|
||||
// TODO: document more accurately what kind of parts?
|
||||
/// The parts used in this kanji.
|
||||
final List<String> parts;
|
||||
|
||||
const KanjiSearchResult({
|
||||
required this.kanji,
|
||||
this.taughtIn,
|
||||
this.jlptLevel,
|
||||
this.newspaperFrequencyRank,
|
||||
required this.strokeCount,
|
||||
required this.meanings,
|
||||
this.kunyomi = const [],
|
||||
this.onyomi = const [],
|
||||
// this.kunyomiExamples = const [],
|
||||
// this.onyomiExamples = const [],
|
||||
this.radical,
|
||||
this.parts = const [],
|
||||
});
|
||||
|
||||
@override
|
||||
// ignore: public_member_api_docs
|
||||
List<Object?> get props => [
|
||||
taughtIn,
|
||||
jlptLevel,
|
||||
newspaperFrequencyRank,
|
||||
strokeCount,
|
||||
meanings,
|
||||
kunyomi,
|
||||
onyomi,
|
||||
// kunyomiExamples,
|
||||
// onyomiExamples,
|
||||
radical,
|
||||
parts,
|
||||
];
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'kanji': kanji,
|
||||
'taughtIn': taughtIn,
|
||||
'jlptLevel': jlptLevel,
|
||||
'newspaperFrequencyRank': newspaperFrequencyRank,
|
||||
'strokeCount': strokeCount,
|
||||
'meanings': meanings,
|
||||
'kunyomi': kunyomi,
|
||||
'onyomi': onyomi,
|
||||
// 'onyomiExamples': onyomiExamples,
|
||||
// 'kunyomiExamples': kunyomiExamples,
|
||||
'radical': radical?.toJson(),
|
||||
'parts': parts,
|
||||
// 'strokeOrderDiagramUri': strokeOrderDiagramUri,
|
||||
// 'strokeOrderSvgUri': strokeOrderSvgUri,
|
||||
// 'strokeOrderGifUri': strokeOrderGifUri,
|
||||
// 'uri': uri,
|
||||
};
|
||||
|
||||
factory KanjiSearchResult.fromJson(Map<String, dynamic> json) {
|
||||
return KanjiSearchResult(
|
||||
kanji: json['kanji'] as String,
|
||||
taughtIn: json['taughtIn'] as String?,
|
||||
jlptLevel: json['jlptLevel'] as String?,
|
||||
newspaperFrequencyRank: json['newspaperFrequencyRank'] as int?,
|
||||
strokeCount: json['strokeCount'] as int,
|
||||
meanings: (json['meanings'] as List).map((e) => e as String).toList(),
|
||||
kunyomi: (json['kunyomi'] as List).map((e) => e as String).toList(),
|
||||
onyomi: (json['onyomi'] as List).map((e) => e as String).toList(),
|
||||
// kunyomiExamples: (json['kunyomiExamples'] as List)
|
||||
// .map((e) => YomiExample.fromJson(e))
|
||||
// .toList(),
|
||||
// onyomiExamples: (json['onyomiExamples'] as List)
|
||||
// .map((e) => YomiExample.fromJson(e))
|
||||
// .toList(),
|
||||
radical: json['radical'] != null
|
||||
? KanjiSearchRadical.fromJson(json['radical'])
|
||||
: null,
|
||||
parts: (json['parts'] as List).map((e) => e as String).toList(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,14 +2,23 @@ import 'package:jadb/models/jmdict/word_search_result.dart';
|
||||
import 'package:jadb/models/kanjidic/kanji_search_result.dart';
|
||||
import 'package:jadb/models/radkfile/radicals_search_result.dart';
|
||||
|
||||
Future<KanjiSearchResult> searchKanji(String kanji) async {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
import 'package:jadb/search/kanji.dart';
|
||||
|
||||
Future<RadicalsSearchResult> searchKanjiByRadicals(List<String> radicals) {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
import 'package:sqflite_common/sqlite_api.dart';
|
||||
|
||||
Future<WordSearchResult> searchWord(String word) async {
|
||||
throw UnimplementedError();
|
||||
class JaDBConnection {
|
||||
final DatabaseExecutor _connection;
|
||||
|
||||
const JaDBConnection(this._connection);
|
||||
|
||||
Future<KanjiSearchResult?> searchKanji(String kanji) async =>
|
||||
searchKanjiWithDbConnection(this._connection, kanji);
|
||||
|
||||
Future<RadicalsSearchResult> searchKanjiByRadicals(List<String> radicals) {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
Future<WordSearchResult> searchWord(String word) async {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
}
|
||||
|
||||
158
lib/search/kanji.dart
Normal file
158
lib/search/kanji.dart
Normal file
@@ -0,0 +1,158 @@
|
||||
import 'package:jadb/models/kanjidic/kanji_search_result.dart';
|
||||
import 'package:sqflite_common/sqflite.dart';
|
||||
|
||||
Future<KanjiSearchResult?> searchKanjiWithDbConnection(
|
||||
DatabaseExecutor connection,
|
||||
String kanji,
|
||||
) async {
|
||||
late final List<Map<String, Object?>> characters;
|
||||
final characters_query = connection.query(
|
||||
"KANJIDIC_Character",
|
||||
where: "KANJIDIC_Character.literal = ?",
|
||||
whereArgs: [kanji],
|
||||
);
|
||||
|
||||
late final List<Map<String, Object?>> codepoints;
|
||||
final codepoints_query = connection.query(
|
||||
"KANJIDIC_Codepoint",
|
||||
where: "KANJIDIC_Codepoint.kanji = ?",
|
||||
whereArgs: [kanji],
|
||||
);
|
||||
|
||||
late final List<Map<String, Object?>> kunyomis;
|
||||
final kunyomis_query = connection.query(
|
||||
"KANJIDIC_Kunyomi",
|
||||
where: "KANJIDIC_Kunyomi.kanji = ?",
|
||||
whereArgs: [kanji],
|
||||
);
|
||||
|
||||
late final List<Map<String, Object?>> onyomis;
|
||||
final onyomis_query = connection.query(
|
||||
"KANJIDIC_Onyomi",
|
||||
where: "KANJIDIC_Onyomi.kanji = ?",
|
||||
whereArgs: [kanji],
|
||||
);
|
||||
|
||||
late final List<Map<String, Object?>> meanings;
|
||||
final meanings_query = connection.query(
|
||||
"KANJIDIC_Meaning",
|
||||
where: "KANJIDIC_Meaning.kanji = ? AND KANJIDIC_Meaning.language = ?",
|
||||
whereArgs: [kanji, 'eng'],
|
||||
);
|
||||
|
||||
late final List<Map<String, Object?>> nanoris;
|
||||
final nanoris_query = connection.query(
|
||||
"KANJIDIC_Nanori",
|
||||
where: "KANJIDIC_Nanori.kanji = ?",
|
||||
whereArgs: [kanji],
|
||||
);
|
||||
|
||||
late final List<Map<String, Object?>> dictionary_references;
|
||||
final dictionary_references_query = connection.query(
|
||||
"KANJIDIC_DictionaryReference",
|
||||
where: "KANJIDIC_DictionaryReference.kanji = ?",
|
||||
whereArgs: [kanji],
|
||||
);
|
||||
|
||||
late final List<Map<String, Object?>> query_codes;
|
||||
final query_codes_query = connection.query(
|
||||
"KANJIDIC_QueryCode",
|
||||
where: "KANJIDIC_QueryCode.kanji = ?",
|
||||
whereArgs: [kanji],
|
||||
);
|
||||
|
||||
late final List<Map<String, Object?>> radicals;
|
||||
final radicals_query = connection.query(
|
||||
"KANJIDIC_Radical",
|
||||
where: "KANJIDIC_Radical.kanji = ?",
|
||||
whereArgs: [kanji],
|
||||
);
|
||||
|
||||
late final List<Map<String, Object?>> radical_names;
|
||||
final radical_names_query = connection.query(
|
||||
"KANJIDIC_RadicalName",
|
||||
where: "KANJIDIC_RadicalName.kanji = ?",
|
||||
whereArgs: [kanji],
|
||||
);
|
||||
|
||||
late final List<Map<String, Object?>> readings;
|
||||
final readings_query = connection.query(
|
||||
"KANJIDIC_Reading",
|
||||
where: "KANJIDIC_Reading.kanji = ?",
|
||||
whereArgs: [kanji],
|
||||
);
|
||||
|
||||
late final List<Map<String, Object?>> stroke_miscounts;
|
||||
final stroke_miscounts_query = connection.query(
|
||||
"KANJIDIC_StrokeMiscount",
|
||||
where: "KANJIDIC_StrokeMiscount.kanji = ?",
|
||||
whereArgs: [kanji],
|
||||
);
|
||||
|
||||
late final List<Map<String, Object?>> variants;
|
||||
final variants_query = connection.query(
|
||||
"KANJIDIC_Variant",
|
||||
where: "KANJIDIC_Variant.kanji = ?",
|
||||
whereArgs: [kanji],
|
||||
);
|
||||
|
||||
// TODO: Search for kunyomi and onyomi usage of the characters
|
||||
// from JMDict. We'll need to fuzzy aquery JMDict_KanjiElement for mathces,
|
||||
// filter JMdict_ReadingElement for kunyomi/onyomi, and then sort the main entry
|
||||
// by JLPT, news frequency, etc.
|
||||
|
||||
await characters_query.then((value) => characters = value);
|
||||
|
||||
if (characters.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
|
||||
await Future.wait({
|
||||
codepoints_query.then((value) => codepoints = value),
|
||||
kunyomis_query.then((value) => kunyomis = value),
|
||||
onyomis_query.then((value) => onyomis = value),
|
||||
meanings_query.then((value) => meanings = value),
|
||||
nanoris_query.then((value) => nanoris = value),
|
||||
dictionary_references_query.then((value) => dictionary_references = value),
|
||||
query_codes_query.then((value) => query_codes = value),
|
||||
radicals_query.then((value) => radicals = value),
|
||||
radical_names_query.then((value) => radical_names = value),
|
||||
readings_query.then((value) => readings = value),
|
||||
stroke_miscounts_query.then((value) => stroke_miscounts = value),
|
||||
variants_query.then((value) => variants = value),
|
||||
});
|
||||
|
||||
final entry = characters.first;
|
||||
|
||||
final String? grade = {
|
||||
1: 'grade 1',
|
||||
2: 'grade 2',
|
||||
3: 'grade 3',
|
||||
4: 'grade 4',
|
||||
5: 'grade 5',
|
||||
6: 'grade 6',
|
||||
7: 'grade 7',
|
||||
8: 'grade 8',
|
||||
9: 'grade 9',
|
||||
10: 'grade 10',
|
||||
}[entry['grade'] as int?];
|
||||
|
||||
final String? jlptLevel = {
|
||||
5: 'N5',
|
||||
4: 'N4',
|
||||
3: 'N3',
|
||||
2: 'N2',
|
||||
1: 'N1',
|
||||
}[entry['jlpt'] as int?];
|
||||
|
||||
return KanjiSearchResult(
|
||||
kanji: entry['literal']! as String,
|
||||
taughtIn: grade,
|
||||
jlptLevel: jlptLevel,
|
||||
newspaperFrequencyRank: entry['frequency'] as int?,
|
||||
strokeCount: entry['strokeCount'] as int,
|
||||
meanings: meanings.map((item) => item['meaning'] as String).toList(),
|
||||
kunyomi: kunyomis.map((item) => item['yomi'] as String).toList(),
|
||||
onyomi: onyomis.map((item) => item['yomi'] as String).toList(),
|
||||
);
|
||||
}
|
||||
10
pubspec.lock
10
pubspec.lock
@@ -17,6 +17,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.19.1"
|
||||
equatable:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: equatable
|
||||
sha256: "567c64b3cb4cf82397aac55f4f0cbd3ca20d77c6c03bedbc4ceaddc08904aef7"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.7"
|
||||
ffi:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -58,7 +66,7 @@ packages:
|
||||
source: hosted
|
||||
version: "6.1.0"
|
||||
sqflite_common:
|
||||
dependency: transitive
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: sqflite_common
|
||||
sha256: "84731e8bfd8303a3389903e01fb2141b6e59b5973cacbb0929021df08dddbe8b"
|
||||
|
||||
@@ -9,6 +9,8 @@ environment:
|
||||
dependencies:
|
||||
args: ^2.7.0
|
||||
collection: ^1.19.1
|
||||
equatable: ^2.0.7
|
||||
sqflite_common: ^2.5.5
|
||||
sqflite_common_ffi: ^2.3.5
|
||||
xml: ^6.5.0
|
||||
|
||||
|
||||
Reference in New Issue
Block a user