show the exact inflection results seperate from the fuzzy inflection results

This commit is contained in:
adueck 2022-12-22 17:58:34 +05:00
parent ffef1dd4bb
commit 229599860a
6 changed files with 145 additions and 57 deletions

View File

@ -115,8 +115,7 @@ class App extends Component<RouteComponentProps, State> {
language: "Pashto",
searchType: "fuzzy",
searchBarStickyFocus: false,
theme: /* istanbul ignore next */ (window.matchMedia &&
window.matchMedia("(prefers-color-scheme: dark)").matches) ? "dark" : "light",
theme: (window.matchMedia?.("(prefers-color-scheme: dark)").matches) ? "dark" : "light",
textOptionsRecord: {
lastModified: Date.now() as AT.TimeStamp,
textOptions: defaultTextOptions,
@ -224,32 +223,50 @@ class App extends Component<RouteComponentProps, State> {
// shortcuts to isolote word in search results
Mousetrap.bind(["1", "2", "3", "4", "5", "6", "7", "8", "9"], (e) => {
e.preventDefault();
if (e.repeat) return;
if (this.props.location.pathname !== "/search") return;
if (e.repeat) {
return;
}
if (this.props.location.pathname !== "/search") {
return;
}
const toIsolate = this.state.results[Number(e.key) - 1];
if (!toIsolate) return;
if (!toIsolate) {
return;
}
this.handleIsolateEntry(toIsolate.ts);
})
Mousetrap.bind(["ctrl+down", "ctrl+up", "command+down", "command+up"], (e) => {
e.preventDefault();
if (e.repeat) return;
if (e.repeat) {
return;
}
this.handleOptionsUpdate({ type: "toggleLanguage" });
});
Mousetrap.bind(["ctrl+b", "command+b"], (e) => {
e.preventDefault();
if (e.repeat) return;
if (e.repeat) {
return;
}
this.handleSearchValueChange("");
});
Mousetrap.bind(["ctrl+i", "command+i"], (e) => {
e.preventDefault();
if (e.repeat) return;
if (!this.state.searchValue) return;
if (e.repeat) {
return;
}
if (!this.state.searchValue) {
return;
}
this.handleInflectionSearch();
});
Mousetrap.bind(["ctrl+s", "command+s"], (e) => {
if (this.state.user?.level === "basic") return;
if (this.state.user?.level === "basic") {
return;
}
e.preventDefault();
if (!this.state.isolatedEntry) return;
if (!this.state.isolatedEntry) {
return;
}
const toAdd = {
entry: this.state.isolatedEntry,
notes: "",
@ -258,8 +275,12 @@ class App extends Component<RouteComponentProps, State> {
});
Mousetrap.bind(["ctrl+\\", "command+\\"], (e) => {
e.preventDefault();
if (e.repeat) return;
if (this.state.user?.level === "basic") return;
if (e.repeat) {
return;
}
if (this.state.user?.level === "basic") {
return;
}
if (this.props.location.pathname !== "/wordlist") {
this.props.history.push("/wordlist");
} else {
@ -319,8 +340,12 @@ class App extends Component<RouteComponentProps, State> {
try {
const prevUser = this.state.user;
const user = await getUser();
if (user === "offline") return;
if (user) sendSubmissions();
if (user === "offline") {
return;
}
if (user) {
sendSubmissions();
}
if (!user) {
if (this.state.user) {
console.log("setting state user because user is newly undefined");
@ -652,7 +677,7 @@ class App extends Component<RouteComponentProps, State> {
state={this.state}
optionsDispatch={this.handleOptionsUpdate}
handleSearchValueChange={this.handleSearchValueChange}
onBottom
onBottom={true}
/>}
</footer>
</div>;

View File

@ -46,7 +46,7 @@ function InflectionFormMatchDisplay(
<div className="mb-2"><strong>{displayFormResult(form.path)}</strong></div>
{form.matches.map((match, i) => <div className="ml-2" key={i}>
<InlinePs opts={textOptions}>{match.ps}</InlinePs>
<div className="ml-3 my-2">
<div className="ml-4 my-2">
<em>
{(transitivity === "grammatically transitive" && isPast)
? "Always 3rd pers. masc. plur."

View File

@ -51,7 +51,7 @@ const SearchBar = ({ state, optionsDispatch, handleSearchValueChange, onBottom }
data-testid="languageToggle"
>
<div aria-label={`language-choice-${language === "Pashto" ? "ps-to-en" : "en-to-ps"}`}>
Ps <span className={`fa fa-arrow-${arrowDirection}`} ></span> En
Ps <span className={`fa fa-arrow-${arrowDirection}`} /> En
</div>
</button>
);
@ -64,7 +64,7 @@ const SearchBar = ({ state, optionsDispatch, handleSearchValueChange, onBottom }
onClick={() => optionsDispatch({ type: "toggleSearchType" })}
data-testid="searchTypeToggle"
>
<span className={`fa fa-${icon}`} ></span>
<span className={`fa fa-${icon}`} />
</button>
);
};
@ -73,7 +73,7 @@ const SearchBar = ({ state, optionsDispatch, handleSearchValueChange, onBottom }
? "Browse alphabetically"
: `Search ${state.options.language === "Pashto" ? "Pashto" : "English"}`;
return (
<nav className={`navbar bg-light${!onBottom ? " fixed-top" : ""}`} style={{ zIndex: 50, width: "100%" }}>
<nav className={`navbar bg-light${onBottom ? "" : " fixed-top"}`} style={{ zIndex: 50, width: "100%" }}>
<div className="form-inline my-1 my-lg-1">
<div className="input-group">
<input

View File

@ -1,6 +1,6 @@
import { searchPile } from "../lib/search-pile";
import {
isNounAdjOrVerb, removeAccents,
isNounAdjOrVerb, removeAccents, standardizePashto,
} from "@lingdocs/ps-react";
import { dictionary } from "../lib/dictionary";
import {
@ -26,7 +26,65 @@ const relevancySorter = new relevancy.Sorter();
// That's so much better I'm removing the option of skipping compounds
// ~4th iteration:~ ignore perfective or imperfective if wasn't present in verb info (not worth it - scrapped)
export function searchAllInflections(allDocs: T.DictionaryEntry[], searchValue: string): InflectionSearchResult[] {
export function searchAllInflections(allDocs: T.DictionaryEntry[], searchValue: string): {
exact: InflectionSearchResult[],
fuzzy: InflectionSearchResult[],
} {
// pretty ugly function to seperate the exact and fuzzy inflection result matches
function getEntryMatchIndex(r: InflectionSearchResult[], entry: T.DictionaryEntry): number {
return r.findIndex(rs => rs.entry.ts === entry.ts);
}
function getFormIndex(r: InflectionSearchResult, path: string[]): number {
const joinedPath = path.join("");
return r.forms.findIndex(rs => rs.path.join("") === joinedPath);
}
const v = standardizePashto(searchValue);
const allResults = searchAllInflectionsCore(allDocs, searchValue);
const results: { exact: InflectionSearchResult[], fuzzy: InflectionSearchResult[] } = {
exact: [],
fuzzy: [],
};
allResults.forEach((result) => {
const entryMatches: { exact: InflectionSearchResult[], fuzzy: InflectionSearchResult[] } = {
exact: [],
fuzzy: [],
};
result.forms.forEach((form) => {
form.matches.forEach((match) => {
if (match.ps.p === v || match.ps.f === v) {
addToEntryMatches("exact");
} else {
addToEntryMatches("fuzzy");
}
function addToEntryMatches(t: "exact" | "fuzzy") {
let entryMatchIndex = getEntryMatchIndex(entryMatches[t], result.entry);
if (entryMatchIndex === -1) {
entryMatches[t].push({
entry: result.entry,
forms: [],
});
entryMatchIndex = entryMatches[t].length - 1;
}
let formIndex = getFormIndex(entryMatches[t][entryMatchIndex], form.path);
if (formIndex === -1) {
entryMatches[t][entryMatchIndex].forms.push({
path: form.path,
matches: [match],
});
formIndex = entryMatches[t][entryMatchIndex].forms.length - 1;
} else {
entryMatches[t][entryMatchIndex].forms[formIndex].matches.push(match);
}
}
})
});
results.exact.push(...entryMatches.exact);
results.fuzzy.push(...entryMatches.fuzzy);
});
return results;
}
export function searchAllInflectionsCore(allDocs: T.DictionaryEntry[], searchValue: string): InflectionSearchResult[] {
const index = isPashtoScript(searchValue) ? "p" : "f"
function sortResultsByRelevancy(arr: InflectionSearchResult[]): InflectionSearchResult[] {
return relevancySorter.sort(arr, searchValue, (obj: InflectionSearchResult, calc: any) => (
@ -65,7 +123,7 @@ export function searchAllInflections(allDocs: T.DictionaryEntry[], searchValue:
);
const preSearchFun = (ps: T.PsString) => !!ps[script].match(begRegex);
const searchRegex = new RegExp(
makeAWeeBitFuzzy(searchValue, script, true) + "$",
`${makeAWeeBitFuzzy(searchValue, script, true)}$`,
"i",
);
// add little bit fuzzy
@ -110,21 +168,18 @@ export function searchAllInflections(allDocs: T.DictionaryEntry[], searchValue:
}, []);
// console.timeEnd(timerLabel);
// TODO!!: Sorting on this as well
if (["را", "ور", "در"].includes(searchValue.slice(0, 2))) {
return [
const allResults = (["را", "ور", "در"].includes(searchValue.slice(0, 2)))
? [
...results,
// also search without the directionary pronoun
...searchAllInflections(allDocs, searchValue.slice(2)),
];
}
if (["raa", "war", "dar", "wăr", "dăr"].includes(searchValue.slice(0, 3))) {
return [
// also search without the directional pronoun
...searchAllInflectionsCore(allDocs, searchValue.slice(2)),
] : (["raa", "war", "dar", "wăr", "dăr"].includes(searchValue.slice(0, 3)))
? [
...results,
// also search without the directionary pronoun
...searchAllInflections(allDocs, searchValue.slice(3)),
];
}
// also search without the directional pronoun
...searchAllInflectionsCore(allDocs, searchValue.slice(3)),
] : results;
// because we used a bit of a fuzzy search, sort the results by relevancy
// this is a bit complicated...
return sortResultsByRelevancy(results.map(sortMatchesByRelevancy));
return sortResultsByRelevancy(allResults.map(sortMatchesByRelevancy));
}

View File

@ -88,31 +88,36 @@ function Results({ state, isolateEntry, handleInflectionSearch }: {
{inflectionResults === "searching" && <div>
<p className="lead mt-1">Searching conjugations/inflections... <i className="fas fa-hourglass-half" /></p>
</div>}
{Array.isArray(inflectionResults) && <div>
{inflectionResults && inflectionResults !== "searching" && <div>
<h4 className="mt-1 mb-3">Conjugation/Inflection Results</h4>
{inflectionResults.length === 0 && <div className="mt-4">
{inflectionResults.exact.length === 0 && inflectionResults.fuzzy.length === 0 && <div className="mt-4">
<div>No conjugation/inflection matches found for <strong>{state.searchValue}</strong></div>
</div>}
{inflectionResults.map((p) => (
<div key={p.entry.ts}>
<Entry
key={p.entry.i}
entry={p.entry}
textOptions={textOptions}
isolateEntry={isolateEntry}
/>
<div className="mb-3 ml-2">
{p.forms.map((form, i) => (
<InflectionFormMatchDisplay
key={"inf-form" + i}
textOptions={textOptions}
form={form}
{(["exact", "fuzzy"] as ("exact" | "fuzzy")[]).map((t) => {
return (inflectionResults[t].length !== 0) ? <>
<h5 className="mb-3">{t === "exact" ? "Exact" : "Approximate"} Matches</h5>
{inflectionResults[t].map((p) => (
<div key={p.entry.ts}>
<Entry
key={p.entry.i}
entry={p.entry}
textOptions={textOptions}
isolateEntry={isolateEntry}
/>
))}
</div>
</div>
))}
<div className="mb-3 ml-2">
{p.forms.map((form, i) => (
<InflectionFormMatchDisplay
key={`inf-form${i}`}
textOptions={textOptions}
form={form}
entry={p.entry}
/>
))}
</div>
</div>
))}
</> : null;
})}
</div>}
{inflectionResults === undefined && suggestionState === "none" && state.results.map((entry) => (
<Entry

View File

@ -11,7 +11,10 @@ export type State = {
reviewTasks: import("./functions-types").ReviewTask[],
dictionaryInfo: import("@lingdocs/ps-react").Types.DictionaryInfo | undefined,
user: undefined | import("./account-types").LingdocsUser,
inflectionSearchResults: undefined | "searching" | InflectionSearchResult[],
inflectionSearchResults: undefined | "searching" | {
exact: InflectionSearchResult[],
fuzzy: InflectionSearchResult[],
},
}
export type DictionaryAPI = {