Skip to content
This repository was archived by the owner on Jul 17, 2024. It is now read-only.

Commit 0c1f6cc

Browse files
committed
feat: sync ConstraintAnalysis API
1 parent c53c04c commit 0c1f6cc

File tree

2 files changed

+47
-1
lines changed

2 files changed

+47
-1
lines changed

tests/test_solution_manager.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,3 +216,43 @@ def test_score_manager_constraint_analysis_map():
216216

217217
constraint_analysis = score_analysis.constraint_analysis(ConstraintRef('package', 'Maximize Value'))
218218
assert constraint_analysis.constraint_name == 'Maximize Value'
219+
assert constraint_analysis.match_count == 3
220+
221+
222+
ignored_java_functions = {
223+
'equals',
224+
'getClass',
225+
'hashCode',
226+
'notify',
227+
'notifyAll',
228+
'toString',
229+
'wait',
230+
'compareTo',
231+
}
232+
233+
234+
def test_has_all_methods():
235+
missing = []
236+
for python_type, java_type in ((ScoreExplanation, JavaScoreExplanation),
237+
(ScoreAnalysis, JavaScoreAnalysis),
238+
(ConstraintAnalysis, JavaConstraintAnalysis),
239+
(ScoreExplanation, JavaScoreExplanation),
240+
(ConstraintMatch, JavaConstraintMatch),
241+
(ConstraintMatchTotal, JavaConstraintMatchTotal),
242+
(Indictment, JavaIndictment)):
243+
for function_name, function_impl in inspect.getmembers(java_type, inspect.isfunction):
244+
if function_name in ignored_java_functions:
245+
continue
246+
247+
snake_case_name = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', function_name)
248+
snake_case_name = re.sub('([a-z0-9])([A-Z])', r'\1_\2', snake_case_name).lower()
249+
snake_case_name_without_prefix = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', function_name[3:] if function_name.startswith("get") else function_name)
250+
snake_case_name_without_prefix = re.sub('([a-z0-9])([A-Z])', r'\1_\2', snake_case_name_without_prefix).lower()
251+
if not hasattr(python_type, snake_case_name) and not hasattr(python_type, snake_case_name_without_prefix):
252+
missing.append((java_type, python_type, snake_case_name))
253+
254+
if missing:
255+
assertion_msg = ''
256+
for java_type, python_type, snake_case_name in missing:
257+
assertion_msg += f'{python_type} is missing a method ({snake_case_name}) from java_type ({java_type}).)\n'
258+
raise AssertionError(assertion_msg)

timefold-solver-python-core/src/main/python/score/_score_analysis.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -446,9 +446,11 @@ class ConstraintAnalysis(Generic[Score_]):
446446
but still non-zero constraint weight; non-empty if constraint has matches.
447447
This is a list to simplify access to individual elements,
448448
but it contains no duplicates just like `set` wouldn't.
449-
summary : str
449+
summarize : str
450450
Returns a diagnostic text
451451
that explains part of the score quality through the ConstraintAnalysis API.
452+
match_count : int
453+
Return the match count of the constraint.
452454
"""
453455
_delegate: '_JavaConstraintAnalysis[Score_]'
454456

@@ -481,6 +483,10 @@ def matches(self) -> list[MatchAnalysis[Score_]]:
481483
return [MatchAnalysis(match_analysis)
482484
for match_analysis in cast(list['_JavaMatchAnalysis[Score_]'], self._delegate.matches())]
483485

486+
@property
487+
def match_count(self) -> int:
488+
return self._delegate.matchCount()
489+
484490
@property
485491
def score(self) -> Score_:
486492
return to_python_score(self._delegate.score())

0 commit comments

Comments
 (0)