🧪 Starting the matching algo
This commit is contained in:
@@ -37,6 +37,7 @@
|
|||||||
"fs-extra": "^9.1.0",
|
"fs-extra": "^9.1.0",
|
||||||
"imghash": "^0.0.8",
|
"imghash": "^0.0.8",
|
||||||
"jsdoc": "^3.6.7",
|
"jsdoc": "^3.6.7",
|
||||||
|
"pretty-bytes": "^5.6.0",
|
||||||
"react": "^17.0.1",
|
"react": "^17.0.1",
|
||||||
"react-collapsible": "^2.8.3",
|
"react-collapsible": "^2.8.3",
|
||||||
"react-dom": "^17.0.1",
|
"react-dom": "^17.0.1",
|
||||||
|
|||||||
@@ -12,7 +12,8 @@ import {
|
|||||||
} from "../constants/action-types";
|
} from "../constants/action-types";
|
||||||
|
|
||||||
import { refineQuery } from "../shared/utils/nlp.utils";
|
import { refineQuery } from "../shared/utils/nlp.utils";
|
||||||
import { assign } from "lodash";
|
import { matchScorer } from "../shared/utils/searchmatchscorer.utils";
|
||||||
|
import { assign, isNull } from "lodash";
|
||||||
|
|
||||||
export async function walkFolder(path: string): Promise<Array<IFolderData>> {
|
export async function walkFolder(path: string): Promise<Array<IFolderData>> {
|
||||||
return axios
|
return axios
|
||||||
@@ -131,8 +132,18 @@ export const fetchComicVineMatches = (searchPayload) => (dispatch) => {
|
|||||||
offset: "5",
|
offset: "5",
|
||||||
resources: "issue",
|
resources: "issue",
|
||||||
},
|
},
|
||||||
|
transformResponse: [
|
||||||
|
(r) => {
|
||||||
|
const searchMatches = JSON.parse(r);
|
||||||
|
return matchScorer(searchMatches.results, {
|
||||||
|
issue: issueSearchQuery,
|
||||||
|
series: seriesSearchQuery,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
],
|
||||||
})
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
|
console.log(response);
|
||||||
dispatch({
|
dispatch({
|
||||||
type: CV_SEARCH_SUCCESS,
|
type: CV_SEARCH_SUCCESS,
|
||||||
searchResults: response.data,
|
searchResults: response.data,
|
||||||
|
|||||||
@@ -151,6 +151,10 @@ $border-color: red;
|
|||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
}
|
}
|
||||||
.search-result-details {
|
.search-result-details {
|
||||||
|
width: 100%;
|
||||||
|
.score {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,13 +4,14 @@ import axios from "axios";
|
|||||||
import Card from "./Card";
|
import Card from "./Card";
|
||||||
import MatchResult from "./MatchResult";
|
import MatchResult from "./MatchResult";
|
||||||
import ComicVineSearchForm from "./ComicVineSearchForm";
|
import ComicVineSearchForm from "./ComicVineSearchForm";
|
||||||
import { Divider } from "antd";
|
|
||||||
import { css } from "@emotion/react";
|
import { css } from "@emotion/react";
|
||||||
import PuffLoader from "react-spinners/PuffLoader";
|
import PuffLoader from "react-spinners/PuffLoader";
|
||||||
import { isEmpty, isUndefined } from "lodash";
|
import { isEmpty, isUndefined } from "lodash";
|
||||||
import { IExtractedComicBookCoverFile, RootState } from "threetwo-ui-typings";
|
import { IExtractedComicBookCoverFile, RootState } from "threetwo-ui-typings";
|
||||||
import { fetchComicVineMatches } from "../actions/fileops.actions";
|
import { fetchComicVineMatches } from "../actions/fileops.actions";
|
||||||
import { Drawer } from "antd";
|
import { Drawer, Divider } from "antd";
|
||||||
|
const prettyBytes = require("pretty-bytes");
|
||||||
import "antd/dist/antd.css";
|
import "antd/dist/antd.css";
|
||||||
|
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
@@ -72,7 +73,7 @@ export const ComicDetail = ({}: ComicDetailProps): ReactElement => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="column">
|
<div className="column">
|
||||||
<p>{comicDetail.rawFileDetails.containedIn}</p>
|
<p>{comicDetail.rawFileDetails.containedIn}</p>
|
||||||
<p>{comicDetail.rawFileDetails.fileSize}</p>
|
<p>{prettyBytes(comicDetail.rawFileDetails.fileSize)}</p>
|
||||||
<button className="button" onClick={openDrawerWithCVMatches}>
|
<button className="button" onClick={openDrawerWithCVMatches}>
|
||||||
<span className="icon">
|
<span className="icon">
|
||||||
<i className="fas fa-magic"></i>
|
<i className="fas fa-magic"></i>
|
||||||
|
|||||||
@@ -71,8 +71,14 @@ export const ComicVineSearchForm = () => {
|
|||||||
<div className="field-body">
|
<div className="field-body">
|
||||||
<div className="field">
|
<div className="field">
|
||||||
<div className="control">
|
<div className="control">
|
||||||
<button type="submit" className="button is-info is-small">
|
<button
|
||||||
Apply Criteria
|
type="submit"
|
||||||
|
className="button is-info is-light is-outlined is-small"
|
||||||
|
>
|
||||||
|
<span className="icon">
|
||||||
|
<i className="fas fa-hand-sparkles"></i>
|
||||||
|
</span>
|
||||||
|
<span>Search</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -85,7 +91,7 @@ export const ComicVineSearchForm = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Collapsible
|
<Collapsible
|
||||||
trigger={"Search Manually"}
|
trigger={"Match Manually"}
|
||||||
triggerTagName="a"
|
triggerTagName="a"
|
||||||
triggerClassName={"is-size-6"}
|
triggerClassName={"is-size-6"}
|
||||||
triggerOpenedClassName={"is-size-6"}
|
triggerOpenedClassName={"is-size-6"}
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ const mapDispatchToProps = (dispatch) => ({
|
|||||||
getRecentlyImportedComicBooks({
|
getRecentlyImportedComicBooks({
|
||||||
paginationOptions: {
|
paginationOptions: {
|
||||||
page: 0,
|
page: 0,
|
||||||
limit: 5,
|
limit: 17,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -6,10 +6,6 @@ interface MatchResultProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const MatchResult = (props: MatchResultProps) => {
|
export const MatchResult = (props: MatchResultProps) => {
|
||||||
useEffect(() => {
|
|
||||||
console.log("match", props.matchData);
|
|
||||||
}, [props.matchData]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<table>
|
<table>
|
||||||
@@ -26,8 +22,12 @@ export const MatchResult = (props: MatchResultProps) => {
|
|||||||
<img className="cover-image" src={match.image.thumb_url} />
|
<img className="cover-image" src={match.image.thumb_url} />
|
||||||
</td>
|
</td>
|
||||||
<td className="search-result-details">
|
<td className="search-result-details">
|
||||||
|
<div className="tag score is-primary is-medium is-pulled-right">
|
||||||
|
{parseFloat(match.score).toFixed(2)}
|
||||||
|
</div>
|
||||||
<div className="is-size-5">{match.name}</div>
|
<div className="is-size-5">{match.name}</div>
|
||||||
<div className="is-size-6">{match.volume.name}</div>
|
<div className="is-size-6">{match.volume.name}</div>
|
||||||
|
|
||||||
<div className="field is-grouped is-grouped-multiline">
|
<div className="field is-grouped is-grouped-multiline">
|
||||||
<div className="control">
|
<div className="control">
|
||||||
<div className="tags has-addons">
|
<div className="tags has-addons">
|
||||||
|
|||||||
@@ -17,9 +17,10 @@ function comicinfoReducer(state = initialState, action) {
|
|||||||
inProgress: true,
|
inProgress: true,
|
||||||
};
|
};
|
||||||
case CV_SEARCH_SUCCESS:
|
case CV_SEARCH_SUCCESS:
|
||||||
|
console.log("ACTION", action);
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
searchResults: action.searchResults.results,
|
searchResults: action.searchResults,
|
||||||
searchQuery: action.searchQueryObject,
|
searchQuery: action.searchQueryObject,
|
||||||
inProgress: false,
|
inProgress: false,
|
||||||
};
|
};
|
||||||
|
|||||||
27
src/client/shared/utils/searchmatchscorer.utils.ts
Normal file
27
src/client/shared/utils/searchmatchscorer.utils.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import { each, isUndefined, isNull } from "lodash";
|
||||||
|
const stringSimilarity = require("string-similarity");
|
||||||
|
|
||||||
|
export const matchScorer = (searchMatches, searchQuery) => {
|
||||||
|
// 1. Check if it exists in the db (score: 0)
|
||||||
|
// 2. Check if issue name matches strongly (score: ++)
|
||||||
|
// 3. Check if issue number matches strongly (score: ++)
|
||||||
|
// 4. Check if issue covers hash match strongly (score: +++)
|
||||||
|
// 5. Check if issue year matches strongly (score: +)
|
||||||
|
const score = 0;
|
||||||
|
console.log("yedvadkar", searchMatches);
|
||||||
|
each(searchMatches, (match, idx) => {
|
||||||
|
if (!isNull(searchQuery.issue.meta.normalized) && !isNull(match.name)) {
|
||||||
|
const issueNameScore = stringSimilarity.compareTwoStrings(
|
||||||
|
searchQuery.issue.meta.normalized,
|
||||||
|
match.name,
|
||||||
|
);
|
||||||
|
match.score = issueNameScore;
|
||||||
|
console.log("name score" + idx + ":", issueNameScore);
|
||||||
|
} else {
|
||||||
|
console.log("match not possible");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return searchMatches;
|
||||||
|
|
||||||
|
// check for the issue name match
|
||||||
|
};
|
||||||
@@ -10360,6 +10360,11 @@ prettier@^2.2.1:
|
|||||||
resolved "https://registry.npmjs.org/prettier/-/prettier-2.3.1.tgz"
|
resolved "https://registry.npmjs.org/prettier/-/prettier-2.3.1.tgz"
|
||||||
integrity sha512-p+vNbgpLjif/+D+DwAZAbndtRrR0md0MwfmOVN9N+2RgyACMT+7tfaRnT+WDPkqnuVwleyuBIG2XBxKDme3hPA==
|
integrity sha512-p+vNbgpLjif/+D+DwAZAbndtRrR0md0MwfmOVN9N+2RgyACMT+7tfaRnT+WDPkqnuVwleyuBIG2XBxKDme3hPA==
|
||||||
|
|
||||||
|
pretty-bytes@^5.6.0:
|
||||||
|
version "5.6.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb"
|
||||||
|
integrity sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==
|
||||||
|
|
||||||
pretty-error@^2.1.1:
|
pretty-error@^2.1.1:
|
||||||
version "2.1.2"
|
version "2.1.2"
|
||||||
resolved "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.2.tgz"
|
resolved "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.2.tgz"
|
||||||
|
|||||||
Reference in New Issue
Block a user