Merge pull request #1 from h7x4ABk3g/development

Finish rewrite for version 1
This commit is contained in:
Oystein Kristoffer Tveit 2020-06-24 17:04:29 +02:00 committed by GitHub
commit 0aa41f075f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 4981 additions and 4927 deletions

126
README.md
View File

@ -1,4 +1,4 @@
# Warning: not functional yet # unofficial_jisho_api
A rewrite of the [unofficial-jisho-api](https://www.npmjs.com/package/unofficial-jisho-api) A rewrite of the [unofficial-jisho-api](https://www.npmjs.com/package/unofficial-jisho-api)
@ -9,19 +9,18 @@ Below are some basic examples.
### Word/phrase search (provided by official Jisho API) ### Word/phrase search (provided by official Jisho API)
This returns the same results as the official [Jisho.org](https://jisho.org/) API. See the discussion of that [here](http://jisho.org/forum/54fefc1f6e73340b1f160000-is-there-any-kind-of-search-api). This returns the same results as the official [Jisho.org](https://jisho.org/) API. See the discussion of that [here](https://jisho.org/forum/54fefc1f6e73340b1f160000-is-there-any-kind-of-search-api).
```dart ```dart
import 'package:unofficial_jisho_api/unofficial_jisho_api.dart'; import 'package:unofficial_jisho_api/unofficial_jisho_api.dart';
final jisho = JishoApi(); final jisho = JishoApi();
main() async { void main() async {
jisho.searchForPhrase('日').then((result) { jisho.searchForPhrase('日').then((result) {
... ...
... ...
... ...
}); });
} }
``` ```
@ -32,13 +31,13 @@ import 'dart:convert' show jsonEncode;
import 'package:unofficial_jisho_api/unofficial_jisho_api.dart'; import 'package:unofficial_jisho_api/unofficial_jisho_api.dart';
final jisho = JishoApi(); final jisho = JishoApi();
main() async { void main() async {
jisho.searchForKanji('語').then((result) { await jisho.searchForKanji('語').then((result) {
print('Found: ' + result.found); print('Found: ' + result.found.toString());
print('Taught in: ' + result.taughtIn); print('Taught in: ' + result.taughtIn);
print('JLPT level: ' + result.jlptLevel); print('JLPT level: ' + result.jlptLevel);
print('Newspaper frequency rank: ' + result.newspaperFrequencyRank); print('Newspaper frequency rank: ' + result.newspaperFrequencyRank.toString());
print('Stroke count: ' + result.strokeCount); print('Stroke count: ' + result.strokeCount.toString());
print('Meaning: ' + result.meaning); print('Meaning: ' + result.meaning);
print('Kunyomi: ' + jsonEncode(result.kunyomi)); print('Kunyomi: ' + jsonEncode(result.kunyomi));
print('Kunyomi example: ' + jsonEncode(result.kunyomiExamples[0])); print('Kunyomi example: ' + jsonEncode(result.kunyomiExamples[0]));
@ -50,7 +49,7 @@ main() async {
print('Stroke order SVG: ' + result.strokeOrderSvgUri); print('Stroke order SVG: ' + result.strokeOrderSvgUri);
print('Stroke order GIF: ' + result.strokeOrderGifUri); print('Stroke order GIF: ' + result.strokeOrderGifUri);
print('Jisho Uri: ' + result.uri); print('Jisho Uri: ' + result.uri);
} });
} }
``` ```
@ -68,11 +67,11 @@ Kunyomi example: {"example":"語る","reading":"かたる","meaning":"to talk ab
Onyomi: ["ゴ"] Onyomi: ["ゴ"]
Onyomi example: {"example":"語","reading":"ゴ","meaning":"language, word"} Onyomi example: {"example":"語","reading":"ゴ","meaning":"language, word"}
Radical: {"symbol":"言","forms":["訁"],"meaning":"speech"} Radical: {"symbol":"言","forms":["訁"],"meaning":"speech"}
Parts: ["口","五","言"] Parts: ["五","口","言"]
Stroke order diagram: http://classic.jisho.org/static/images/stroke_diagrams/35486_frames.png Stroke order diagram: https://classic.jisho.org/static/images/stroke_diagrams/35486_frames.png
Stroke order SVG: http://d1w6u4xc3l95km.cloudfront.net/kanji-2015-03/08a9e.svg Stroke order SVG: https://d1w6u4xc3l95km.cloudfront.net/kanji-2015-03/08a9e.svg
Stroke order GIF: https://raw.githubusercontent.com/mistval/kotoba/master/resources/images/kanjianimations/08a9e_anim.gif Stroke order GIF: https://raw.githubusercontent.com/mistval/kanji_images/master/gifs/8a9e.gif
Jisho Uri: http://jisho.org/search/%E8%AA%9E%23kanji Jisho Uri: https://jisho.org/search/%E8%AA%9E%23kanji
``` ```
### Example search ### Example search
@ -82,18 +81,18 @@ import 'dart:convert' show jsonEncode;
import 'package:unofficial_jisho_api/unofficial_jisho_api.dart'; import 'package:unofficial_jisho_api/unofficial_jisho_api.dart';
final jisho = JishoApi(); final jisho = JishoApi();
main() async { void main() async {
jisho.searchForExamples('日').then((result) { await jisho.searchForExamples('日').then((result) {
print('Jisho Uri: ' + result.uri); print('Jisho Uri: ' + result.uri);
print(); print('');
for (int i = 0; i < 3; ++i) { for (int i = 0; i < 3; i++) {
var example = result.results[i]; var example = result.results[i];
print(example.kanji); print(example.kanji);
print(example.kana); print(example.kana);
print(example.english); print(example.english);
print(jsonEncode(example.pieces)); print(jsonEncode(example.pieces));
print(); print('');
} }
}); });
} }
@ -102,24 +101,23 @@ main() async {
This outputs the following: This outputs the following:
``` ```
Jisho Uri: http://jisho.org/search/%E6%97%A5%23sentences Jisho Uri: https://jisho.org/search/%E6%97%A5%23sentences
日本人ならそんなことはけっしてしないでしょう。 日本人ならそんなことはけっしてしないでしょう。
にほんじんならそんなことはけっしてしないでしょう。 にほんじんならそんなことはけっしてしないでしょう。
A Japanese person would never do such a thing. A Japanese person would never do such a thing.
[{"lifted":"にほんじん","unlifted":"日本人"},{"lifted":"","unlifted":"なら"},{"lifted":"","unlifted":"そんな"},{"lifted":"","unlifted":"こと"},{"lifted":"","unlifted":"は"},{"lifted":"","unlifted":"けっして"},{"lifted":"","unlifted":"しない"},{"lifted":""," [{"lifted":"にほんじん","unlifted":"日本人"},{"lifted":null,"unlifted":"なら"},{"lifted":null,"unlifted":"そんな"},{"lifted":null,"unlifted":"こと"},{"lifted":null,"unlifted":"は"},{"lifted":null,"unlifted":"けっして"},{"lifted":null,"unlifted":"しない"},{"lifted":null,"unlifted":"でしょう"}]
unlifted":"でしょう"}]
今日はとても暑い。 今日はとても暑い。
きょうはとてもあつい。 きょうはとてもあつい。
It is very hot today. It is very hot today.
[{"lifted":"きょう","unlifted":"今日"},{"lifted":"","unlifted":"は"},{"lifted":"","unlifted":"とても"},{"lifted":"あつ","unlifted":"暑い"}] [{"lifted":"きょう","unlifted":"今日"},{"lifted":null,"unlifted":"は"},{"lifted":null,"unlifted":"とても"},{"lifted":"あつ","unlifted":"暑い"}]
日本には美しい都市が多い。例えば京都、奈良だ。 日本には美しい都市が多い。例えば京都、奈良だ。
にほんにはうつくしいとしがおおい。たとえばきょうと、奈良だ。 にほんにはうつくしいとしがおおい。たとえばきょうと、奈良だ。
Japan is full of beautiful cities. Kyoto and Nara, for instance. Japan is full of beautiful cities. Kyoto and Nara, for instance.
[{"lifted":"にほん","unlifted":"日本"},{"lifted":"","unlifted":"には"},{"lifted":"うつく","unlifted":"美しい"},{"lifted":"とし","unlifted":"都市"},{"lifted":"","unlifted":"が"},{"lifted":"おお","unlifted":"多い"},{"lifted":"たと","unlifted":"例えば"},{"lift [{"lifted":"にほん","unlifted":"日本"},{"lifted":null,"unlifted":"には"},{"lifted":"うつく","unlifted":"美しい"},{"lifted":"とし","unlifted":"都市"},{"lifted":null,"unlifted":"が"},{"lifted":"おお","unlifted":"多い"},{"lifted":"たと","unlifted":"例えば"},{"lifted":"きょうと","unlifted":"京都"},{"lifted":null,"unlifted":"だ"}]
ed":"きょうと","unlifted":"京都"},{"lifted":"","unlifted":"だ"}]
``` ```
### Word/phrase scraping ### Word/phrase scraping
@ -127,13 +125,14 @@ ed":"きょうと","unlifted":"京都"},{"lifted":"","unlifted":"だ"}]
This scrapes the word/phrase page on Jisho.org. This can get you some data that the official API doesn't have, such as JLPT level and part-of-speech. The official API (`searchForPhrase`) should be preferred if it has the data you need. This scrapes the word/phrase page on Jisho.org. This can get you some data that the official API doesn't have, such as JLPT level and part-of-speech. The official API (`searchForPhrase`) should be preferred if it has the data you need.
```dart ```dart
import 'dart:convert' show jsonEncode; import 'dart:convert';
import 'package:unofficial_jisho_api/unofficial_jisho_api.dart'; import 'package:unofficial_jisho_api/unofficial_jisho_api.dart';
final jisho = JishoApi(); final jisho = JishoApi();
final encoder = JsonEncoder.withIndent(' ');
main() async { void main() async {
jisho.scrapeForPhrase('谷').then((data) { await jisho.scrapeForPhrase('谷').then((data) {
print(jsonEncode(data); print(encoder.convert(data));
}); });
} }
``` ```
@ -156,7 +155,7 @@ This outputs the following:
"sentences": [], "sentences": [],
"definition": "valley", "definition": "valley",
"supplemental": [], "supplemental": [],
"definitionAbstract": "", "definitionAbstract": null,
"tags": [ "tags": [
"noun" "noun"
] ]
@ -186,69 +185,6 @@ This outputs the following:
} }
``` ```
## Parsing HTML strings
You can provide the HTML responses from Jisho yourself. This can be useful if you need to use a CORS proxy or something. You can do whatever you need to do to get the HTML and then provide it to this module's parsing functions. For example:
### Parse kanji page HTML
```dart
import 'package:http/http.dart' as http;
import 'package:unofficial_jisho_api/unofficial_jisho_api.dart';
final jisho = JishoApi();
main() async {
const SEARCH_KANJI = '車';
final SEARCH_URI = jisho.getUriForKanjiSearch(SEARCH_KANJI);
final response = await http.get(SEARCH_URI);
final json = jisho.parseKanjiPageHtml(response.body, SEARCH_KANJI);
print('JLPT level: ${json.jlptLevel}');
print('Stroke count: ${json.strokeCount}');
print('Meaning: ${json.meaning}');
}
```
### Parse example page HTML
```dart
import 'package:http/http.dart' as http;
import 'package:unofficial_jisho_api/unofficial_jisho_api.dart';
final jisho = JishoApi();
main() async {
const SEARCH_EXAMPLE = '保護者';
final SEARCH_URI = jisho.getUriForExampleSearch(SEARCH_EXAMPLE);
final response = await http.get(SEARCH_URI);
final json = jisho.parseExamplePageHtml(response.body, SEARCH_EXAMPLE);
print('English: ${json.results[0].english}');
print('Kanji ${json.results[0].kanji}');
print('Kana: ${json.results[0].kana}');
}
```
### Parse phrase page HTML
```dart
import 'dart:convert' show jsonEncode;
import 'package:http/http.dart' as http;
import 'package:unofficial_jisho_api/unofficial_jisho_api.dart';
final jisho = JishoApi();
main() async {
const SEARCH_EXAMPLE = '保護者';
final SEARCH_URI = jisho.getUriForPhraseScrape(SEARCH_EXAMPLE);
final response = await http.get(SEARCH_URI);
const json = jisho.parsePhraseScrapeHtml(response.body, SEARCH_EXAMPLE);
print(jsonEncode(json, null, 2));
}
```
## About ## About
Permission to scrape granted by Jisho's admin Kimtaro: http://jisho.org/forum/54fefc1f6e73340b1f160000-is-there-any-kind-of-search-api Permission to scrape granted by Jisho's admin Kimtaro: https://jisho.org/forum/54fefc1f6e73340b1f160000-is-there-any-kind-of-search-api

View File

@ -0,0 +1,19 @@
import 'dart:convert' show jsonEncode;
import 'package:unofficial_jisho_api/unofficial_jisho_api.dart';
final jisho = JishoApi();
void main() async {
await jisho.searchForExamples('').then((result) {
print('Jisho Uri: ' + result.uri);
print('');
for (int i = 0; i < 3; i++) {
var example = result.results[i];
print(example.kanji);
print(example.kana);
print(example.english);
print(jsonEncode(example.pieces));
print('');
}
});
}

View File

@ -0,0 +1,24 @@
import 'dart:convert' show jsonEncode;
import 'package:unofficial_jisho_api/unofficial_jisho_api.dart';
final jisho = JishoApi();
void main() async {
await jisho.searchForKanji('').then((result) {
print('Found: ' + result.found.toString());
print('Taught in: ' + result.taughtIn);
print('JLPT level: ' + result.jlptLevel);
print('Newspaper frequency rank: ' + result.newspaperFrequencyRank.toString());
print('Stroke count: ' + result.strokeCount.toString());
print('Meaning: ' + result.meaning);
print('Kunyomi: ' + jsonEncode(result.kunyomi));
print('Kunyomi example: ' + jsonEncode(result.kunyomiExamples[0]));
print('Onyomi: ' + jsonEncode(result.onyomi));
print('Onyomi example: ' + jsonEncode(result.onyomiExamples[0]));
print('Radical: ' + jsonEncode(result.radical));
print('Parts: ' + jsonEncode(result.parts));
print('Stroke order diagram: ' + result.strokeOrderDiagramUri);
print('Stroke order SVG: ' + result.strokeOrderSvgUri);
print('Stroke order GIF: ' + result.strokeOrderGifUri);
print('Jisho Uri: ' + result.uri);
});
}

View File

@ -0,0 +1,10 @@
import 'dart:convert';
import 'package:unofficial_jisho_api/unofficial_jisho_api.dart';
final jisho = JishoApi();
final encoder = JsonEncoder.withIndent(' ');
void main() async {
await jisho.scrapeForPhrase('').then((data) {
print(encoder.convert(data));
});
}

View File

@ -0,0 +1,10 @@
import 'dart:convert';
import 'package:unofficial_jisho_api/unofficial_jisho_api.dart';
final jisho = JishoApi();
final encoder = JsonEncoder.withIndent(' ');
void main() async {
await jisho.searchForPhrase('反対').then((result) {
print(encoder.convert(result));
});
}

View File

@ -1,4 +0,0 @@
import 'package:unofficial_jisho_api/unofficial_jisho_api.dart';
void main() {
}

3
lib/src/baseURI.dart Normal file
View File

@ -0,0 +1,3 @@
const String JISHO_API = 'https://jisho.org/api/v1/search/words';
const String SCRAPE_BASE_URI = 'https://jisho.org/search/';
const String STROKE_ORDER_DIAGRAM_BASE_URI = 'https://classic.jisho.org/static/images/stroke_diagrams/';

123
lib/src/exampleSearch.dart Normal file
View File

@ -0,0 +1,123 @@
import './baseURI.dart';
import './objects.dart';
import 'package:html/parser.dart';
import 'package:html/dom.dart';
final RegExp kanjiRegex = RegExp(r'[\u4e00-\u9faf\u3400-\u4dbf]');
String uriForExampleSearch(String phrase) {
return '${SCRAPE_BASE_URI}${Uri.encodeComponent(phrase)}%23sentences';
}
List<Element> getChildrenAndSymbols(Element ul) {
final ulText = ul.text;
final ulCharArray = ulText.split('');
final ulChildren = ul.children;
var offsetPointer = 0;
List<Element> result = [];
for (var element in ulChildren) {
if (element.text != ulText.substring(offsetPointer, offsetPointer + element.text.length)){
var symbols = '';
while (element.text.substring(0,1) != ulCharArray[offsetPointer]) {
symbols += ulCharArray[offsetPointer];
offsetPointer++;
}
final symbolElement = Element.html('<span>${symbols}</span>');
result.add(symbolElement);
}
offsetPointer += element.text.length;
result.add(element);
}
if (offsetPointer + 1 != ulText.length){
final symbols = ulText.substring(offsetPointer, ulText.length-1);
final symbolElement = Element.html('<span>${symbols}</span>');
result.add(symbolElement);
}
return result;
}
ExampleResultData getKanjiAndKana(Element div) {
final ul = div.querySelector('ul');
final contents = getChildrenAndSymbols(ul);
var kanji = '';
var kana = '';
for (var i = 0; i < contents.length; i += 1) {
final content = contents[i];
if (content.localName == 'li') {
final li = content;
final furigana = li.querySelector('.furigana')?.text;
final unlifted = li.querySelector('.unlinked')?.text;
if (furigana != null) {
kanji += unlifted;
kana += furigana;
final kanaEnding = [];
for (var j = unlifted.length - 1; j > 0; j -= 1) {
final char = unlifted[j];
if (!kanjiRegex.hasMatch(char)) {
kanaEnding.add(char);
} else {
break;
}
}
kana += kanaEnding.reversed.join('');
} else {
kanji += unlifted;
kana += unlifted;
}
} else {
final text = content.text.trim();
if (text != null) {
kanji += text;
kana += text;
}
}
}
return ExampleResultData(
kanji: kanji,
kana: kana,
);
}
List<ExampleSentencePiece> getPieces(Element sentenceElement) {
final pieceElements = sentenceElement.querySelectorAll('li.clearfix');
final List<ExampleSentencePiece> pieces = [];
for (var pieceIndex = 0; pieceIndex < pieceElements.length; pieceIndex += 1) {
final pieceElement = pieceElements[pieceIndex];
pieces.add(ExampleSentencePiece(
lifted: pieceElement.querySelector('.furigana')?.text,
unlifted: pieceElement.querySelector('.unlinked')?.text,
));
}
return pieces;
}
ExampleResultData parseExampleDiv(Element div) {
final result = getKanjiAndKana(div);
result.english = div.querySelector('.english').text;
result.pieces = getPieces(div) ?? [];
return result;
}
ExampleResults parseExamplePageData(String pageHtml, String phrase) {
final document = parse(pageHtml);
final divs = document.querySelectorAll('.sentence_content');
final results = divs.map((div) => parseExampleDiv(div)).toList();
return ExampleResults(
query: phrase,
found: results.isNotEmpty,
results: results ?? [],
uri: uriForExampleSearch(phrase),
phrase: phrase,
);
}

214
lib/src/kanjiSearch.dart Normal file
View File

@ -0,0 +1,214 @@
import './baseURI.dart';
import './objects.dart';
import 'package:html_unescape/html_unescape.dart' as html_entities;
final htmlUnescape = html_entities.HtmlUnescape();
const String ONYOMI_LOCATOR_SYMBOL = 'On';
const String KUNYOMI_LOCATOR_SYMBOL = 'Kun';
String removeNewlines(String str) {
return str.replaceAll(RegExp(r'(?:\r|\n)') , '').trim();
}
String uriForKanjiSearch(String kanji) {
return '${SCRAPE_BASE_URI}${Uri.encodeComponent(kanji)}%23kanji';
}
String getUriForStrokeOrderDiagram(String kanji) {
return '${STROKE_ORDER_DIAGRAM_BASE_URI}${kanji.codeUnitAt(0)}_frames.png';
}
bool containsKanjiGlyph(String pageHtml, String kanji) {
final kanjiGlyphToken = '<h1 class="character" data-area-name="print" lang="ja">${kanji}</h1>';
return pageHtml.contains(kanjiGlyphToken);
}
String getStringBetweenIndicies(String data, int startIndex, int endIndex) {
final result = data.substring(startIndex, endIndex);
return removeNewlines(result).trim();
}
String getStringBetweenStrings(String data, String startString, String endString) {
final regex = RegExp('${RegExp.escape(startString)}(.*?)${RegExp.escape(endString)}', dotAll: true);
final match = regex.allMatches(data).toList();
return match.isNotEmpty ? match[0].group(1).toString() : null;
}
int getIntBetweenStrings(String pageHtml, String startString, String endString) {
final stringBetweenStrings = getStringBetweenStrings(pageHtml, startString, endString);
return int.parse(stringBetweenStrings);
}
List<String> getAllGlobalGroupMatches(String str, RegExp regex) {
var regexResults = regex.allMatches(str).toList();
List<String> results = [];
for (var match in regexResults) {
results.add(match.group(1));
}
return results;
}
List<String> parseAnchorsToArray(String str) {
final regex = RegExp(r'<a href=".*?">(.*?)<\/a>');
return getAllGlobalGroupMatches(str, regex);
}
List<String> getYomi(String pageHtml, String yomiLocatorSymbol) {
final yomiSection = getStringBetweenStrings(pageHtml, '<dt>${yomiLocatorSymbol}:</dt>', '</dl>');
return parseAnchorsToArray(yomiSection ?? '');
}
List<String> getKunyomi(String pageHtml) {
return getYomi(pageHtml, KUNYOMI_LOCATOR_SYMBOL);
}
List<String> getOnyomi(String pageHtml) {
return getYomi(pageHtml, ONYOMI_LOCATOR_SYMBOL);
}
List<YomiExample> getYomiExamples(String pageHtml, String yomiLocatorSymbol) {
final locatorString = '<h2>${yomiLocatorSymbol} reading compounds</h2>';
final exampleSection = getStringBetweenStrings(pageHtml, locatorString, '</ul>');
if (exampleSection==null) {
return null;
}
final regex = RegExp(r'<li>(.*?)<\/li>', dotAll: true);
final regexResults = getAllGlobalGroupMatches(exampleSection, regex).map((s) => s.trim());
final examples = regexResults.map((regexResult) {
final examplesLines = regexResult.split('\n').map((s) => s.trim()).toList();
return YomiExample(
example: examplesLines[0],
reading: examplesLines[1].replaceAll('', '').replaceAll('', ''),
meaning: htmlUnescape.convert(examplesLines[2]),
);
});
return examples.toList();
}
List<YomiExample> getOnyomiExamples(String pageHtml) {
return getYomiExamples(pageHtml, ONYOMI_LOCATOR_SYMBOL);
}
List<YomiExample> getKunyomiExamples(String pageHtml) {
return getYomiExamples(pageHtml, KUNYOMI_LOCATOR_SYMBOL);
}
Radical getRadical(String pageHtml) {
const radicalMeaningStartString = '<span class="radical_meaning">';
const radicalMeaningEndString = '</span>';
var radicalMeaning = getStringBetweenStrings(
pageHtml,
radicalMeaningStartString,
radicalMeaningEndString,
).trim();
if (radicalMeaning!=null) {
final radicalMeaningStartIndex = pageHtml.indexOf(radicalMeaningStartString);
final radicalMeaningEndIndex = pageHtml.indexOf(
radicalMeaningEndString,
radicalMeaningStartIndex,
);
final radicalSymbolStartIndex = radicalMeaningEndIndex + radicalMeaningEndString.length;
const radicalSymbolEndString = '</span>';
final radicalSymbolEndIndex = pageHtml.indexOf(radicalSymbolEndString, radicalSymbolStartIndex);
final radicalSymbolsString = getStringBetweenIndicies(
pageHtml,
radicalSymbolStartIndex,
radicalSymbolEndIndex,
);
if (radicalSymbolsString.length > 1) {
final radicalForms = radicalSymbolsString
.substring(1)
.replaceAll('(', '')
.replaceAll(')', '')
.trim()
.split(', ');
return Radical(
symbol: radicalSymbolsString[0],
forms: radicalForms ?? [],
meaning: radicalMeaning
);
}
return Radical (
symbol: radicalSymbolsString,
meaning: radicalMeaning
);
}
return null;
}
List<String> getParts(String pageHtml) {
const partsSectionStartString = '<dt>Parts:</dt>';
const partsSectionEndString = '</dl>';
final partsSection = getStringBetweenStrings(
pageHtml,
partsSectionStartString,
partsSectionEndString,
);
var result = parseAnchorsToArray(partsSection);
result.sort();
return (result);
}
String getSvgUri(String pageHtml) {
var svgRegex = RegExp('\/\/.*?.cloudfront.net\/.*?.svg');
final regexResult = svgRegex.firstMatch(pageHtml).group(0).toString();
return regexResult.isNotEmpty ? 'https:${regexResult}' : null;
}
String getGifUri(String kanji) {
final unicodeString = kanji.codeUnitAt(0).toRadixString(16);
final fileName = '${unicodeString}.gif';
final animationUri = 'https://raw.githubusercontent.com/mistval/kanji_images/master/gifs/${fileName}';
return animationUri;
}
int getNewspaperFrequencyRank(String pageHtml) {
final frequencySection = getStringBetweenStrings(pageHtml, '<div class="frequency">', '</div>');
return (frequencySection != null) ? int.parse(getStringBetweenStrings(frequencySection, '<strong>', '</strong>')) : null;
}
KanjiResult parseKanjiPageData(String pageHtml, String kanji) {
final result = KanjiResult();
result.query = kanji;
result.found = containsKanjiGlyph(pageHtml, kanji);
if (result.found==false) {
return result;
}
result.taughtIn = getStringBetweenStrings(pageHtml, 'taught in <strong>', '</strong>');
result.jlptLevel = getStringBetweenStrings(pageHtml, 'JLPT level <strong>', '</strong>');
result.newspaperFrequencyRank = getNewspaperFrequencyRank(pageHtml);
result.strokeCount = getIntBetweenStrings(pageHtml, '<strong>', '</strong> strokes');
result.meaning = htmlUnescape.convert(removeNewlines(getStringBetweenStrings(pageHtml, '<div class="kanji-details__main-meanings">', '</div>')).trim());
result.kunyomi = getKunyomi(pageHtml) ?? [];
result.onyomi = getOnyomi(pageHtml) ?? [];
result.onyomiExamples = getOnyomiExamples(pageHtml) ?? [];
result.kunyomiExamples = getKunyomiExamples(pageHtml) ?? [];
result.radical = getRadical(pageHtml);
result.parts = getParts(pageHtml) ?? [];
result.strokeOrderDiagramUri = getUriForStrokeOrderDiagram(kanji);
result.strokeOrderSvgUri = getSvgUri(pageHtml);
result.strokeOrderGifUri = getGifUri(kanji);
result.uri = uriForKanjiSearch(kanji);
return result;
}

View File

@ -1,80 +1,17 @@
class PhraseScrapeSentence { /* -------------------------------------------------------------------------- */
String english; /* searchForKanji related classes */
String japanese; /* -------------------------------------------------------------------------- */
List<ExampleSentencePiece> pieces;
PhraseScrapeSentence ({String english, String japanese, List<ExampleSentencePiece> pieces}){
this.english = english;
this.japanese = japanese;
this.pieces = pieces;
}
}
class PhraseScrapeMeaning {
List<String> seeAlsoTerms;
List<PhraseScrapeSentence> sentences;
String definition;
List<String> supplemental;
String definitionAbstract;
List<String> tags;
PhraseScrapeMeaning({
List<String> seeAlsoTerms,
List<PhraseScrapeSentence> sentences,
String definition,
List<String> supplemental,
String definitionAbstract,
List<String> tags,
}){
this.seeAlsoTerms = seeAlsoTerms;
this.sentences = sentences;
this.definition = definition;
this.supplemental = supplemental;
this.definitionAbstract = definitionAbstract;
this.tags = tags;
}
}
class PhrasePageScrapeResult {
bool found;
String query;
String uri;
List<String> otherForms;
List<PhraseScrapeMeaning> meanings;
List<String> tags;
List<String> notes;
PhrasePageScrapeResult({
bool found,
String query,
String uri,
List<String> otherForms,
List<PhraseScrapeMeaning> meanings,
List<String> tags,
List<String> notes,
}){
this.found = found;
this.query = query;
this.uri = uri;
this.otherForms = otherForms;
this.meanings = meanings;
this.tags = tags;
this.notes = notes;
}
}
class YomiExample { class YomiExample {
String example; String example;
String reading; String reading;
String meaning; String meaning;
YomiExample({String example, String reading, String meaning}) YomiExample({
{ this.example,
this.example = example; this.reading,
this.reading = reading; this.meaning
this.meaning = meaning; });
}
Map<String, String> toJson() => Map<String, String> toJson() =>
{ {
@ -90,17 +27,17 @@ class Radical {
List<String> forms; List<String> forms;
String meaning; String meaning;
Radical({String symbol, List<String> forms, String meaning}){ Radical({
this.symbol = symbol; this.symbol,
this.forms = forms; this.forms,
this.meaning = meaning; this.meaning
} });
Map<String, dynamic> toJson() => Map<String, dynamic> toJson() =>
{ {
if (symbol != null) 'symbol': symbol, 'symbol': symbol,
if (forms != null) 'forms': forms, 'forms': forms,
if (meaning != null) 'meaning': meaning 'meaning': meaning
}; };
} }
@ -125,26 +62,40 @@ class KanjiResult {
String strokeOrderGifUri; String strokeOrderGifUri;
String uri; String uri;
KanjiResult({
this.query,
this.found,
this.taughtIn,
this.jlptLevel,
this.newspaperFrequencyRank,
this.strokeCount,
this.meaning,
this.kunyomi,
this.onyomi,
this.kunyomiExamples,
this.onyomiExamples,
this.radical,
this.parts,
this.strokeOrderDiagramUri,
this.strokeOrderSvgUri,
this.strokeOrderGifUri,
this.uri
});
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
if (found == false) return {
'query': query,
'found': found
};
return { return {
'query': query, 'query': query,
'found': found, 'found': found,
'taughtIn': taughtIn, 'taughtIn': taughtIn,
'jlptLevel': jlptLevel, 'jlptLevel': jlptLevel,
'newspaperFrequencyRank': newspaperFrequencyRank.toString(), 'newspaperFrequencyRank': newspaperFrequencyRank,
'strokeCount': strokeCount, 'strokeCount': strokeCount,
'meaning': meaning, 'meaning': meaning,
'kunyomi': kunyomi, 'kunyomi': kunyomi,
'onyomi': onyomi, 'onyomi': onyomi,
'onyomiExamples': onyomiExamples.map((onyomiExample) => onyomiExample.toJson()).toList(), 'onyomiExamples': onyomiExamples,
'kunyomiExamples': kunyomiExamples.map((kunyomiExample) => kunyomiExample.toJson()).toList(), 'kunyomiExamples': kunyomiExamples,
'radical': radical.toJson(), 'radical': (radical != null) ? radical.toJson() : null,
'parts': parts, 'parts': parts,
'strokeOrderDiagramUri': strokeOrderDiagramUri, 'strokeOrderDiagramUri': strokeOrderDiagramUri,
'strokeOrderSvgUri': strokeOrderSvgUri, 'strokeOrderSvgUri': strokeOrderSvgUri,
@ -154,13 +105,24 @@ class KanjiResult {
} }
} }
class ExampleSentencePiece { /* -------------------------------------------------------------------------- */
String unlifted; /* searchForExamples related classes */
String lifted; /* -------------------------------------------------------------------------- */
ExampleSentencePiece({String unlifted, String lifted}){ class ExampleSentencePiece {
this.unlifted = unlifted; String lifted;
this.lifted = lifted; String unlifted;
ExampleSentencePiece({
this.lifted,
this.unlifted
});
Map<String, dynamic> toJson() {
return {
'lifted': lifted,
'unlifted': unlifted
};
} }
} }
@ -170,11 +132,20 @@ class ExampleResultData {
String english; String english;
List<ExampleSentencePiece> pieces; List<ExampleSentencePiece> pieces;
ExampleResultData({String kanji, String kana, String english, List<ExampleSentencePiece> pieces}){ ExampleResultData({
this.kanji = kanji; this.english,
this.kana = kana; this.kanji,
this.english = english; this.kana,
this.pieces = pieces; this.pieces
});
Map<String, dynamic> toJson() {
return {
'english': english,
'kanji': kanji,
'kana': kana,
'pieces': pieces
};
} }
} }
@ -185,15 +156,310 @@ class ExampleResults {
List<ExampleResultData> results; List<ExampleResultData> results;
String phrase; String phrase;
ExampleResults({String query, bool found, String uri, List<ExampleResultData> results, String phrase}){ ExampleResults({
this.query = query; this.query,
this.found = found; this.found,
this.uri = uri; this.results,
this.results = results; this.uri,
this.phrase = phrase; this.phrase
});
Map<String, dynamic> toJson() {
return {
'query': query,
'found': found,
'results': results,
'uri': uri,
'phrase': phrase
};
} }
} }
/* -------------------------------------------------------------------------- */
/* scrapeForPhrase related classes */
/* -------------------------------------------------------------------------- */
class PhraseScrapeSentence {
String english;
String japanese;
List<ExampleSentencePiece> pieces;
PhraseScrapeSentence ({
this.english,
this.japanese,
this.pieces
});
Map<String, dynamic> toJson() => {
'english': english,
'japanese': japanese,
'pieces': pieces
};
}
class PhraseScrapeMeaning {
List<String> seeAlsoTerms;
List<PhraseScrapeSentence> sentences;
String definition;
List<String> supplemental;
String definitionAbstract;
List<String> tags;
PhraseScrapeMeaning({
this.seeAlsoTerms,
this.sentences,
this.definition,
this.supplemental,
this.definitionAbstract,
this.tags
});
Map<String, dynamic> toJson() => {
'seeAlsoTerms': seeAlsoTerms,
'sentences': sentences,
'definition': definition,
'supplemental': supplemental,
'definitionAbstract': definitionAbstract,
'tags': tags
};
}
class KanjiKanaPair {
String kanji;
String kana;
KanjiKanaPair({
this.kanji,
this.kana
});
Map<String, String> toJson() => {
'kanji': kanji,
'kana': kana
};
}
class PhrasePageScrapeResult {
bool found;
String query;
String uri;
List<String> tags;
List<PhraseScrapeMeaning> meanings;
List<KanjiKanaPair> otherForms;
List<String> notes;
PhrasePageScrapeResult({
this.found,
this.query,
this.uri,
this.tags,
this.meanings,
this.otherForms,
this.notes
});
Map<String, dynamic> toJson() => {
'found': found,
'query': query,
'uri': uri,
'tags': tags,
'meanings': meanings,
'otherForms': otherForms,
'notes': notes
};
}
/* -------------------------------------------------------------------------- */
/* searchForPhrase related classes */
/* -------------------------------------------------------------------------- */
class JishoJapaneseWord {
String word;
String reading;
JishoJapaneseWord({this.word, this.reading});
factory JishoJapaneseWord.fromJson(Map<String, dynamic> json){
return JishoJapaneseWord(
word: json['word'] as String,
reading: json['reading'] as String
);
}
Map<String, dynamic> toJson() => {
'word': word,
'reading': reading
};
}
class JishoSenseLink {
String text;
String url;
JishoSenseLink({this.text, this.url});
factory JishoSenseLink.fromJson(Map<String, dynamic> json){
return JishoSenseLink(
text: json['text'] as String,
url: json['url'] as String
);
}
Map<String, dynamic> toJson() => {
'text': text,
'url': url
};
}
class JishoWordSense {
List<String> english_definitions;
List<String> parts_of_speech;
List<JishoSenseLink> links;
List<String> tags;
List<String> see_also;
List<String> antonyms;
List<dynamic> source;
List<String> info;
List<dynamic> restrictions;
JishoWordSense({
this.english_definitions,
this.parts_of_speech,
this.links,
this.tags,
this.see_also,
this.antonyms,
this.source,
this.info,
this.restrictions
});
factory JishoWordSense.fromJson(Map<String, dynamic> json){
return JishoWordSense(
english_definitions: (json['english_definitions'] as List).map((result) => result as String).toList(),
parts_of_speech: (json['parts_of_speech'] as List).map((result) => result as String).toList(),
links: (json['links'] as List).map((result) => JishoSenseLink.fromJson(result)).toList(),
tags: (json['tags'] as List).map((result) => result as String).toList(),
see_also: (json['see_also'] as List).map((result) => result as String).toList(),
antonyms: (json['antonyms'] as List).map((result) => result as String).toList(),
source: json['source'] as List<dynamic>,
info: (json['info'] as List).map((result) => result as String).toList(),
restrictions: json['restrictions'] as List<dynamic>
);
}
Map<String, dynamic> toJson() => {
'english_definitions': english_definitions,
'parts_of_speech': parts_of_speech,
'links': links,
'tags': tags,
'see_also': see_also,
'antonyms': antonyms,
'source': source,
'info': info,
'restrictions': restrictions
};
}
class JishoAttribution {
bool jmdict;
bool jmnedict;
String dbpedia;
JishoAttribution({
this.jmdict,
this.jmnedict,
this.dbpedia
});
factory JishoAttribution.fromJson(Map<String, dynamic> json){
return JishoAttribution(
jmdict: (json['jmdict'].toString() == 'true'),
jmnedict: (json['jmnedict'].toString() == 'true'),
dbpedia: (json['dbpedia'].toString() != 'false') ? json['dbpedia'].toString() : null
);
}
Map<String, dynamic> toJson() => {
'jmdict': jmdict,
'jmnedict': jmnedict,
'dbpedia': dbpedia
};
}
class JishoResult {
String slug;
bool is_common;
List<String> tags;
List<String> jlpt;
List<JishoJapaneseWord> japanese;
List<JishoWordSense> senses;
JishoAttribution attribution;
JishoResult({
this.slug,
this.is_common,
this.tags,
this.jlpt,
this.japanese,
this.senses,
this.attribution
});
factory JishoResult.fromJson(Map<String, dynamic> json){
return JishoResult(
slug: json['slug'] as String,
is_common: json['is_common'] as bool,
tags: (json['tags'] as List).map((result) => result as String).toList(),
jlpt: (json['jlpt'] as List).map((result) => result as String).toList(),
japanese: (json['japanese'] as List).map((result) => JishoJapaneseWord.fromJson(result)).toList(),
senses: (json['senses'] as List).map((result) => JishoWordSense.fromJson(result)).toList(),
attribution: JishoAttribution.fromJson(json['attribution'])
);
}
Map<String, dynamic> toJson() => {
'slug': slug,
'is_common': is_common,
'tags': tags,
'jlpt': jlpt,
'japanese': japanese,
'senses': senses,
'attribution': attribution
};
}
class JishoResultMeta {
int status;
JishoResultMeta({this.status});
factory JishoResultMeta.fromJson(Map<String, dynamic> json){
return JishoResultMeta(
status: json['status'] as int
);
}
Map<String, dynamic> toJson() => {
'status': status
};
}
class JishoAPIResult {
JishoResultMeta meta;
List<JishoResult> data;
JishoAPIResult({this.meta, this.data});
factory JishoAPIResult.fromJson(Map<String, dynamic> json){
return JishoAPIResult(
meta: JishoResultMeta.fromJson(json['meta']),
data: (json['data'] as List).map((result) => JishoResult.fromJson(result)).toList()
);
}
Map<String, dynamic> toJson() => {
'meta': meta.toJson(),
'data': data
};
}

154
lib/src/phraseScrape.dart Normal file
View File

@ -0,0 +1,154 @@
import './objects.dart';
import './exampleSearch.dart';
import 'package:html/parser.dart';
import 'package:html/dom.dart';
List<String> getTags(Document document) {
final List<String> tags = [];
final tagElements = document.querySelectorAll('.concept_light-tag');
for (var i = 0; i < tagElements.length; i += 1) {
final tagText = tagElements[i].text;
tags.add(tagText);
}
return tags;
}
List<String> getMostRecentWordTypes(Element child) {
return child.text.split(',').map((s) => s.trim().toLowerCase()).toList();
}
List<KanjiKanaPair> getOtherForms(Element child) {
return child.text.split('')
.map((s) => s.replaceAll('', '').replaceAll('', '').split(' '))
.map((a) => (KanjiKanaPair( kanji: a[0], kana: (a.length == 2) ? a[1] : null ))).toList();
}
List<String> getNotes(Element child) => child.text.split('\n');
String getMeaning(Element child) => child.querySelector('.meaning-meaning').text;
String getMeaningAbstract(Element child) {
final meaningAbstract = child.querySelector('.meaning-abstract');
if (meaningAbstract == null) return null;
for (var element in meaningAbstract.querySelectorAll('a')) {
element.remove();
}
return child.querySelector('.meaning-abstract')?.text;
}
List<String> getSupplemental(Element child) {
final supplemental = child.querySelector('.supplemental_info');
if (supplemental == null) return [];
return supplemental.text.split(',').map((s) => s.trim()).toList();
}
List<String> getSeeAlsoTerms(List<String> supplemental) {
if (supplemental == null) return [];
final List<String> seeAlsoTerms = [];
for (var i = supplemental.length - 1; i >= 0; i -= 1) {
final supplementalEntry = supplemental[i];
if (supplementalEntry.startsWith('See also')) {
seeAlsoTerms.add(supplementalEntry.replaceAll('See also ', ''));
supplemental.removeAt(i);
}
}
return seeAlsoTerms;
}
List<PhraseScrapeSentence> getSentences(Element child) {
final sentenceElements = child.querySelector('.sentences')?.querySelectorAll('.sentence');
if (sentenceElements == null) return [];
final List<PhraseScrapeSentence> sentences = [];
for (var sentenceIndex = 0; sentenceIndex < (sentenceElements?.length ?? 0); sentenceIndex += 1) {
final sentenceElement = sentenceElements[sentenceIndex];
final english = sentenceElement.querySelector('.english').text;
final pieces = getPieces(sentenceElement);
sentenceElement.querySelector('.english').remove();
for (var element in sentenceElement.children[0].children) {
element.querySelector('.furigana')?.remove();
}
final japanese = sentenceElement.text;
sentences.add(
PhraseScrapeSentence(
english: english,
japanese: japanese,
pieces: pieces ?? []
)
);
}
return sentences;
}
PhrasePageScrapeResult getMeaningsOtherFormsAndNotes(Document document) {
final returnValues = PhrasePageScrapeResult( otherForms: [], notes: [] );
final meaningsWrapper = document.querySelector('.meanings-wrapper');
if (meaningsWrapper == null) return PhrasePageScrapeResult(found: false);
returnValues.found = true;
final meaningsChildren = meaningsWrapper.children;
final List<PhraseScrapeMeaning> meanings = [];
var mostRecentWordTypes = [];
for (var meaningIndex = 0; meaningIndex < meaningsChildren.length; meaningIndex += 1) {
final child = meaningsChildren[meaningIndex];
if (child.className.contains('meaning-tags')) {
mostRecentWordTypes = getMostRecentWordTypes(child);
} else if (mostRecentWordTypes[0] == 'other forms') {
returnValues.otherForms = getOtherForms(child);
} else if (mostRecentWordTypes[0] == 'notes') {
returnValues.notes = getNotes(child);
} else {
final meaning = getMeaning(child);
final meaningAbstract = getMeaningAbstract(child);
final supplemental = getSupplemental(child);
final seeAlsoTerms = getSeeAlsoTerms(supplemental);
final sentences = getSentences(child);
meanings.add(PhraseScrapeMeaning(
seeAlsoTerms: seeAlsoTerms ?? [],
sentences: sentences ?? [],
definition: meaning,
supplemental: supplemental ?? [],
definitionAbstract: meaningAbstract,
tags: mostRecentWordTypes ?? [],
));
}
}
returnValues.meanings = meanings;
return returnValues;
}
String uriForPhraseScrape(String searchTerm) {
return 'https://jisho.org/word/${Uri.encodeComponent(searchTerm)}';
}
PhrasePageScrapeResult parsePhrasePageData(String pageHtml, String query) {
final document = parse(pageHtml);
final result = getMeaningsOtherFormsAndNotes(document);
result.query = query;
if (!result.found) return result;
result.uri = uriForPhraseScrape(query);
result.tags = getTags(document);
return result;
}

View File

@ -0,0 +1,5 @@
import './baseURI.dart';
String uriForPhraseSearch(String phrase) {
return '${JISHO_API}?keyword=${Uri.encodeComponent(phrase)}';
}

View File

@ -1,437 +1,12 @@
import 'package:unofficial_jisho_api/src/objects.dart'; import './objects.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'package:xml/xml.dart' as xml;
import 'package:html_unescape/html_unescape.dart' as html_entities;
import 'dart:convert'; import 'dart:convert';
final htmlUnescape = html_entities.HtmlUnescape(); import './phraseSearch.dart';
import './kanjiSearch.dart';
import './exampleSearch.dart';
import './phraseScrape.dart';
// TODO: Put public facing types in this file.
const String JISHO_API = 'http://jisho.org/api/v1/search/words';
const String SCRAPE_BASE_URI = 'http://jisho.org/search/';
const String STROKE_ORDER_DIAGRAM_BASE_URI = 'http://classic.jisho.org/static/images/stroke_diagrams/';
/* KANJI SEARCH FUNCTIONS START */
const String ONYOMI_LOCATOR_SYMBOL = 'On';
const KUNYOMI_LOCATOR_SYMBOL = 'Kun';
String removeNewlines(String str) {
return str.replaceAll(RegExp(r'(?:\r|\n)') , '').trim();
}
String uriForKanjiSearch(String kanji) {
return '${SCRAPE_BASE_URI}${Uri.encodeComponent(kanji)}%23kanji';
}
String getUriForStrokeOrderDiagram(String kanji) {
return '${STROKE_ORDER_DIAGRAM_BASE_URI}${kanji.codeUnitAt(0)}_frames.png';
}
String uriForPhraseSearch(String phrase) {
return '${JISHO_API}?keyword=${Uri.encodeComponent(phrase)}';
}
bool containsKanjiGlyph(String pageHtml, String kanji) {
final kanjiGlyphToken = '<h1 class="character" data-area-name="print" lang="ja">${kanji}</h1>';
return pageHtml.contains(kanjiGlyphToken);
}
String getStringBetweenIndicies(String data, int startIndex, int endIndex) {
final result = data.substring(startIndex, endIndex);
return removeNewlines(result).trim();
}
String getStringBetweenStrings(String data, String startString, String endString) {
final regex = RegExp('${RegExp.escape(startString)}(.*?)${RegExp.escape(endString)}', dotAll: true);
final match = regex.allMatches(data).toList(); //TODO: Something wrong here
return match.isNotEmpty ? match[0].group(1).toString() : null;
}
int getIntBetweenStrings(String pageHtml, String startString, String endString) {
final stringBetweenStrings = getStringBetweenStrings(pageHtml, startString, endString);
return int.parse(stringBetweenStrings);
}
List<String> getAllGlobalGroupMatches(String str, RegExp regex) {
var regexResults = regex.allMatches(str).toList();
List<String> results = [];
for (var match in regexResults) {
results.add(match.group(1));
}
return results;
}
List<String> parseAnchorsToArray(String str) {
final regex = RegExp(r'<a href=".*?">(.*?)<\/a>');
return getAllGlobalGroupMatches(str, regex);
}
List<String> getYomi(String pageHtml, String yomiLocatorSymbol) {
final yomiSection = getStringBetweenStrings(pageHtml, '<dt>${yomiLocatorSymbol}:</dt>', '</dl>');
return parseAnchorsToArray(yomiSection ?? '');
}
List<String> getKunyomi(String pageHtml) {
return getYomi(pageHtml, KUNYOMI_LOCATOR_SYMBOL);
}
List<String> getOnyomi(String pageHtml) {
return getYomi(pageHtml, ONYOMI_LOCATOR_SYMBOL);
}
List<YomiExample> getYomiExamples(String pageHtml, String yomiLocatorSymbol) {
final locatorString = '<h2>${yomiLocatorSymbol} reading compounds</h2>';
final exampleSection = getStringBetweenStrings(pageHtml, locatorString, '</ul>');
if (exampleSection==null) {
return null;
}
final regex = RegExp(r'<li>(.*?)<\/li>', dotAll: true);
final regexResults = getAllGlobalGroupMatches(exampleSection, regex).map((s) => s.trim());
final examples = regexResults.map((regexResult) {
final examplesLines = regexResult.split('\n').map((s) => s.trim()).toList();
return YomiExample(
example: examplesLines[0],
reading: examplesLines[1].replaceAll('', '').replaceAll('', ''),
meaning: htmlUnescape.convert(examplesLines[2]),
);
});
return examples.toList();
}
List<YomiExample> getOnyomiExamples(String pageHtml) {
return getYomiExamples(pageHtml, ONYOMI_LOCATOR_SYMBOL);
}
List<YomiExample> getKunyomiExamples(String pageHtml) {
return getYomiExamples(pageHtml, KUNYOMI_LOCATOR_SYMBOL);
}
Radical getRadical(String pageHtml) {
const radicalMeaningStartString = '<span class="radical_meaning">';
const radicalMeaningEndString = '</span>';
var radicalMeaning = getStringBetweenStrings(
pageHtml,
radicalMeaningStartString,
radicalMeaningEndString,
).trim();
if (radicalMeaning!=null) {
final radicalMeaningStartIndex = pageHtml.indexOf(radicalMeaningStartString);
final radicalMeaningEndIndex = pageHtml.indexOf(
radicalMeaningEndString,
radicalMeaningStartIndex,
);
final radicalSymbolStartIndex = radicalMeaningEndIndex + radicalMeaningEndString.length;
const radicalSymbolEndString = '</span>';
final radicalSymbolEndIndex = pageHtml.indexOf(radicalSymbolEndString, radicalSymbolStartIndex);
final radicalSymbolsString = getStringBetweenIndicies(
pageHtml,
radicalSymbolStartIndex,
radicalSymbolEndIndex,
);
if (radicalSymbolsString.length > 1) {
final radicalForms = radicalSymbolsString
.substring(1)
.replaceAll('(', '')
.replaceAll(')', '')
.trim()
.split(', ');
return Radical(
symbol: radicalSymbolsString[0],
forms: radicalForms,
meaning: radicalMeaning
);
}
return Radical (
symbol: radicalSymbolsString,
meaning: radicalMeaning
);
}
return null;
}
List<String> getParts(String pageHtml) {
const partsSectionStartString = '<dt>Parts:</dt>';
const partsSectionEndString = '</dl>';
final partsSection = getStringBetweenStrings(
pageHtml,
partsSectionStartString,
partsSectionEndString,
);
var result = parseAnchorsToArray(partsSection);
result.sort();
return (result);
}
String getSvgUri(String pageHtml) {
var svgRegex = RegExp('\/\/.*?.cloudfront.net\/.*?.svg');
final regexResult = svgRegex.firstMatch(pageHtml).group(0).toString();
return regexResult.isNotEmpty ? 'http:${regexResult}' : null;
}
String getGifUri(String kanji) {
final unicodeString = kanji.codeUnitAt(0).toRadixString(16);
final fileName = '${unicodeString}.gif';
final animationUri = 'https://raw.githubusercontent.com/mistval/kanji_images/master/gifs/${fileName}';
return animationUri;
}
int getNewspaperFrequencyRank(String pageHtml) {
final frequencySection = getStringBetweenStrings(pageHtml, '<div class="frequency">', '</div>');
return frequencySection.isNotEmpty ? int.parse(getStringBetweenStrings(frequencySection, '<strong>', '</strong>')) : null;
}
KanjiResult parseKanjiPageData(String pageHtml, String kanji) {
final result = KanjiResult();
result.query = kanji;
result.found = containsKanjiGlyph(pageHtml, kanji);
if (result.found==false) {
return result;
}
result.taughtIn = getStringBetweenStrings(pageHtml, 'taught in <strong>', '</strong>');
result.jlptLevel = getStringBetweenStrings(pageHtml, 'JLPT level <strong>', '</strong>');
result.newspaperFrequencyRank = getNewspaperFrequencyRank(pageHtml);
result.strokeCount = getIntBetweenStrings(pageHtml, '<strong>', '</strong> strokes');
result.meaning = htmlUnescape.convert(removeNewlines(getStringBetweenStrings(pageHtml, '<div class="kanji-details__main-meanings">', '</div>')).trim());
result.kunyomi = getKunyomi(pageHtml);
result.onyomi = getOnyomi(pageHtml);
result.onyomiExamples = getOnyomiExamples(pageHtml);
result.kunyomiExamples = getKunyomiExamples(pageHtml);
result.radical = getRadical(pageHtml);
result.parts = getParts(pageHtml);
result.strokeOrderDiagramUri = getUriForStrokeOrderDiagram(kanji);
result.strokeOrderSvgUri = getSvgUri(pageHtml);
result.strokeOrderGifUri = getGifUri(kanji);
result.uri = uriForKanjiSearch(kanji);
return result;
}
/* KANJI SEARCH FUNCTIONS END */
/* EXAMPLE SEARCH FUNCTIONS START */
RegExp kanjiRegex = RegExp(r'[\u4e00-\u9faf\u3400-\u4dbf]');
String uriForExampleSearch(String phrase) {
return '${SCRAPE_BASE_URI}${Uri.encodeComponent(phrase)}%23sentences';
}
ExampleResultData getKanjiAndKana(div) {
final ul = div.find('ul').eq(0);
final contents = ul.contents();
var kanji = '';
var kana = '';
for (var i = 0; i < contents.length; i += 1) {
final content = contents.eq(i);
if (content[0].name == 'li') {
final li = content;
final furigana = li.find('.furigana').text();
final unlifted = li.find('.unlinked').text();
if (furigana) {
kanji += unlifted;
kana += furigana;
final kanaEnding = [];
for (var j = unlifted.length - 1; j > 0; j -= 1) {
if (!unlifted[j].match(kanjiRegex)) {
kanaEnding.add(unlifted[j]);
} else {
break;
}
}
kana += kanaEnding.reversed.join('');
} else {
kanji += unlifted;
kana += unlifted;
}
} else {
final text = content.text().trim();
if (text) {
kanji += text;
kana += text;
}
}
}
return ExampleResultData(
kanji: kanji,
kana: kana,
);
}
List<ExampleSentencePiece> getPieces(sentenceElement) {
final pieceElements = sentenceElement.find('li.clearfix');
final pieces = [];
for (var pieceIndex = 0; pieceIndex < pieceElements.length; pieceIndex += 1) {
final pieceElement = pieceElements.eq(pieceIndex);
pieces.add(ExampleSentencePiece(
lifted: pieceElement.children('.furigana').text(),
unlifted: pieceElement.children('.unlinked').text(),
));
}
return pieces;
}
ExampleResultData parseExampleDiv(div) {
final result = getKanjiAndKana(div);
result.english = div.find('.english').text();
result.pieces = getPieces(div);
return result;
}
ExampleResults parseExamplePageData(String pageHtml, String phrase) {
final document = xml.parse(pageHtml);
final divs = document.descendants.where((node) => node.attributes[0].value == 'sentence_content').toList();
final results = divs.map((div) => parseExampleDiv(div));
return ExampleResults(
query: phrase,
found: results.isNotEmpty,
results: results,
uri: uriForExampleSearch(phrase),
phrase: phrase,
);
}
/* EXAMPLE SEARCH FUNCTIONS END */
/* PHRASE SCRAPE FUNCTIONS START */
List<String> getTags(document) {
final tags = [];
final tagElements = document.descendants.where((node) => node.attributes[0].value == 'concept_light-tag').toList();
for (var i = 0; i < tagElements.length; i += 1) {
final tagText = tagElements.eq(i).text();
tags.add(tagText);
}
return tags;
}
PhrasePageScrapeResult getMeaningsOtherFormsAndNotes(document) {
final returnValues = PhrasePageScrapeResult( otherForms: [], notes: [] );
//TODO: Fix
// const meaningsWrapper = $('#page_container > div > div > article > div > div.concept_light-meanings.medium-9.columns > div');
final meaningsWrapper = document.descendants.where((node) => node.attributes[0].value == 'page_container').toList();
final meaningsChildren = meaningsWrapper.children();
final meanings = [];
var mostRecentWordTypes = [];
for (var meaningIndex = 0; meaningIndex < meaningsChildren.length; meaningIndex += 1) {
final child = meaningsChildren.eq(meaningIndex);
if (child.hasClass('meaning-tags')) {
mostRecentWordTypes = child.text().split(',').map((s) => s.trim().toLowerCase());
} else if (mostRecentWordTypes[0] == 'other forms') {
returnValues.otherForms = child.text().split('')
.map((s) => s.replaceAll('', '').replaceAll('', '').split(' '))
.map((a) => (ExampleResultData( kanji: a[0], kana: a[1] )));
} else if (mostRecentWordTypes[0] == 'notes') {
returnValues.notes = child.text().split('\n');
} else {
final meaning = child.find('.meaning-meaning').text();
final meaningAbstract = child.find('.meaning-abstract')
.find('a')
.remove()
.end()
.text();
final supplemental = child.find('.supplemental_info').text().split(',')
.map((s) => s.trim())
.filter((s) => s);
final seeAlsoTerms = [];
for (var i = supplemental.length - 1; i >= 0; i -= 1) {
final supplementalEntry = supplemental[i];
if (supplementalEntry.startsWith('See also')) {
seeAlsoTerms.add(supplementalEntry.replaceAll('See also ', ''));
supplemental.splice(i, 1);
}
}
final sentences = [];
final sentenceElements = child.find('.sentences').children('.sentence');
for (var sentenceIndex = 0; sentenceIndex < sentenceElements.length; sentenceIndex += 1) {
final sentenceElement = sentenceElements.eq(sentenceIndex);
final english = sentenceElement.find('.english').text();
final pieces = getPieces(sentenceElement);
final japanese = sentenceElement
.find('.english').remove().end()
.find('.furigana')
.remove()
.end()
.text();
sentences.add(PhraseScrapeSentence(english: english, japanese: japanese, pieces: pieces));
}
meanings.add(PhraseScrapeMeaning(
seeAlsoTerms: seeAlsoTerms,
sentences: sentences,
definition: meaning,
supplemental: supplemental,
definitionAbstract: meaningAbstract,
tags: mostRecentWordTypes,
));
}
}
returnValues.meanings = meanings;
return returnValues;
}
String uriForPhraseScrape(searchTerm) {
return 'https://jisho.org/word/${Uri.encodeComponent(searchTerm)}';
}
PhrasePageScrapeResult parsePhrasePageData(pageHtml, query) {
final document = xml.parse(pageHtml);
final result = getMeaningsOtherFormsAndNotes(document);
result.found = true;
result.query = query;
result.uri = uriForPhraseScrape(query);
result.tags = getTags(document);
// result.meanings = meanings;
// result.otherForms = forms;
// result.notes = notes;
return result;
}
class JishoApi { class JishoApi {
@ -443,9 +18,27 @@ class JishoApi {
/// @returns {Object} The response data from the official Jisho.org API. Its format is somewhat /// @returns {Object} The response data from the official Jisho.org API. Its format is somewhat
/// complex and is not documented, so put on your trial-and-error hat. /// complex and is not documented, so put on your trial-and-error hat.
/// @async /// @async
searchForPhrase(String phrase) { Future<JishoAPIResult> searchForPhrase(String phrase) async {
final uri = uriForPhraseSearch(phrase); final uri = uriForPhraseSearch(phrase);
return http.get(uri).then((response) => jsonDecode(response.body).data); return await http.get(uri).then((response) => JishoAPIResult.fromJson(jsonDecode(response.body)));
}
/// Scrape Jisho.org for information about a kanji character.
/// @param {string} kanji The kanji to search for.
/// @returns {KanjiResult} Information about the searched kanji.
/// @async
Future<KanjiResult> searchForKanji(String kanji) async {
final uri = uriForKanjiSearch(kanji);
return http.get(uri).then((response) => parseKanjiPageData(response.body, kanji));
}
/// Scrape Jisho.org for examples.
/// @param {string} phrase The word or phrase to search for.
/// @returns {ExampleResults}
/// @async
Future<ExampleResults> searchForExamples(String phrase) async {
final uri = uriForExampleSearch(phrase);
return http.get(uri).then((response) => parseExamplePageData(response.body, phrase));
} }
/// Scrape the word page for a word/phrase. /// Scrape the word page for a word/phrase.
@ -464,32 +57,14 @@ class JishoApi {
final response = await http.get(uri); final response = await http.get(uri);
return parsePhrasePageData(response.body, phrase); return parsePhrasePageData(response.body, phrase);
} catch (err) { } catch (err) {
if (err.response.status == 404) { // if (response.statusCode == 404) {
return PhrasePageScrapeResult( // return PhrasePageScrapeResult(
query: phrase, // query: phrase,
found: false, // found: false,
); // );
} // }
throw err; throw err;
} }
} }
/// Scrape Jisho.org for information about a kanji character.
/// @param {string} kanji The kanji to search for.
/// @returns {KanjiResult} Information about the searched kanji.
/// @async
Future<KanjiResult> searchForKanji(String kanji) {
final uri = uriForKanjiSearch(kanji);
return http.get(uri).then((response) => parseKanjiPageData(response.body, kanji));
}
/// Scrape Jisho.org for examples.
/// @param {string} phrase The word or phrase to search for.
/// @returns {ExampleResults}
/// @async
Future<ExampleResults> searchForExamples(String phrase) {
final uri = uriForExampleSearch(phrase);
return http.get(uri).then((response) => parseExamplePageData(response.body, phrase));
}
} }

View File

@ -10,6 +10,7 @@ dependencies:
# path: ^1.6.0 # path: ^1.6.0
xml: ^3.7.0 xml: ^3.7.0
html_unescape: ^1.0.1+3 html_unescape: ^1.0.1+3
html: ^0.14.0+3
dev_dependencies: dev_dependencies:
pedantic: ^1.8.0 pedantic: ^1.8.0

View File

@ -0,0 +1,31 @@
import 'dart:io';
import 'package:path/path.dart' as path;
import 'dart:convert';
import 'package:unofficial_jisho_api/unofficial_jisho_api.dart';
final jisho = JishoApi();
final encoder = JsonEncoder.withIndent(' ');
final currentdir = Directory.current.path;
void writeCases(Function apiFunction, String folderName, List<String> queries) async {
final dir = path.join(currentdir, 'test', folderName);
for (var testCount = 0; testCount < queries.length; testCount++) {
final result = await apiFunction(queries[testCount]);
final content = encoder.convert(result);
final filePath = path.join(dir, '${testCount}.json');
await File(filePath).writeAsString(content);
}
}
const kanjiQueries = ['', '', '', '極上', '', 'ネガティブ', 'wegmwrlgkrgmg', ''];
const exampleQueries = ['', '日本人', '彼*叩く', '', 'ネガティブ', 'grlgmregmneriireg'];
const phraseQueries = ['', '日本人', '', 'ネガティブ', 'grlgmregmneriireg'];
void main() async {
await writeCases(jisho.searchForKanji, 'kanji_test_cases', kanjiQueries);
await writeCases(jisho.searchForExamples, 'example_test_cases', exampleQueries);
await writeCases(jisho.scrapeForPhrase, 'phrase_scrape_test_cases', phraseQueries);
}

View File

@ -1,6 +1,4 @@
{ {
"query": "車",
"expectedResult": {
"query": "車", "query": "車",
"found": true, "found": true,
"results": [ "results": [
@ -14,11 +12,11 @@
"unlifted": "社長" "unlifted": "社長"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "さん" "unlifted": "さん"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "の" "unlifted": "の"
}, },
{ {
@ -26,7 +24,7 @@
"unlifted": "車種" "unlifted": "車種"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "と" "unlifted": "と"
}, },
{ {
@ -34,7 +32,7 @@
"unlifted": "色" "unlifted": "色"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "は" "unlifted": "は"
} }
] ]
@ -45,7 +43,7 @@
"kana": "そのくるまはうごこうとしなかった。", "kana": "そのくるまはうごこうとしなかった。",
"pieces": [ "pieces": [
{ {
"lifted": "", "lifted": null,
"unlifted": "その" "unlifted": "その"
}, },
{ {
@ -53,7 +51,7 @@
"unlifted": "車" "unlifted": "車"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "は" "unlifted": "は"
}, },
{ {
@ -61,7 +59,7 @@
"unlifted": "動こう" "unlifted": "動こう"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "としなかった" "unlifted": "としなかった"
} }
] ]
@ -72,7 +70,7 @@
"kana": "そういえば、でんしゃのなかでとなりにすわったほろよいのおっさんに、じぶんのはいくをみてほしいといわれたことがある。", "kana": "そういえば、でんしゃのなかでとなりにすわったほろよいのおっさんに、じぶんのはいくをみてほしいといわれたことがある。",
"pieces": [ "pieces": [
{ {
"lifted": "", "lifted": null,
"unlifted": "そういえば" "unlifted": "そういえば"
}, },
{ {
@ -80,7 +78,7 @@
"unlifted": "電車" "unlifted": "電車"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "の" "unlifted": "の"
}, },
{ {
@ -88,7 +86,7 @@
"unlifted": "中" "unlifted": "中"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "で" "unlifted": "で"
}, },
{ {
@ -96,7 +94,7 @@
"unlifted": "隣" "unlifted": "隣"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "に" "unlifted": "に"
}, },
{ {
@ -108,15 +106,15 @@
"unlifted": "ほろ酔い" "unlifted": "ほろ酔い"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "の" "unlifted": "の"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "おっさん" "unlifted": "おっさん"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "に" "unlifted": "に"
}, },
{ {
@ -124,7 +122,7 @@
"unlifted": "自分" "unlifted": "自分"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "の" "unlifted": "の"
}, },
{ {
@ -132,7 +130,7 @@
"unlifted": "俳句" "unlifted": "俳句"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "を" "unlifted": "を"
}, },
{ {
@ -144,7 +142,7 @@
"unlifted": "欲しい" "unlifted": "欲しい"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "と" "unlifted": "と"
}, },
{ {
@ -152,7 +150,7 @@
"unlifted": "言われた" "unlifted": "言われた"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "ことがある" "unlifted": "ことがある"
} }
] ]
@ -163,7 +161,7 @@
"kana": "ひんぱんにとけつするばあいは、きゅうきゅうしゃをよぶか、ちかくのないかいにおうしんしてもらう。", "kana": "ひんぱんにとけつするばあいは、きゅうきゅうしゃをよぶか、ちかくのないかいにおうしんしてもらう。",
"pieces": [ "pieces": [
{ {
"lifted": "", "lifted": null,
"unlifted": "ひんぱんに" "unlifted": "ひんぱんに"
}, },
{ {
@ -171,7 +169,7 @@
"unlifted": "吐血" "unlifted": "吐血"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "する" "unlifted": "する"
}, },
{ {
@ -179,7 +177,7 @@
"unlifted": "場合" "unlifted": "場合"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "は" "unlifted": "は"
}, },
{ {
@ -187,7 +185,7 @@
"unlifted": "救急車" "unlifted": "救急車"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "を" "unlifted": "を"
}, },
{ {
@ -195,7 +193,7 @@
"unlifted": "呼ぶ" "unlifted": "呼ぶ"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "か" "unlifted": "か"
}, },
{ {
@ -203,7 +201,7 @@
"unlifted": "近く" "unlifted": "近く"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "の" "unlifted": "の"
}, },
{ {
@ -211,7 +209,7 @@
"unlifted": "内科医" "unlifted": "内科医"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "に" "unlifted": "に"
}, },
{ {
@ -219,11 +217,11 @@
"unlifted": "往診" "unlifted": "往診"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "して" "unlifted": "して"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "もらう" "unlifted": "もらう"
} }
] ]
@ -238,7 +236,7 @@
"unlifted": "二輪車" "unlifted": "二輪車"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "が" "unlifted": "が"
}, },
{ {
@ -246,7 +244,7 @@
"unlifted": "倒れず" "unlifted": "倒れず"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "に" "unlifted": "に"
}, },
{ {
@ -254,15 +252,15 @@
"unlifted": "走行" "unlifted": "走行"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "する" "unlifted": "する"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "のに" "unlifted": "のに"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "は" "unlifted": "は"
}, },
{ {
@ -270,7 +268,7 @@
"unlifted": "前輪" "unlifted": "前輪"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "が" "unlifted": "が"
}, },
{ {
@ -293,7 +291,7 @@
"unlifted": "列車" "unlifted": "列車"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "に" "unlifted": "に"
}, },
{ {
@ -301,11 +299,11 @@
"unlifted": "間に合った" "unlifted": "間に合った"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "の" "unlifted": "の"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "か" "unlifted": "か"
} }
] ]
@ -320,7 +318,7 @@
"unlifted": "車" "unlifted": "車"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "の" "unlifted": "の"
}, },
{ {
@ -328,7 +326,7 @@
"unlifted": "速度" "unlifted": "速度"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "を" "unlifted": "を"
}, },
{ {
@ -340,11 +338,11 @@
"unlifted": "装置" "unlifted": "装置"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "ハンプ" "unlifted": "ハンプ"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "や" "unlifted": "や"
}, },
{ {
@ -352,7 +350,7 @@
"unlifted": "狭さく" "unlifted": "狭さく"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "について" "unlifted": "について"
}, },
{ {
@ -360,11 +358,11 @@
"unlifted": "意見" "unlifted": "意見"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "が" "unlifted": "が"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "あったら" "unlifted": "あったら"
}, },
{ {
@ -372,7 +370,7 @@
"unlifted": "書いて" "unlifted": "書いて"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "ください" "unlifted": "ください"
} }
] ]
@ -383,7 +381,7 @@
"kana": "メキシコせいふは1998ねんがたのちゅうこしゃいがいのちゅうこしゃのゆにゅうをきんしするとはっぴょうした。", "kana": "メキシコせいふは1998ねんがたのちゅうこしゃいがいのちゅうこしゃのゆにゅうをきんしするとはっぴょうした。",
"pieces": [ "pieces": [
{ {
"lifted": "", "lifted": null,
"unlifted": "メキシコ" "unlifted": "メキシコ"
}, },
{ {
@ -391,7 +389,7 @@
"unlifted": "政府" "unlifted": "政府"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "は" "unlifted": "は"
}, },
{ {
@ -399,7 +397,7 @@
"unlifted": "年型" "unlifted": "年型"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "の" "unlifted": "の"
}, },
{ {
@ -411,7 +409,7 @@
"unlifted": "以外" "unlifted": "以外"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "の" "unlifted": "の"
}, },
{ {
@ -419,7 +417,7 @@
"unlifted": "中古車" "unlifted": "中古車"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "の" "unlifted": "の"
}, },
{ {
@ -427,7 +425,7 @@
"unlifted": "輸入" "unlifted": "輸入"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "を" "unlifted": "を"
}, },
{ {
@ -435,11 +433,11 @@
"unlifted": "禁止" "unlifted": "禁止"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "する" "unlifted": "する"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "と" "unlifted": "と"
}, },
{ {
@ -447,7 +445,7 @@
"unlifted": "発表" "unlifted": "発表"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "した" "unlifted": "した"
} }
] ]
@ -462,7 +460,7 @@
"unlifted": "車" "unlifted": "車"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "は" "unlifted": "は"
}, },
{ {
@ -470,7 +468,7 @@
"unlifted": "草地" "unlifted": "草地"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "に" "unlifted": "に"
}, },
{ {
@ -478,11 +476,11 @@
"unlifted": "飛び込み" "unlifted": "飛び込み"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "しばらく" "unlifted": "しばらく"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "ガクンガクンと" "unlifted": "ガクンガクンと"
}, },
{ {
@ -494,7 +492,7 @@
"unlifted": "止まった" "unlifted": "止まった"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "のです" "unlifted": "のです"
} }
] ]
@ -509,7 +507,7 @@
"unlifted": "車" "unlifted": "車"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "も" "unlifted": "も"
}, },
{ {
@ -517,7 +515,7 @@
"unlifted": "何も" "unlifted": "何も"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "なく" "unlifted": "なく"
}, },
{ {
@ -525,7 +523,7 @@
"unlifted": "生活保護" "unlifted": "生活保護"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "で" "unlifted": "で"
}, },
{ {
@ -556,7 +554,7 @@
"unlifted": "円" "unlifted": "円"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "した" "unlifted": "した"
}, },
{ {
@ -564,7 +562,7 @@
"unlifted": "車" "unlifted": "車"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "も" "unlifted": "も"
}, },
{ {
@ -580,19 +578,19 @@
"unlifted": "円" "unlifted": "円"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "位" "unlifted": "位"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "だ" "unlifted": "だ"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "そうです" "unlifted": "そうです"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "から" "unlifted": "から"
}, },
{ {
@ -604,7 +602,7 @@
"unlifted": "乗ろう" "unlifted": "乗ろう"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "と" "unlifted": "と"
}, },
{ {
@ -619,11 +617,11 @@
"kana": "このときじょせつしゃはたしかにわたしたちのえいゆうでした。", "kana": "このときじょせつしゃはたしかにわたしたちのえいゆうでした。",
"pieces": [ "pieces": [
{ {
"lifted": "", "lifted": null,
"unlifted": "この" "unlifted": "この"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "とき" "unlifted": "とき"
}, },
{ {
@ -631,7 +629,7 @@
"unlifted": "除雪車" "unlifted": "除雪車"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "は" "unlifted": "は"
}, },
{ {
@ -643,7 +641,7 @@
"unlifted": "私たち" "unlifted": "私たち"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "の" "unlifted": "の"
}, },
{ {
@ -651,7 +649,7 @@
"unlifted": "英雄" "unlifted": "英雄"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "でした" "unlifted": "でした"
} }
] ]
@ -666,7 +664,7 @@
"unlifted": "彼" "unlifted": "彼"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "は" "unlifted": "は"
}, },
{ {
@ -674,7 +672,7 @@
"unlifted": "彼女" "unlifted": "彼女"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "に" "unlifted": "に"
}, },
{ {
@ -682,7 +680,7 @@
"unlifted": "車" "unlifted": "車"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "を" "unlifted": "を"
}, },
{ {
@ -690,27 +688,27 @@
"unlifted": "運転させる" "unlifted": "運転させる"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "つもり" "unlifted": "つもり"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "は" "unlifted": "は"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "なかった" "unlifted": "なかった"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "の" "unlifted": "の"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "だ" "unlifted": "だ"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "が" "unlifted": "が"
}, },
{ {
@ -718,23 +716,23 @@
"unlifted": "彼女" "unlifted": "彼女"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "が" "unlifted": "が"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "あまり" "unlifted": "あまり"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "に" "unlifted": "に"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "せがむ" "unlifted": "せがむ"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "ものだから" "unlifted": "ものだから"
}, },
{ {
@ -746,11 +744,11 @@
"unlifted": "方" "unlifted": "方"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "が" "unlifted": "が"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "とうとう" "unlifted": "とうとう"
}, },
{ {
@ -765,7 +763,7 @@
"kana": "これからかそうばへいどうしますので、小川さまときみはくるまへ。", "kana": "これからかそうばへいどうしますので、小川さまときみはくるまへ。",
"pieces": [ "pieces": [
{ {
"lifted": "", "lifted": null,
"unlifted": "これから" "unlifted": "これから"
}, },
{ {
@ -773,7 +771,7 @@
"unlifted": "火葬場" "unlifted": "火葬場"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "へ" "unlifted": "へ"
}, },
{ {
@ -781,11 +779,11 @@
"unlifted": "移動" "unlifted": "移動"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "します" "unlifted": "します"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "ので" "unlifted": "ので"
}, },
{ {
@ -793,7 +791,7 @@
"unlifted": "様" "unlifted": "様"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "と" "unlifted": "と"
}, },
{ {
@ -801,7 +799,7 @@
"unlifted": "君" "unlifted": "君"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "は" "unlifted": "は"
}, },
{ {
@ -809,7 +807,7 @@
"unlifted": "車" "unlifted": "車"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "へ" "unlifted": "へ"
} }
] ]
@ -824,7 +822,7 @@
"unlifted": "日本車" "unlifted": "日本車"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "は" "unlifted": "は"
}, },
{ {
@ -832,7 +830,7 @@
"unlifted": "右ハンドル" "unlifted": "右ハンドル"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "です" "unlifted": "です"
} }
] ]
@ -847,15 +845,15 @@
"unlifted": "同盟軍" "unlifted": "同盟軍"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "は" "unlifted": "は"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "バグダッド" "unlifted": "バグダッド"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "の" "unlifted": "の"
}, },
{ {
@ -863,7 +861,7 @@
"unlifted": "検問所" "unlifted": "検問所"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "で" "unlifted": "で"
}, },
{ {
@ -875,7 +873,7 @@
"unlifted": "車" "unlifted": "車"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "を" "unlifted": "を"
}, },
{ {
@ -883,7 +881,7 @@
"unlifted": "襲撃" "unlifted": "襲撃"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "した" "unlifted": "した"
} }
] ]
@ -898,23 +896,23 @@
"unlifted": "動き出す" "unlifted": "動き出す"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "とき" "unlifted": "とき"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "ベル" "unlifted": "ベル"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "が" "unlifted": "が"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "ちんちん" "unlifted": "ちんちん"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "と" "unlifted": "と"
}, },
{ {
@ -922,11 +920,11 @@
"unlifted": "鳴る" "unlifted": "鳴る"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "だから" "unlifted": "だから"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "ちんちん" "unlifted": "ちんちん"
}, },
{ {
@ -945,15 +943,15 @@
"unlifted": "車" "unlifted": "車"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "が" "unlifted": "が"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "ぬかるみ" "unlifted": "ぬかるみ"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "に" "unlifted": "に"
}, },
{ {
@ -972,19 +970,19 @@
"unlifted": "車" "unlifted": "車"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "が" "unlifted": "が"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "いかれる" "unlifted": "いかれる"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "か" "unlifted": "か"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "と" "unlifted": "と"
}, },
{ {
@ -992,7 +990,7 @@
"unlifted": "思った" "unlifted": "思った"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "ころ" "unlifted": "ころ"
}, },
{ {
@ -1000,7 +998,7 @@
"unlifted": "終点" "unlifted": "終点"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "に" "unlifted": "に"
}, },
{ {
@ -1019,19 +1017,19 @@
"unlifted": "車" "unlifted": "車"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "ああ" "unlifted": "ああ"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "あの" "unlifted": "あの"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "リムジン" "unlifted": "リムジン"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "でしたら" "unlifted": "でしたら"
}, },
{ {
@ -1039,15 +1037,15 @@
"unlifted": "私" "unlifted": "私"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "が" "unlifted": "が"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "チャーター" "unlifted": "チャーター"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "した" "unlifted": "した"
}, },
{ {
@ -1055,17 +1053,16 @@
"unlifted": "物" "unlifted": "物"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "です" "unlifted": "です"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "わ" "unlifted": "わ"
} }
] ]
} }
], ],
"uri": "http://jisho.org/search/%E8%BB%8A%23sentences", "uri": "https://jisho.org/search/%E8%BB%8A%23sentences",
"phrase": "車" "phrase": "車"
} }
}

View File

@ -1,6 +1,4 @@
{ {
"query": "日本人",
"expectedResult": {
"query": "日本人", "query": "日本人",
"found": true, "found": true,
"results": [ "results": [
@ -14,31 +12,31 @@
"unlifted": "日本人" "unlifted": "日本人"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "なら" "unlifted": "なら"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "そんな" "unlifted": "そんな"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "こと" "unlifted": "こと"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "は" "unlifted": "は"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "けっして" "unlifted": "けっして"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "しない" "unlifted": "しない"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "でしょう" "unlifted": "でしょう"
} }
] ]
@ -49,7 +47,7 @@
"kana": "いつからにほんじんはせいはくまいをたべるようになったのですか?", "kana": "いつからにほんじんはせいはくまいをたべるようになったのですか?",
"pieces": [ "pieces": [
{ {
"lifted": "", "lifted": null,
"unlifted": "いつから" "unlifted": "いつから"
}, },
{ {
@ -57,7 +55,7 @@
"unlifted": "日本人" "unlifted": "日本人"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "は" "unlifted": "は"
}, },
{ {
@ -65,7 +63,7 @@
"unlifted": "精白米" "unlifted": "精白米"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "を" "unlifted": "を"
}, },
{ {
@ -73,15 +71,15 @@
"unlifted": "食べる" "unlifted": "食べる"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "ようになった" "unlifted": "ようになった"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "のです" "unlifted": "のです"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "か" "unlifted": "か"
} }
] ]
@ -92,7 +90,7 @@
"kana": "こんなうたをのこしているめいじてんのうのいちめんをしっているにほんじんはすくないのではないだろうか。", "kana": "こんなうたをのこしているめいじてんのうのいちめんをしっているにほんじんはすくないのではないだろうか。",
"pieces": [ "pieces": [
{ {
"lifted": "", "lifted": null,
"unlifted": "こんな" "unlifted": "こんな"
}, },
{ {
@ -100,7 +98,7 @@
"unlifted": "歌" "unlifted": "歌"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "を" "unlifted": "を"
}, },
{ {
@ -112,7 +110,7 @@
"unlifted": "明治天皇" "unlifted": "明治天皇"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "の" "unlifted": "の"
}, },
{ {
@ -120,7 +118,7 @@
"unlifted": "一面" "unlifted": "一面"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "を" "unlifted": "を"
}, },
{ {
@ -132,7 +130,7 @@
"unlifted": "日本人" "unlifted": "日本人"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "は" "unlifted": "は"
}, },
{ {
@ -140,15 +138,15 @@
"unlifted": "少ない" "unlifted": "少ない"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "の" "unlifted": "の"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "ではない" "unlifted": "ではない"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "だろうか" "unlifted": "だろうか"
} }
] ]
@ -163,7 +161,7 @@
"unlifted": "私" "unlifted": "私"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "は" "unlifted": "は"
}, },
{ {
@ -171,11 +169,11 @@
"unlifted": "日本人" "unlifted": "日本人"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "です" "unlifted": "です"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "が" "unlifted": "が"
}, },
{ {
@ -183,7 +181,7 @@
"unlifted": "日本" "unlifted": "日本"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "に" "unlifted": "に"
}, },
{ {
@ -202,19 +200,19 @@
"unlifted": "向こう" "unlifted": "向こう"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "で" "unlifted": "で"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "は" "unlifted": "は"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "セレブ" "unlifted": "セレブ"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "という" "unlifted": "という"
}, },
{ {
@ -222,7 +220,7 @@
"unlifted": "言葉" "unlifted": "言葉"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "を" "unlifted": "を"
}, },
{ {
@ -230,7 +228,7 @@
"unlifted": "金持ち" "unlifted": "金持ち"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "の" "unlifted": "の"
}, },
{ {
@ -238,11 +236,11 @@
"unlifted": "意味" "unlifted": "意味"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "で" "unlifted": "で"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "は" "unlifted": "は"
}, },
{ {
@ -250,11 +248,11 @@
"unlifted": "使わない" "unlifted": "使わない"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "と" "unlifted": "と"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "いう" "unlifted": "いう"
}, },
{ {
@ -262,7 +260,7 @@
"unlifted": "事" "unlifted": "事"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "で" "unlifted": "で"
}, },
{ {
@ -270,7 +268,7 @@
"unlifted": "日本人" "unlifted": "日本人"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "と" "unlifted": "と"
}, },
{ {
@ -278,7 +276,7 @@
"unlifted": "判明" "unlifted": "判明"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "しました" "unlifted": "しました"
} }
] ]
@ -297,11 +295,11 @@
"unlifted": "離れ" "unlifted": "離れ"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "した" "unlifted": "した"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "この" "unlifted": "この"
}, },
{ {
@ -313,23 +311,23 @@
"unlifted": "相貌" "unlifted": "相貌"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "から" "unlifted": "から"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "も" "unlifted": "も"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "わかる" "unlifted": "わかる"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "ように" "unlifted": "ように"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "は" "unlifted": "は"
}, },
{ {
@ -345,7 +343,7 @@
"unlifted": "日本人" "unlifted": "日本人"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "じゃない" "unlifted": "じゃない"
}, },
{ {
@ -353,15 +351,15 @@
"unlifted": "西洋人" "unlifted": "西洋人"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "を" "unlifted": "を"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "おばあちゃん" "unlifted": "おばあちゃん"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "に" "unlifted": "に"
}, },
{ {
@ -369,15 +367,15 @@
"unlifted": "持つ" "unlifted": "持つ"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "クォーター" "unlifted": "クォーター"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "だったり" "unlifted": "だったり"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "する" "unlifted": "する"
} }
] ]
@ -392,15 +390,15 @@
"unlifted": "今日" "unlifted": "今日"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "の" "unlifted": "の"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "トピック" "unlifted": "トピック"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "は" "unlifted": "は"
}, },
{ {
@ -408,7 +406,7 @@
"unlifted": "北朝鮮" "unlifted": "北朝鮮"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "による" "unlifted": "による"
}, },
{ {
@ -424,7 +422,7 @@
"unlifted": "問題" "unlifted": "問題"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "です" "unlifted": "です"
} }
] ]
@ -439,15 +437,15 @@
"unlifted": "外米" "unlifted": "外米"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "は" "unlifted": "は"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "ぼそぼそ" "unlifted": "ぼそぼそ"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "していて" "unlifted": "していて"
}, },
{ {
@ -455,7 +453,7 @@
"unlifted": "日本人" "unlifted": "日本人"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "の" "unlifted": "の"
}, },
{ {
@ -463,7 +461,7 @@
"unlifted": "口" "unlifted": "口"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "には" "unlifted": "には"
}, },
{ {
@ -486,7 +484,7 @@
"unlifted": "多く" "unlifted": "多く"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "の" "unlifted": "の"
}, },
{ {
@ -494,7 +492,7 @@
"unlifted": "日本人" "unlifted": "日本人"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "が" "unlifted": "が"
}, },
{ {
@ -502,7 +500,7 @@
"unlifted": "海外" "unlifted": "海外"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "へ" "unlifted": "へ"
}, },
{ {
@ -510,7 +508,7 @@
"unlifted": "旅行" "unlifted": "旅行"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "する" "unlifted": "する"
} }
] ]
@ -529,7 +527,7 @@
"unlifted": "父親" "unlifted": "父親"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "は" "unlifted": "は"
}, },
{ {
@ -537,7 +535,7 @@
"unlifted": "日本人" "unlifted": "日本人"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "だ" "unlifted": "だ"
} }
] ]
@ -552,7 +550,7 @@
"unlifted": "彼ら" "unlifted": "彼ら"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "は" "unlifted": "は"
}, },
{ {
@ -564,7 +562,7 @@
"unlifted": "労働者" "unlifted": "労働者"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "が" "unlifted": "が"
}, },
{ {
@ -572,11 +570,11 @@
"unlifted": "不足" "unlifted": "不足"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "している" "unlifted": "している"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "から" "unlifted": "から"
}, },
{ {
@ -584,7 +582,7 @@
"unlifted": "外国人" "unlifted": "外国人"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "を" "unlifted": "を"
}, },
{ {
@ -603,7 +601,7 @@
"unlifted": "彼ら" "unlifted": "彼ら"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "は" "unlifted": "は"
}, },
{ {
@ -611,7 +609,7 @@
"unlifted": "日本人" "unlifted": "日本人"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "ですか" "unlifted": "ですか"
} }
] ]
@ -626,7 +624,7 @@
"unlifted": "彼ら" "unlifted": "彼ら"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "は" "unlifted": "は"
}, },
{ {
@ -634,11 +632,11 @@
"unlifted": "日本人" "unlifted": "日本人"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "ですか" "unlifted": "ですか"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "それとも" "unlifted": "それとも"
}, },
{ {
@ -646,7 +644,7 @@
"unlifted": "中国人" "unlifted": "中国人"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "ですか" "unlifted": "ですか"
} }
] ]
@ -661,7 +659,7 @@
"unlifted": "彼" "unlifted": "彼"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "は" "unlifted": "は"
}, },
{ {
@ -669,7 +667,7 @@
"unlifted": "日本人" "unlifted": "日本人"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "ではありません" "unlifted": "ではありません"
} }
] ]
@ -684,7 +682,7 @@
"unlifted": "彼" "unlifted": "彼"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "は" "unlifted": "は"
}, },
{ {
@ -692,11 +690,11 @@
"unlifted": "日本語" "unlifted": "日本語"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "を" "unlifted": "を"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "まるで" "unlifted": "まるで"
}, },
{ {
@ -704,11 +702,11 @@
"unlifted": "日本人" "unlifted": "日本人"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "である" "unlifted": "である"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "かのように" "unlifted": "かのように"
}, },
{ {
@ -727,7 +725,7 @@
"unlifted": "彼" "unlifted": "彼"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "は" "unlifted": "は"
}, },
{ {
@ -739,7 +737,7 @@
"unlifted": "日本人" "unlifted": "日本人"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "だ" "unlifted": "だ"
} }
] ]
@ -754,7 +752,7 @@
"unlifted": "彼" "unlifted": "彼"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "は" "unlifted": "は"
}, },
{ {
@ -762,7 +760,7 @@
"unlifted": "生まれ" "unlifted": "生まれ"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "は" "unlifted": "は"
}, },
{ {
@ -770,7 +768,7 @@
"unlifted": "日本人" "unlifted": "日本人"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "です" "unlifted": "です"
} }
] ]
@ -785,7 +783,7 @@
"unlifted": "彼" "unlifted": "彼"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "は" "unlifted": "は"
}, },
{ {
@ -793,7 +791,7 @@
"unlifted": "最初" "unlifted": "最初"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "に" "unlifted": "に"
}, },
{ {
@ -801,11 +799,11 @@
"unlifted": "宇宙旅行" "unlifted": "宇宙旅行"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "を" "unlifted": "を"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "した" "unlifted": "した"
}, },
{ {
@ -813,7 +811,7 @@
"unlifted": "日本人" "unlifted": "日本人"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "です" "unlifted": "です"
} }
] ]
@ -828,7 +826,7 @@
"unlifted": "彼" "unlifted": "彼"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "は" "unlifted": "は"
}, },
{ {
@ -840,7 +838,7 @@
"unlifted": "日本人" "unlifted": "日本人"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "だ" "unlifted": "だ"
} }
] ]
@ -855,7 +853,7 @@
"unlifted": "彼" "unlifted": "彼"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "は" "unlifted": "は"
}, },
{ {
@ -863,11 +861,11 @@
"unlifted": "日本語" "unlifted": "日本語"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "を" "unlifted": "を"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "まるで" "unlifted": "まるで"
}, },
{ {
@ -875,7 +873,7 @@
"unlifted": "日本人" "unlifted": "日本人"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "かのように" "unlifted": "かのように"
}, },
{ {
@ -885,7 +883,6 @@
] ]
} }
], ],
"uri": "http://jisho.org/search/%E6%97%A5%E6%9C%AC%E4%BA%BA%23sentences", "uri": "https://jisho.org/search/%E6%97%A5%E6%9C%AC%E4%BA%BA%23sentences",
"phrase": "日本人" "phrase": "日本人"
} }
}

View File

@ -1,10 +1,7 @@
{ {
"query": "彼*叩く",
"expectedResult": {
"query": "彼*叩く", "query": "彼*叩く",
"found": false, "found": false,
"results": [], "results": [],
"uri": "http://jisho.org/search/%E5%BD%BC%EF%BC%8A%E5%8F%A9%E3%81%8F%23sentences", "uri": "https://jisho.org/search/%E5%BD%BC%EF%BC%8A%E5%8F%A9%E3%81%8F%23sentences",
"phrase": "彼*叩く" "phrase": "彼*叩く"
} }
}

File diff suppressed because it is too large Load Diff

View File

@ -1,10 +1,7 @@
{ {
"query": "ネガティブ",
"expectedResult": {
"query": "ネガティブ", "query": "ネガティブ",
"found": false, "found": false,
"results": [], "results": [],
"uri": "http://jisho.org/search/%E3%83%8D%E3%82%AC%E3%83%86%E3%82%A3%E3%83%96%23sentences", "uri": "https://jisho.org/search/%E3%83%8D%E3%82%AC%E3%83%86%E3%82%A3%E3%83%96%23sentences",
"phrase": "ネガティブ" "phrase": "ネガティブ"
} }
}

View File

@ -1,10 +1,7 @@
{ {
"query": "grlgmregmneriireg",
"expectedResult": {
"query": "grlgmregmneriireg", "query": "grlgmregmneriireg",
"found": false, "found": false,
"results": [], "results": [],
"uri": "http://jisho.org/search/grlgmregmneriireg%23sentences", "uri": "https://jisho.org/search/grlgmregmneriireg%23sentences",
"phrase": "grlgmregmneriireg" "phrase": "grlgmregmneriireg"
} }
}

View File

@ -1,11 +1,9 @@
{ {
"query": "車",
"expectedResult": {
"query": "車", "query": "車",
"found": true, "found": true,
"taughtIn": "grade 1", "taughtIn": "grade 1",
"jlptLevel": "N5", "jlptLevel": "N5",
"newspaperFrequencyRank": "333", "newspaperFrequencyRank": 333,
"strokeCount": 7, "strokeCount": 7,
"meaning": "car", "meaning": "car",
"kunyomi": [ "kunyomi": [
@ -55,14 +53,14 @@
], ],
"radical": { "radical": {
"symbol": "車", "symbol": "車",
"forms": null,
"meaning": "cart, car" "meaning": "cart, car"
}, },
"parts": [ "parts": [
"車" "車"
], ],
"strokeOrderDiagramUri": "http://classic.jisho.org/static/images/stroke_diagrams/36554_frames.png", "strokeOrderDiagramUri": "https://classic.jisho.org/static/images/stroke_diagrams/36554_frames.png",
"strokeOrderSvgUri": "http://d1w6u4xc3l95km.cloudfront.net/kanji-2015-03/08eca.svg", "strokeOrderSvgUri": "https://d1w6u4xc3l95km.cloudfront.net/kanji-2015-03/08eca.svg",
"strokeOrderGifUri": "https://raw.githubusercontent.com/mistval/kanji_images/master/gifs/8eca.gif", "strokeOrderGifUri": "https://raw.githubusercontent.com/mistval/kanji_images/master/gifs/8eca.gif",
"uri": "http://jisho.org/search/%E8%BB%8A%23kanji" "uri": "https://jisho.org/search/%E8%BB%8A%23kanji"
}
} }

View File

@ -1,11 +1,9 @@
{ {
"query": "家",
"expectedResult": {
"query": "家", "query": "家",
"found": true, "found": true,
"taughtIn": "grade 2", "taughtIn": "grade 2",
"jlptLevel": "N4", "jlptLevel": "N4",
"newspaperFrequencyRank": "133", "newspaperFrequencyRank": 133,
"strokeCount": 10, "strokeCount": 10,
"meaning": "house, home, family, professional, expert, performer", "meaning": "house, home, family, professional, expert, performer",
"kunyomi": [ "kunyomi": [
@ -113,15 +111,15 @@
], ],
"radical": { "radical": {
"symbol": "宀", "symbol": "宀",
"forms": null,
"meaning": "roof" "meaning": "roof"
}, },
"parts": [ "parts": [
"宀", "宀",
"豕" "豕"
], ],
"strokeOrderDiagramUri": "http://classic.jisho.org/static/images/stroke_diagrams/23478_frames.png", "strokeOrderDiagramUri": "https://classic.jisho.org/static/images/stroke_diagrams/23478_frames.png",
"strokeOrderSvgUri": "http://d1w6u4xc3l95km.cloudfront.net/kanji-2015-03/05bb6.svg", "strokeOrderSvgUri": "https://d1w6u4xc3l95km.cloudfront.net/kanji-2015-03/05bb6.svg",
"strokeOrderGifUri": "https://raw.githubusercontent.com/mistval/kanji_images/master/gifs/5bb6.gif", "strokeOrderGifUri": "https://raw.githubusercontent.com/mistval/kanji_images/master/gifs/5bb6.gif",
"uri": "http://jisho.org/search/%E5%AE%B6%23kanji" "uri": "https://jisho.org/search/%E5%AE%B6%23kanji"
}
} }

View File

@ -1,11 +1,9 @@
{ {
"query": "楽",
"expectedResult": {
"query": "楽", "query": "楽",
"found": true, "found": true,
"taughtIn": "grade 2", "taughtIn": "grade 2",
"jlptLevel": "N4", "jlptLevel": "N4",
"newspaperFrequencyRank": "373", "newspaperFrequencyRank": 373,
"strokeCount": 13, "strokeCount": 13,
"meaning": "music, comfort, ease", "meaning": "music, comfort, ease",
"kunyomi": [ "kunyomi": [
@ -84,6 +82,7 @@
], ],
"radical": { "radical": {
"symbol": "木", "symbol": "木",
"forms": null,
"meaning": "tree" "meaning": "tree"
}, },
"parts": [ "parts": [
@ -91,9 +90,8 @@
"木", "木",
"白" "白"
], ],
"strokeOrderDiagramUri": "http://classic.jisho.org/static/images/stroke_diagrams/27005_frames.png", "strokeOrderDiagramUri": "https://classic.jisho.org/static/images/stroke_diagrams/27005_frames.png",
"strokeOrderSvgUri": "http://d1w6u4xc3l95km.cloudfront.net/kanji-2015-03/0697d.svg", "strokeOrderSvgUri": "https://d1w6u4xc3l95km.cloudfront.net/kanji-2015-03/0697d.svg",
"strokeOrderGifUri": "https://raw.githubusercontent.com/mistval/kanji_images/master/gifs/697d.gif", "strokeOrderGifUri": "https://raw.githubusercontent.com/mistval/kanji_images/master/gifs/697d.gif",
"uri": "http://jisho.org/search/%E6%A5%BD%23kanji" "uri": "https://jisho.org/search/%E6%A5%BD%23kanji"
}
} }

View File

@ -1,7 +1,19 @@
{ {
"query": "極上", "query": "極上",
"expectedResult": { "found": false,
"query": "極上", "taughtIn": null,
"found": false "jlptLevel": null,
} "newspaperFrequencyRank": null,
"strokeCount": null,
"meaning": null,
"kunyomi": null,
"onyomi": null,
"onyomiExamples": null,
"kunyomiExamples": null,
"radical": null,
"parts": null,
"strokeOrderDiagramUri": null,
"strokeOrderSvgUri": null,
"strokeOrderGifUri": null,
"uri": null
} }

View File

@ -1,8 +1,9 @@
{ {
"query": "贄",
"expectedResult": {
"query": "贄", "query": "贄",
"found": true, "found": true,
"taughtIn": null,
"jlptLevel": null,
"newspaperFrequencyRank": null,
"strokeCount": 18, "strokeCount": 18,
"meaning": "offering, sacrifice", "meaning": "offering, sacrifice",
"kunyomi": [ "kunyomi": [
@ -31,6 +32,7 @@
], ],
"radical": { "radical": {
"symbol": "貝", "symbol": "貝",
"forms": null,
"meaning": "shell" "meaning": "shell"
}, },
"parts": [ "parts": [
@ -44,9 +46,8 @@
"貝", "貝",
"辛" "辛"
], ],
"strokeOrderDiagramUri": "http://classic.jisho.org/static/images/stroke_diagrams/36100_frames.png", "strokeOrderDiagramUri": "https://classic.jisho.org/static/images/stroke_diagrams/36100_frames.png",
"strokeOrderSvgUri": "http://d1w6u4xc3l95km.cloudfront.net/kanji-2015-03/08d04.svg", "strokeOrderSvgUri": "https://d1w6u4xc3l95km.cloudfront.net/kanji-2015-03/08d04.svg",
"strokeOrderGifUri": "https://raw.githubusercontent.com/mistval/kanji_images/master/gifs/8d04.gif", "strokeOrderGifUri": "https://raw.githubusercontent.com/mistval/kanji_images/master/gifs/8d04.gif",
"uri": "http://jisho.org/search/%E8%B4%84%23kanji" "uri": "https://jisho.org/search/%E8%B4%84%23kanji"
}
} }

View File

@ -1,7 +1,19 @@
{ {
"query": "ネガティブ", "query": "ネガティブ",
"expectedResult": { "found": false,
"query": "ネガティブ", "taughtIn": null,
"found": false "jlptLevel": null,
} "newspaperFrequencyRank": null,
"strokeCount": null,
"meaning": null,
"kunyomi": null,
"onyomi": null,
"onyomiExamples": null,
"kunyomiExamples": null,
"radical": null,
"parts": null,
"strokeOrderDiagramUri": null,
"strokeOrderSvgUri": null,
"strokeOrderGifUri": null,
"uri": null
} }

View File

@ -1,7 +1,19 @@
{ {
"query": "wegmwrlgkrgmg", "query": "wegmwrlgkrgmg",
"expectedResult": { "found": false,
"query": "wegmwrlgkrgmg", "taughtIn": null,
"found": false "jlptLevel": null,
} "newspaperFrequencyRank": null,
"strokeCount": null,
"meaning": null,
"kunyomi": null,
"onyomi": null,
"onyomiExamples": null,
"kunyomiExamples": null,
"radical": null,
"parts": null,
"strokeOrderDiagramUri": null,
"strokeOrderSvgUri": null,
"strokeOrderGifUri": null,
"uri": null
} }

View File

@ -1,11 +1,9 @@
{ {
"query": "水",
"expectedResult": {
"query": "水", "query": "水",
"found": true, "found": true,
"taughtIn": "grade 1", "taughtIn": "grade 1",
"jlptLevel": "N5", "jlptLevel": "N5",
"newspaperFrequencyRank": "223", "newspaperFrequencyRank": 223,
"strokeCount": 4, "strokeCount": 4,
"meaning": "water", "meaning": "water",
"kunyomi": [ "kunyomi": [
@ -70,9 +68,8 @@
"parts": [ "parts": [
"水" "水"
], ],
"strokeOrderDiagramUri": "http://classic.jisho.org/static/images/stroke_diagrams/27700_frames.png", "strokeOrderDiagramUri": "https://classic.jisho.org/static/images/stroke_diagrams/27700_frames.png",
"strokeOrderSvgUri": "http://d1w6u4xc3l95km.cloudfront.net/kanji-2015-03/06c34.svg", "strokeOrderSvgUri": "https://d1w6u4xc3l95km.cloudfront.net/kanji-2015-03/06c34.svg",
"strokeOrderGifUri": "https://raw.githubusercontent.com/mistval/kanji_images/master/gifs/6c34.gif", "strokeOrderGifUri": "https://raw.githubusercontent.com/mistval/kanji_images/master/gifs/6c34.gif",
"uri": "http://jisho.org/search/%E6%B0%B4%23kanji" "uri": "https://jisho.org/search/%E6%B0%B4%23kanji"
}
} }

View File

@ -1,293 +0,0 @@
import 'package:unofficial_jisho_api/src/objects.dart';
import 'package:unofficial_jisho_api/unofficial_jisho_api.dart';
import 'package:test/test.dart';
import 'dart:convert';
import 'package:http/http.dart' as http;
void test_local_functions() async {
/* KANJI SEARCH FUNCTION TESTS START */
test('removeNewLines', () {
final result = removeNewlines('Line \nwith\r\n Newlines and spaces\n');
expect(result, 'Line with Newlines and spaces');
});
test('uriForKanjiSearch', () {
final result = uriForKanjiSearch('');
expect(result, 'http://jisho.org/search/%E6%99%82%23kanji');
});
test('getUriForStrokeOrderDiagram', () {
final result = getUriForStrokeOrderDiagram('');
expect(result, 'http://classic.jisho.org/static/images/stroke_diagrams/26178_frames.png');
});
test('uriForPhraseSearch', () {
final result = uriForPhraseSearch('時間');
expect(result, 'http://jisho.org/api/v1/search/words?keyword=%E6%99%82%E9%96%93');
});
final kanjiPage = (await http.get('http://jisho.org/search/%E6%99%82%23kanji')).body;
test('containsKanjiGlyph', () {
final result = containsKanjiGlyph(kanjiPage, '');
expect(result, true);
});
test('getStringBetweenIndicies', () {
final result = getStringBetweenIndicies('String\n\rwith\nNewlines', 3, 9);
expect(result, 'ingw');
});
test('getStringBetweenStrings', () {
const data = 'STArT I want this string END';
final result = getStringBetweenStrings(data, 'STArT', 'END');
expect(result, ' I want this string ');
});
test('getIntBetweenStrings', () {
final result = getIntBetweenStrings(kanjiPage, '<strong>', '</strong> strokes');
expect(result, 10);
});
test('getAllGlobalGroupMatches', () {
});
test('parseAnchorsToArray', () {
const htmlCode =
'''
<div class="test">
<p>
<a href="https://test.test">Hello</a>
</p>
<a href="//xyz">Hi</a>
<span>
<p>
<a href="">How are you doing</a>
</p>
</span>
</div>
''';
final result = parseAnchorsToArray(htmlCode);
expect(result, [
'Hello', 'Hi', 'How are you doing']);
});
test('getYomi', () {
final result = getYomi(kanjiPage, 'On');
expect(result, ['']);
});
test('getKunyomi', () {
final result = getKunyomi(kanjiPage);
expect(result, ['とき', '-どき']);
});
test('getOnyomi', () {
final result = getOnyomi(kanjiPage);
expect(result, ['']);
});
test('getYomiExamples', () {
final result = getYomiExamples(kanjiPage, 'On');
expect(
json.encode(result),
json.encode([
YomiExample(
example: '',
reading: '',
meaning: '''hour, o'clock, (specified) time, when ..., during ...'''
),
YomiExample(
example: '時価',
reading: 'ジカ',
meaning: 'current value, price, market value'
),
YomiExample(
example: '零時',
reading: 'レイジ',
meaning: '''twelve o'clock, midnight, noon'''
),
YomiExample(
example: '平時',
reading: 'ヘイジ',
meaning: 'peacetime, time of peace, ordinary times, normal times'
),
])
);
});
test('getOnyomiExamples', () {
final result = getOnyomiExamples(kanjiPage);
expect(
json.encode(result),
json.encode([
YomiExample(
example: '',
reading: '',
meaning: '''hour, o'clock, (specified) time, when ..., during ...'''
),
YomiExample(
example: '時価',
reading: 'ジカ',
meaning: 'current value, price, market value'
),
YomiExample(
example: '零時',
reading: 'レイジ',
meaning: '''twelve o'clock, midnight, noon'''
),
YomiExample(
example: '平時',
reading: 'ヘイジ',
meaning: 'peacetime, time of peace, ordinary times, normal times'
),
])
);
});
test('getKunyomiExamples', () {
final result = getKunyomiExamples(kanjiPage);
expect(
json.encode(result),
json.encode([
YomiExample(
example: '',
reading: 'とき',
meaning: 'time, hour, moment, occasion, case, chance, opportunity, season, the times, the age, the day, tense'
),
YomiExample(
example: '時折',
reading: 'ときおり',
meaning: 'sometimes'
),
YomiExample(
example: '切り替え時',
reading: 'きりかえとき',
meaning: 'time to switch over, response time'
),
YomiExample(
example: '逢魔が時',
reading: 'おうまがとき',
meaning: '''twilight, time for disasters (similar to 'the witching hour' but not midnight)'''
),
])
);
});
test('getRadical', () {
final result = getRadical(kanjiPage);
expect(
json.encode(result),
json.encode(Radical(
symbol: '',
meaning: 'sun, day'
))
);
});
test('getParts', () {
final result = getParts(kanjiPage);
expect(result, ['', '', '']);
});
test('getSvgUri', () {
final result = getSvgUri(kanjiPage);
expect(result, 'http://d1w6u4xc3l95km.cloudfront.net/kanji-2015-03/06642.svg');
});
test('getGifUri', () {
final result = getGifUri(kanjiPage);
expect(result, 'https://raw.githubusercontent.com/mistval/kanji_images/master/gifs/3c.gif');
});
test('getNewspaperFrequencyRank', () {
final result = getNewspaperFrequencyRank(kanjiPage);
expect(result, 16);
});
test('parseKanjiPageData', () {
final result = parseKanjiPageData(kanjiPage, '');
final expectedResult = KanjiResult();
expectedResult.query = '';
expectedResult.found = true;
expectedResult.taughtIn = 'grade 2';
expectedResult.jlptLevel = 'N5';
expectedResult.newspaperFrequencyRank = 16;
expectedResult.strokeCount = 10;
expectedResult.meaning = 'time, hour';
expectedResult.kunyomi = ['とき', '-どき'];
expectedResult.onyomi = [''];
expectedResult.onyomiExamples =
[
YomiExample(
example: '',
reading: '',
meaning: '''hour, o'clock, (specified) time, when ..., during ...'''
),
YomiExample(
example: '時価',
reading: 'ジカ',
meaning: 'current value, price, market value'
),
YomiExample(
example: '零時',
reading: 'レイジ',
meaning: '''twelve o'clock, midnight, noon'''
),
YomiExample(
example: '平時',
reading: 'ヘイジ',
meaning: 'peacetime, time of peace, ordinary times, normal times'
),
];
expectedResult.kunyomiExamples =
[
YomiExample(
example: '',
reading: 'とき',
meaning: 'time, hour, moment, occasion, case, chance, opportunity, season, the times, the age, the day, tense'
),
YomiExample(
example: '時折',
reading: 'ときおり',
meaning: 'sometimes'
),
YomiExample(
example: '切り替え時',
reading: 'きりかえとき',
meaning: 'time to switch over, response time'
),
YomiExample(
example: '逢魔が時',
reading: 'おうまがとき',
meaning: '''twilight, time for disasters (similar to 'the witching hour' but not midnight)'''
),
];
expectedResult.radical =
Radical(
symbol: '',
meaning: 'sun, day'
);
expectedResult.parts = ['', '', ''];
expectedResult.strokeOrderDiagramUri = 'http://classic.jisho.org/static/images/stroke_diagrams/26178_frames.png';
expectedResult.strokeOrderSvgUri = 'http://d1w6u4xc3l95km.cloudfront.net/kanji-2015-03/06642.svg';
expectedResult.strokeOrderGifUri = 'https://raw.githubusercontent.com/mistval/kanji_images/master/gifs/6642.gif';
expectedResult.uri = 'http://jisho.org/search/%E6%99%82%23kanji';
expect(
json.encode(result),
json.encode(expectedResult)
);
});
/* KANJI SEARCH FUNCTION TESTS END */
}

View File

@ -1,6 +1,4 @@
{ {
"query": "車",
"expectedResult": {
"found": true, "found": true,
"query": "車", "query": "車",
"uri": "https://jisho.org/word/%E8%BB%8A", "uri": "https://jisho.org/word/%E8%BB%8A",
@ -22,7 +20,7 @@
"unlifted": "車" "unlifted": "車"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "を" "unlifted": "を"
}, },
{ {
@ -30,7 +28,7 @@
"unlifted": "運転" "unlifted": "運転"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "する" "unlifted": "する"
}, },
{ {
@ -38,11 +36,11 @@
"unlifted": "時" "unlifted": "時"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "は" "unlifted": "は"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "いくら" "unlifted": "いくら"
}, },
{ {
@ -50,27 +48,27 @@
"unlifted": "注意" "unlifted": "注意"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "して" "unlifted": "して"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "も" "unlifted": "も"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "しすぎる" "unlifted": "しすぎる"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "こと" "unlifted": "こと"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "は" "unlifted": "は"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "ない" "unlifted": "ない"
} }
] ]
@ -78,7 +76,7 @@
], ],
"definition": "car; automobile; vehicle", "definition": "car; automobile; vehicle",
"supplemental": [], "supplemental": [],
"definitionAbstract": "", "definitionAbstract": null,
"tags": [ "tags": [
"noun" "noun"
] ]
@ -88,7 +86,7 @@
"sentences": [], "sentences": [],
"definition": "wheel", "definition": "wheel",
"supplemental": [], "supplemental": [],
"definitionAbstract": "", "definitionAbstract": null,
"tags": [ "tags": [
"noun" "noun"
] ]
@ -97,4 +95,3 @@
"otherForms": [], "otherForms": [],
"notes": [] "notes": []
} }
}

View File

@ -1,6 +1,4 @@
{ {
"query": "日本人",
"expectedResult": {
"found": true, "found": true,
"query": "日本人", "query": "日本人",
"uri": "https://jisho.org/word/%E6%97%A5%E6%9C%AC%E4%BA%BA", "uri": "https://jisho.org/word/%E6%97%A5%E6%9C%AC%E4%BA%BA",
@ -13,7 +11,7 @@
"sentences": [], "sentences": [],
"definition": "Japanese person; Japanese people", "definition": "Japanese person; Japanese people",
"supplemental": [], "supplemental": [],
"definitionAbstract": "", "definitionAbstract": null,
"tags": [ "tags": [
"noun" "noun"
] ]
@ -37,4 +35,3 @@
], ],
"notes": [] "notes": []
} }
}

View File

@ -1,6 +1,4 @@
{ {
"query": "皆",
"expectedResult": {
"found": true, "found": true,
"query": "皆", "query": "皆",
"uri": "https://jisho.org/word/%E7%9A%86", "uri": "https://jisho.org/word/%E7%9A%86",
@ -17,15 +15,15 @@
"japanese": "ここにいた人々はみんな行ってしまった。", "japanese": "ここにいた人々はみんな行ってしまった。",
"pieces": [ "pieces": [
{ {
"lifted": "", "lifted": null,
"unlifted": "ここ" "unlifted": "ここ"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "に" "unlifted": "に"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "いた" "unlifted": "いた"
}, },
{ {
@ -33,11 +31,11 @@
"unlifted": "人々" "unlifted": "人々"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "は" "unlifted": "は"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "みんな" "unlifted": "みんな"
}, },
{ {
@ -45,7 +43,7 @@
"unlifted": "行って" "unlifted": "行って"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "しまった" "unlifted": "しまった"
} }
] ]
@ -55,7 +53,7 @@
"supplemental": [ "supplemental": [
"Usually written using kana alone" "Usually written using kana alone"
], ],
"definitionAbstract": "", "definitionAbstract": null,
"tags": [ "tags": [
"adverb", "adverb",
"noun" "noun"
@ -69,15 +67,15 @@
"japanese": "ごちそうはみんな彼が来ないうちに食べられてしまった。", "japanese": "ごちそうはみんな彼が来ないうちに食べられてしまった。",
"pieces": [ "pieces": [
{ {
"lifted": "", "lifted": null,
"unlifted": "ごちそう" "unlifted": "ごちそう"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "は" "unlifted": "は"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "みんな" "unlifted": "みんな"
}, },
{ {
@ -85,7 +83,7 @@
"unlifted": "彼" "unlifted": "彼"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "が" "unlifted": "が"
}, },
{ {
@ -93,7 +91,7 @@
"unlifted": "来" "unlifted": "来"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "ないうちに" "unlifted": "ないうちに"
}, },
{ {
@ -101,7 +99,7 @@
"unlifted": "食べられて" "unlifted": "食べられて"
}, },
{ {
"lifted": "", "lifted": null,
"unlifted": "しまった" "unlifted": "しまった"
} }
] ]
@ -111,7 +109,7 @@
"supplemental": [ "supplemental": [
"Usually written using kana alone" "Usually written using kana alone"
], ],
"definitionAbstract": "", "definitionAbstract": null,
"tags": [ "tags": [
"adverb", "adverb",
"noun" "noun"
@ -132,4 +130,3 @@
"皆んな: Irregular okurigana usage." "皆んな: Irregular okurigana usage."
] ]
} }
}

View File

@ -1,6 +1,4 @@
{ {
"query": "ネガティブ",
"expectedResult": {
"found": true, "found": true,
"query": "ネガティブ", "query": "ネガティブ",
"uri": "https://jisho.org/word/%E3%83%8D%E3%82%AC%E3%83%86%E3%82%A3%E3%83%96", "uri": "https://jisho.org/word/%E3%83%8D%E3%82%AC%E3%83%86%E3%82%A3%E3%83%96",
@ -15,7 +13,7 @@
"supplemental": [ "supplemental": [
"Antonym: ポジティブ" "Antonym: ポジティブ"
], ],
"definitionAbstract": "", "definitionAbstract": null,
"tags": [ "tags": [
"na-adjective" "na-adjective"
] ]
@ -25,7 +23,7 @@
"sentences": [], "sentences": [],
"definition": "negative (photography)", "definition": "negative (photography)",
"supplemental": [], "supplemental": [],
"definitionAbstract": "", "definitionAbstract": null,
"tags": [ "tags": [
"noun" "noun"
] ]
@ -37,7 +35,7 @@
"sentences": [], "sentences": [],
"definition": "negative (electrical polarity)", "definition": "negative (electrical polarity)",
"supplemental": [], "supplemental": [],
"definitionAbstract": "", "definitionAbstract": null,
"tags": [ "tags": [
"noun" "noun"
] ]
@ -55,15 +53,17 @@
], ],
"otherForms": [ "otherForms": [
{ {
"kanji": "ネガティヴ" "kanji": "ネガティヴ",
"kana": null
}, },
{ {
"kanji": "ネガチブ" "kanji": "ネガチブ",
"kana": null
}, },
{ {
"kanji": "ネガチィブ" "kanji": "ネガチィブ",
"kana": null
} }
], ],
"notes": [] "notes": []
} }
}

View File

@ -1,7 +1,9 @@
{ {
"found": false,
"query": "grlgmregmneriireg", "query": "grlgmregmneriireg",
"expectedResult": { "uri": null,
"query": "grlgmregmneriireg", "tags": null,
"found": false "meanings": null,
} "otherForms": null,
"notes": null
} }

View File

@ -3,7 +3,6 @@ import 'package:path/path.dart' as path;
import 'dart:convert'; import 'dart:convert';
import 'package:unofficial_jisho_api/unofficial_jisho_api.dart'; import 'package:unofficial_jisho_api/unofficial_jisho_api.dart';
import 'local_function_test_cases.dart' show test_local_functions;
import 'package:test/test.dart'; import 'package:test/test.dart';
final jisho = JishoApi(); final jisho = JishoApi();
@ -14,41 +13,19 @@ List<String> getFilePaths(String dirname) {
return filenames.map((filename) => path.join(currentdir, 'test', dirname, filename.path)).toList(); return filenames.map((filename) => path.join(currentdir, 'test', dirname, filename.path)).toList();
} }
void runTestCases(List<String> testCaseFiles, String apiFunction) async { void runTestCases(List<String> testCaseFiles, Function apiFunction) async {
for (var testCount = 0; testCount < testCaseFiles.length; testCount++) { for (var testCount = 0; testCount < testCaseFiles.length; testCount++) {
final file = await File(testCaseFiles[testCount]).readAsString(); final file = await File(testCaseFiles[testCount]).readAsString();
final testCase = jsonDecode(file); final testCase = jsonDecode(file);
await test('Test ${testCount}', () async { await test('Test ${testCount}', () async {
switch(apiFunction) { final result = await apiFunction(testCase['query']);
case 'searchForKanji': { expect(jsonEncode(result), jsonEncode(testCase));
final result = await jisho.searchForKanji(testCase['query']);
expect(result.toJson(), testCase['expectedResult']);
break;
}
case 'searchForExamples': {
final result = await jisho.searchForExamples(testCase['query']);
expect(result, testCase['expectedResult']);
break;
}
case 'scrapeForPhrase': {
final result = await jisho.scrapeForPhrase(testCase['query']);
expect(result, testCase['expectedResult']);
break;
}
throw 'No API function provided';
}
}); });
} }
} }
void main() async { void main() async {
await runTestCases(getFilePaths('kanji_test_cases'), jisho.searchForKanji);
await test_local_functions(); await runTestCases(getFilePaths('example_test_cases'), jisho.searchForExamples);
await runTestCases(getFilePaths('phrase_scrape_test_cases'), jisho.scrapeForPhrase);
await runTestCases(getFilePaths('kanji_test_cases'), 'searchForKanji');
await runTestCases(getFilePaths('example_test_cases'), 'searchForExamples');
await runTestCases(getFilePaths('phrase_scrape_test_cases'), 'scrapeForPhrase');
} }