46static std::vector<wxString>
SplitWords(
const wxString& aName )
48 std::vector<wxString> words;
55 words.push_back( word.Lower() );
60 for(
size_t ii = 0; ii < aName.length(); ++ii )
62 wxUniChar ch = aName[ii];
64 if( !wxIsalnum( ch ) )
72 wxUniChar prev = word.Last();
73 bool nextIsLower = ii + 1 < aName.length() && wxIslower( aName[ii + 1] );
76 if( ( wxIslower( prev ) && wxIsupper( ch ) ) || ( !wxIsdigit( prev ) != !wxIsdigit( ch ) )
77 || ( wxIsupper( prev ) && wxIsupper( ch ) && nextIsLower ) )
91static bool sameWord(
const wxString& aA,
const wxString& aB )
93 return aA == aB || aA + wxS(
"s" ) == aB || aB + wxS(
"s" ) == aA || aA + wxS(
"es" ) == aB
94 || aB + wxS(
"es" ) == aA;
98static bool wordsMatchAt(
const std::vector<wxString>& aWords,
const std::vector<wxString>& aTerm,
size_t aPos )
100 for(
size_t ii = 0; ii < aTerm.size(); ++ii )
102 if( !
sameWord( aWords[aPos + ii], aTerm[ii] ) )
110static wxString
joined(
const std::vector<wxString>& aWords )
114 for(
const wxString& word : aWords )
122static int MatchScore(
const wxString& aName,
const wxString& aTerm )
124 std::vector<wxString> words =
SplitWords( aName );
125 std::vector<wxString> term =
SplitWords( aTerm );
127 if( words.empty() || term.empty() )
130 if( term.size() <= words.size() )
132 size_t last = words.size() - term.size();
140 for(
size_t pos = 1; pos < last; ++pos )
148 wxString needle =
joined( term );
150 if(
name.StartsWith( needle ) ||
name.EndsWith( needle ) )
154 if( needle.length() >= 4 &&
name.Contains( needle ) )
163 if( aRole.
excluded.contains( aColumn ) )
166 wxString column = wxString::FromUTF8( aColumn );
168 for(
size_t ii = 0; ii < aRole.
remembered.size(); ++ii )
170 if( column.IsSameAs( wxString::FromUTF8( aRole.
remembered[ii] ),
false ) )
176 for(
const wxString& term : aRole.
terms )
177 best = std::max( best,
MatchScore( column, term ) );
183std::vector<std::string>
AssignColumns(
const std::vector<std::string>& aColumns,
const std::vector<ROLE>& aRoles )
186 std::vector<std::tuple<int, size_t, size_t>>
candidates;
188 for(
size_t role = 0; role < aRoles.size(); ++role )
190 for(
size_t col = 0; col < aColumns.size(); ++col )
199 std::vector<std::string> chosen( aRoles.size() );
200 std::vector<bool> roleDone( aRoles.size(),
false );
201 std::vector<bool> columnUsed( aColumns.size(),
false );
203 for(
const auto& [negScore, role, col] :
candidates )
205 if( roleDone[role] || columnUsed[col] )
208 chosen[role] = aColumns[col];
209 roleDone[role] =
true;
210 columnUsed[col] =
true;
217std::string
MatchTable(
const std::vector<std::string>& aTables,
const std::string& aLibraryName )
219 wxString
library = wxString::FromUTF8( aLibraryName );
223 for(
const std::string&
table : aTables )
225 wxString
name = wxString::FromUTF8(
table );
230 if( score > bestScore )
241std::map<std::string, size_t>
RepeatCounts(
const std::vector<std::map<std::string, std::any>>& aRows )
243 std::map<std::string, std::set<std::string>> seen;
244 std::map<std::string, size_t> repeats;
246 for(
const std::map<std::string, std::any>& row : aRows )
248 for(
const auto& [column, value] : row )
251 const std::string*
text = std::any_cast<std::string>( &value );
253 repeats.try_emplace( column, 0 );
255 if( !seen[column].insert(
text ? *
text : std::string() ).second )
264void RememberChoice( std::vector<std::string>& aHistory,
const std::string& aColumn )
266 if( aColumn.empty() )
269 wxString column = wxString::FromUTF8( aColumn );
271 std::erase_if( aHistory,
272 [&](
const std::string& aEntry )
274 return column.IsSameAs( wxString::FromUTF8( aEntry ),
false );
277 aHistory.insert( aHistory.begin(), aColumn );
Guesses the table and column mappings of a new database library from their names.
static wxString joined(const std::vector< wxString > &aWords)
std::vector< std::string > AssignColumns(const std::vector< std::string > &aColumns, const std::vector< ROLE > &aRoles)
Chooses a column for each role, best scores first, so no column serves two roles.
static std::vector< wxString > SplitWords(const wxString &aName)
Splits an identifier into lower case words: "PCBFootprint_ref2" gives pcb, footprint,...
static int roleScore(const ROLE &aRole, const std::string &aColumn)
static bool wordsMatchAt(const std::vector< wxString > &aWords, const std::vector< wxString > &aTerm, size_t aPos)
static bool sameWord(const wxString &aA, const wxString &aB)
void RememberChoice(std::vector< std::string > &aHistory, const std::string &aColumn)
Moves aColumn to the front of a role's history, dropping repeats and the oldest entries.
std::string MatchTable(const std::vector< std::string > &aTables, const std::string &aLibraryName)
@ SUBSTRING
The term is buried inside a word: "myfootprintref" for "footprint".
@ AFFIX
The name starts or ends with the term mid-word: "Identical", "partid".
@ REMEMBERED
A column the user chose before; earlier choices score lower.
@ EDGE_WORD
The term is a whole word at either end: "PartID", "symbol_ref".
@ INNER_WORD
The term is a whole word inside the name: "Main ID Code".
@ EXACT
The name is the term: "ID", "Symbols".
static int MatchScore(const wxString &aName, const wxString &aTerm)
std::map< std::string, size_t > RepeatCounts(const std::vector< std::map< std::string, std::any > > &aRows)
Counts, per column, the rows whose value already appeared in an earlier row, with NULL counted as emp...
static constexpr size_t MAX_REMEMBERED
One mapping to fill, such as the key column or the Value field.
std::vector< std::string > remembered
Columns the user chose before, most recent first.
std::set< std::string > excluded
Columns this role must not take, even when remembered.
std::vector< wxString > terms
Words to look for, in English and translated.
static std::vector< int > candidates(const SEGMENT_INDEX &aIndex, const SEG &aQuery, int aPadding)