|
|
|
@ -1,7 +1,12 @@ |
|
|
|
|
using System ; |
|
|
|
|
using System.Text ; |
|
|
|
|
using System.IO ; |
|
|
|
|
using System.Collections.Generic ; |
|
|
|
|
using System.Drawing ; |
|
|
|
|
|
|
|
|
|
using Manina.Windows.Forms ; |
|
|
|
|
using Newtonsoft.Json.Linq ; |
|
|
|
|
using log4net ; |
|
|
|
|
|
|
|
|
|
// -------------------------------------------------------------------- |
|
|
|
|
|
|
|
|
@ -9,15 +14,25 @@ public class ChartImage |
|
|
|
|
{ |
|
|
|
|
private string mFullPath ; |
|
|
|
|
private dynamic mConfig ; |
|
|
|
|
private HashSet<string> mKeywords = new HashSet<string>() ; |
|
|
|
|
private Image mImage ; |
|
|
|
|
private ImageListViewItem mImageListViewItem ; |
|
|
|
|
|
|
|
|
|
public ChartImage( string key, string fullPath ) |
|
|
|
|
{ |
|
|
|
|
// initialize the ChartImage |
|
|
|
|
mFullPath = fullPath ; |
|
|
|
|
mConfig = Program.dataConfig.data[ key ] ; |
|
|
|
|
if ( mConfig == null ) |
|
|
|
|
mConfig = new JObject() ; |
|
|
|
|
mImage = Image.FromFile( fullPath ) ; |
|
|
|
|
|
|
|
|
|
// prepare for search scoring |
|
|
|
|
if ( mConfig["keywords"] != null ) { |
|
|
|
|
foreach( string kywd in mConfig["keywords"] ) |
|
|
|
|
mKeywords.Add( kywd.ToUpper() ) ; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// NOTE: Thumbnails are cached by ImageListViewItem GUID, so we reuse these objects, |
|
|
|
|
// instead of creating new ones every time we reload the search results. |
|
|
|
|
mImageListViewItem = new ImageListViewItem( fullPath ) ; |
|
|
|
@ -25,6 +40,90 @@ public class ChartImage |
|
|
|
|
mImageListViewItem.Text = caption() ; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public float getSearchScore( string searchQuery ) |
|
|
|
|
{ |
|
|
|
|
// initialize |
|
|
|
|
searchQuery = searchQuery.ToUpper() ; |
|
|
|
|
List< Tuple<string,float> > scores = new List<Tuple<string,float>>() ; |
|
|
|
|
ILog logger = LogManager.GetLogger( "search" ) ; |
|
|
|
|
if ( logger.IsDebugEnabled ) { |
|
|
|
|
if ( mKeywords.Count == 0 ) |
|
|
|
|
logger.Debug( $"- \"{this.caption()}\": (no keywords)" ) ; |
|
|
|
|
else { |
|
|
|
|
StringBuilder buf2 = new StringBuilder() ; |
|
|
|
|
foreach( string kywd in mKeywords ) |
|
|
|
|
buf2.Append( $"{kywd} ; " ) ; |
|
|
|
|
string val = buf2.ToString() ; |
|
|
|
|
logger.Debug( $"- \"{this.caption()}\": [ {val.Substring(0,val.Length-3)} ]" ) ; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// initialize the search score weights |
|
|
|
|
float exactKeywordMatchScore = Program.appConfig.getFloatVal( new string[]{"search","exactKeywordMatchScore"}, 10f ) ; |
|
|
|
|
float leadingPartialKeywordMatchScore = Program.appConfig.getFloatVal( new string[]{"search","leadingPartialKeywordMatchScore"}, 2f ) ; |
|
|
|
|
float internalPartialKeywordMatchScore = Program.appConfig.getFloatVal( new string[]{"search","internalPartialKeywordMatchScore"}, 1.5f ) ; |
|
|
|
|
float exactCaptionMatchScore = Program.appConfig.getFloatVal( new string[]{"search","exactCaptionMatchScore"}, 5f ) ; |
|
|
|
|
float leadingPartialCaptionMatchScore = Program.appConfig.getFloatVal( new string[]{"search","leadingPartialCaptionMatchScore"}, 1f ) ; |
|
|
|
|
float internalPartialCaptionMatchScore = Program.appConfig.getFloatVal( new string[]{"search","internalPartialCaptionMatchScore"}, 0.5f ) ; |
|
|
|
|
|
|
|
|
|
// look for keyword matches |
|
|
|
|
foreach ( string kywd in mKeywords ) { |
|
|
|
|
if ( kywd == searchQuery ) { |
|
|
|
|
scores.Add( new Tuple<string,float>( "exactKeywordMatch", exactKeywordMatchScore ) ) ; |
|
|
|
|
continue ; |
|
|
|
|
} |
|
|
|
|
int pos = kywd.IndexOf( searchQuery ) ; |
|
|
|
|
if ( pos == 0 ) { |
|
|
|
|
scores.Add( new Tuple<string,float>( |
|
|
|
|
$"leadingPartialKeywordMatch[{kywd}]", |
|
|
|
|
Math.Min( leadingPartialKeywordMatchScore * searchQuery.Length, exactKeywordMatchScore ) |
|
|
|
|
) ) ; |
|
|
|
|
} else if ( pos > 0 ) { |
|
|
|
|
scores.Add( new Tuple<string,float>( |
|
|
|
|
$"internalPartialKeywordMatch[{kywd}]", |
|
|
|
|
Math.Min( internalPartialKeywordMatchScore * searchQuery.Length, exactKeywordMatchScore ) |
|
|
|
|
) ) ; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// look for caption matches |
|
|
|
|
string caption = this.caption().ToUpper() ; |
|
|
|
|
if ( searchQuery == caption ) |
|
|
|
|
scores.Add( new Tuple<string,float>( "exactCaptionMatch", exactCaptionMatchScore ) ) ; |
|
|
|
|
else { |
|
|
|
|
int pos = caption.IndexOf( searchQuery ) ; |
|
|
|
|
if ( pos == 0 ) { |
|
|
|
|
scores.Add( new Tuple<string,float>( |
|
|
|
|
$"leadingPartialCaptionMatch[{caption}]", |
|
|
|
|
Math.Min( leadingPartialCaptionMatchScore * searchQuery.Length, exactCaptionMatchScore ) |
|
|
|
|
) ) ; |
|
|
|
|
} else if ( pos > 0 ) { |
|
|
|
|
scores.Add( new Tuple<string,float>( |
|
|
|
|
$"internalPartialCaptionMatch[{caption}]", |
|
|
|
|
Math.Min( internalPartialCaptionMatchScore * searchQuery.Length, exactCaptionMatchScore ) |
|
|
|
|
) ) ; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// calculate the total score |
|
|
|
|
float totalScore = 0 ; |
|
|
|
|
StringBuilder buf = logger.IsDebugEnabled ? new StringBuilder() : null ; |
|
|
|
|
for ( int i=0 ; i < scores.Count ; ++i ) { |
|
|
|
|
totalScore += scores[i].Item2 ; |
|
|
|
|
if ( buf != null ) { |
|
|
|
|
if ( i > 0 ) |
|
|
|
|
buf.Append( " ; " ) ; |
|
|
|
|
buf.Append( $"{scores[i].Item1}={scores[i].Item2:F1}" ) ; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if ( totalScore > 0 ) { |
|
|
|
|
logger.Debug( $" - {buf}" ) ; |
|
|
|
|
logger.Debug( $" - totalScore = {totalScore:F1}" ) ; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return totalScore ; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public string caption() |
|
|
|
|
{ |
|
|
|
|
string caption = (mConfig != null) ? mConfig["caption"] : null ; |
|
|
|
|