solverforge 0.6.12__tar.gz → 0.6.13__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {solverforge-0.6.12 → solverforge-0.6.13}/Cargo.lock +1 -1
- {solverforge-0.6.12 → solverforge-0.6.13}/Cargo.toml +1 -1
- {solverforge-0.6.12 → solverforge-0.6.13}/PKG-INFO +5 -5
- {solverforge-0.6.12 → solverforge-0.6.13}/README.md +4 -4
- {solverforge-0.6.12 → solverforge-0.6.13}/pyproject.toml +1 -1
- {solverforge-0.6.12 → solverforge-0.6.13}/python/solverforge/__init__.py +1 -1
- {solverforge-0.6.12 → solverforge-0.6.13}/src/runtime/dynamic_assignment_group.rs +102 -8
- {solverforge-0.6.12 → solverforge-0.6.13}/src/state/entity_table.rs +14 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/src/state/marshal.rs +56 -1
- {solverforge-0.6.12 → solverforge-0.6.13}/LICENSE +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/python/solverforge/_native.pyi +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/python/solverforge/config.py +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/python/solverforge/console.py +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/python/solverforge/constraints.py +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/python/solverforge/decorators.py +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/python/solverforge/errors.py +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/python/solverforge/events.py +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/python/solverforge/fields.py +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/python/solverforge/groups.py +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/python/solverforge/joiner.py +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/python/solverforge/manager.py +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/python/solverforge/model.py +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/python/solverforge/py.typed +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/python/solverforge/score.py +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/python/solverforge/solver.py +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/python/solverforge/typing.py +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/python/solverforge/ui.py +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/rust-toolchain.toml +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/src/bindings.rs +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/src/callbacks/call.rs +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/src/callbacks/mod.rs +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/src/callbacks/registry.rs +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/src/callbacks/threading.rs +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/src/config.rs +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/src/constraints/compiled.rs +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/src/constraints/evaluate.rs +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/src/constraints/incremental.rs +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/src/constraints/list_precedence.rs +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/src/constraints/matches.rs +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/src/constraints/mod.rs +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/src/constraints/state.rs +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/src/constraints/stream_plan.rs +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/src/descriptor/extractor.rs +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/src/descriptor/mod.rs +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/src/descriptor/variable.rs +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/src/error.rs +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/src/intern.rs +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/src/lib.rs +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/src/manager/candidate_trace.rs +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/src/manager/events.rs +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/src/manager/jobs.rs +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/src/manager/mod.rs +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/src/manager/provenance.rs +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/src/proxy/entity.rs +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/src/proxy/list.rs +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/src/proxy/mod.rs +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/src/proxy/solution.rs +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/src/runtime/candidate_metric.rs +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/src/runtime/dynamic_provider.rs +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/src/runtime/list_slots.rs +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/src/runtime/mod.rs +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/src/runtime/scalar_slots.rs +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/src/schema/build.rs +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/src/schema/compiled.rs +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/src/schema/mod.rs +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/src/schema/parse.rs +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/src/schema/python.rs +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/src/schema/runtime_plan.rs +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/src/schema/types.rs +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/src/schema/validate.rs +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/src/score.rs +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/src/solver/api.rs +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/src/solver/console.rs +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/src/solver/mod.rs +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/src/solver/progress.rs +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/src/solver/result.rs +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/src/solver/solvable.rs +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/src/state/callback_view.rs +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/src/state/clone.rs +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/src/state/mod.rs +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/src/state/solution.rs +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/src/state/variables.rs +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/src/ui.rs +0 -0
- {solverforge-0.6.12 → solverforge-0.6.13}/src/value.rs +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: solverforge
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.13
|
|
4
4
|
Classifier: Development Status :: 3 - Alpha
|
|
5
5
|
Classifier: Intended Audience :: Developers
|
|
6
6
|
Classifier: Intended Audience :: Science/Research
|
|
@@ -51,12 +51,12 @@ requirements, and other APIs that are not part of this package.
|
|
|
51
51
|
|
|
52
52
|
## Installation
|
|
53
53
|
|
|
54
|
-
The package version prepared by this checkout is `solverforge` `0.6.
|
|
54
|
+
The package version prepared by this checkout is `solverforge` `0.6.13`, targeting
|
|
55
55
|
the published and registry-locked SolverForge Rust `0.21.2` dependency base.
|
|
56
56
|
Install the matching package after its Python release with:
|
|
57
57
|
|
|
58
58
|
```sh
|
|
59
|
-
python3.14 -m pip install "solverforge==0.6.
|
|
59
|
+
python3.14 -m pip install "solverforge==0.6.13"
|
|
60
60
|
```
|
|
61
61
|
|
|
62
62
|
The installable wheel contains the core `solverforge` package, native extension,
|
|
@@ -76,12 +76,12 @@ make deliveries-run PORT=7861
|
|
|
76
76
|
```
|
|
77
77
|
|
|
78
78
|
The same source-checkout examples can be run against the matching published
|
|
79
|
-
package once `0.6.
|
|
79
|
+
package once `0.6.13` is released, without `PYTHONPATH` or an editable checkout:
|
|
80
80
|
|
|
81
81
|
```sh
|
|
82
82
|
python3.14 -m venv .venv-examples
|
|
83
83
|
. .venv-examples/bin/activate
|
|
84
|
-
python -m pip install "solverforge[examples]==0.6.
|
|
84
|
+
python -m pip install "solverforge[examples]==0.6.13"
|
|
85
85
|
python examples/nqueens.py
|
|
86
86
|
python -m examples.solverforge_hospital
|
|
87
87
|
python -m examples.solverforge_deliveries
|
|
@@ -20,12 +20,12 @@ requirements, and other APIs that are not part of this package.
|
|
|
20
20
|
|
|
21
21
|
## Installation
|
|
22
22
|
|
|
23
|
-
The package version prepared by this checkout is `solverforge` `0.6.
|
|
23
|
+
The package version prepared by this checkout is `solverforge` `0.6.13`, targeting
|
|
24
24
|
the published and registry-locked SolverForge Rust `0.21.2` dependency base.
|
|
25
25
|
Install the matching package after its Python release with:
|
|
26
26
|
|
|
27
27
|
```sh
|
|
28
|
-
python3.14 -m pip install "solverforge==0.6.
|
|
28
|
+
python3.14 -m pip install "solverforge==0.6.13"
|
|
29
29
|
```
|
|
30
30
|
|
|
31
31
|
The installable wheel contains the core `solverforge` package, native extension,
|
|
@@ -45,12 +45,12 @@ make deliveries-run PORT=7861
|
|
|
45
45
|
```
|
|
46
46
|
|
|
47
47
|
The same source-checkout examples can be run against the matching published
|
|
48
|
-
package once `0.6.
|
|
48
|
+
package once `0.6.13` is released, without `PYTHONPATH` or an editable checkout:
|
|
49
49
|
|
|
50
50
|
```sh
|
|
51
51
|
python3.14 -m venv .venv-examples
|
|
52
52
|
. .venv-examples/bin/activate
|
|
53
|
-
python -m pip install "solverforge[examples]==0.6.
|
|
53
|
+
python -m pip install "solverforge[examples]==0.6.13"
|
|
54
54
|
python examples/nqueens.py
|
|
55
55
|
python -m examples.solverforge_hospital
|
|
56
56
|
python -m examples.solverforge_deliveries
|
|
@@ -123,7 +123,6 @@ fn build_assignment_group(
|
|
|
123
123
|
let metadata = Arc::new(PyDynamicAssignmentMetadata {
|
|
124
124
|
schema,
|
|
125
125
|
group_index,
|
|
126
|
-
entity_index,
|
|
127
126
|
});
|
|
128
127
|
Ok(ScalarGroupBinding::dynamic_assignment(
|
|
129
128
|
intern(group.name.clone()),
|
|
@@ -146,7 +145,6 @@ fn scalar_group_limits(group: &AssignmentScalarGroupSchema) -> ScalarGroupLimits
|
|
|
146
145
|
struct PyDynamicAssignmentMetadata {
|
|
147
146
|
schema: Arc<DynamicSchema>,
|
|
148
147
|
group_index: usize,
|
|
149
|
-
entity_index: usize,
|
|
150
148
|
}
|
|
151
149
|
|
|
152
150
|
impl PyDynamicAssignmentMetadata {
|
|
@@ -182,10 +180,16 @@ impl PyDynamicAssignmentMetadata {
|
|
|
182
180
|
};
|
|
183
181
|
solution
|
|
184
182
|
.state
|
|
185
|
-
.
|
|
186
|
-
.get(self.
|
|
183
|
+
.assignment_group_fields
|
|
184
|
+
.get(self.group_index)
|
|
187
185
|
.and_then(|rows| rows.get(entity_index))
|
|
188
|
-
.and_then(|
|
|
186
|
+
.and_then(|fields| match hook_name {
|
|
187
|
+
"required_entity" => fields.required_entity.as_ref(),
|
|
188
|
+
"capacity_key" => fields.capacity_key.as_ref(),
|
|
189
|
+
"position_key" => fields.position_key.as_ref(),
|
|
190
|
+
"sequence_key" => fields.sequence_key.as_ref(),
|
|
191
|
+
_ => unreachable!("assignment metadata source resolved"),
|
|
192
|
+
})
|
|
189
193
|
.map(Some)
|
|
190
194
|
.ok_or_else(|| {
|
|
191
195
|
py_err(format!(
|
|
@@ -420,10 +424,10 @@ impl PyDynamicAssignmentMetadata {
|
|
|
420
424
|
) -> PyResult<bool> {
|
|
421
425
|
let value = solution
|
|
422
426
|
.state
|
|
423
|
-
.
|
|
424
|
-
.get(self.
|
|
427
|
+
.assignment_group_fields
|
|
428
|
+
.get(self.group_index)
|
|
425
429
|
.and_then(|rows| rows.get(entity_index))
|
|
426
|
-
.and_then(|
|
|
430
|
+
.and_then(|fields| fields.same_value_conflicts.as_ref())
|
|
427
431
|
.ok_or_else(|| {
|
|
428
432
|
py_err(format!(
|
|
429
433
|
"assignment scalar group `{}` field `{field_name}` is missing on row {entity_index}",
|
|
@@ -547,3 +551,93 @@ fn indexed_usize(field_value: &DynamicValue, candidate_value: usize) -> PyResult
|
|
|
547
551
|
)),
|
|
548
552
|
}
|
|
549
553
|
}
|
|
554
|
+
|
|
555
|
+
#[cfg(test)]
|
|
556
|
+
mod tests {
|
|
557
|
+
use pyo3::ffi::c_str;
|
|
558
|
+
|
|
559
|
+
use super::*;
|
|
560
|
+
use crate::schema::parse::parse_schema;
|
|
561
|
+
use crate::schema::runtime_plan::CompiledRuntimePlan;
|
|
562
|
+
use crate::state::callback_view::PythonCallbackView;
|
|
563
|
+
use crate::state::entity_table::{AssignmentRowFields, DynamicEntityRow, DynamicState};
|
|
564
|
+
|
|
565
|
+
#[test]
|
|
566
|
+
fn assignment_access_uses_imported_slots_not_named_row_fields() {
|
|
567
|
+
Python::initialize();
|
|
568
|
+
Python::attach(|py| {
|
|
569
|
+
let raw = py
|
|
570
|
+
.eval(
|
|
571
|
+
c_str!(
|
|
572
|
+
r#"{
|
|
573
|
+
'solution_type': 'Plan', 'score_family': 'hard_soft',
|
|
574
|
+
'entities': [{'type_name': 'Task', 'collection': 'tasks', 'fields': [
|
|
575
|
+
{'name': 'value', 'kind': 'planning_variable', 'allows_unassigned': True}
|
|
576
|
+
]}],
|
|
577
|
+
'constraints': [], 'conflict_repairs': [], 'candidate_metrics': [],
|
|
578
|
+
'scalar_groups': [{'kind': 'assignment', 'name': 'assign',
|
|
579
|
+
'entity_class': 'Task', 'variable_name': 'value',
|
|
580
|
+
'required_entity_field': 'required', 'capacity_key_field': 'capacity',
|
|
581
|
+
'position_key_field': 'position', 'sequence_key_field': 'sequence',
|
|
582
|
+
'same_value_conflict_field': 'conflicts'}]
|
|
583
|
+
}"#
|
|
584
|
+
),
|
|
585
|
+
None,
|
|
586
|
+
None,
|
|
587
|
+
)
|
|
588
|
+
.unwrap();
|
|
589
|
+
let schema = Arc::new(parse_schema(raw.cast().unwrap()).unwrap());
|
|
590
|
+
let plan = Arc::new(CompiledRuntimePlan::from_schema(Arc::clone(&schema)).unwrap());
|
|
591
|
+
let metadata = PyDynamicAssignmentMetadata {
|
|
592
|
+
schema,
|
|
593
|
+
group_index: 0,
|
|
594
|
+
};
|
|
595
|
+
let state = DynamicState {
|
|
596
|
+
entities: vec![vec![DynamicEntityRow::default(); 2]],
|
|
597
|
+
assignment_group_fields: Arc::new(vec![vec![
|
|
598
|
+
AssignmentRowFields {
|
|
599
|
+
required_entity: Some(DynamicValue::Bool(true)),
|
|
600
|
+
capacity_key: Some(DynamicValue::List(vec![
|
|
601
|
+
DynamicValue::Int(7),
|
|
602
|
+
DynamicValue::None,
|
|
603
|
+
])),
|
|
604
|
+
position_key: Some(DynamicValue::Int(13)),
|
|
605
|
+
sequence_key: Some(DynamicValue::Int(5)),
|
|
606
|
+
same_value_conflicts: Some(DynamicValue::List(vec![DynamicValue::Int(1)])),
|
|
607
|
+
},
|
|
608
|
+
AssignmentRowFields {
|
|
609
|
+
required_entity: Some(DynamicValue::Bool(false)),
|
|
610
|
+
same_value_conflicts: Some(DynamicValue::List(Vec::new())),
|
|
611
|
+
..AssignmentRowFields::default()
|
|
612
|
+
},
|
|
613
|
+
]]),
|
|
614
|
+
..DynamicState::default()
|
|
615
|
+
};
|
|
616
|
+
let solution = PyDynamicSolution::from_runtime_plan(
|
|
617
|
+
plan,
|
|
618
|
+
state,
|
|
619
|
+
PythonCallbackView::default(),
|
|
620
|
+
None,
|
|
621
|
+
SolverConfig::default(),
|
|
622
|
+
0,
|
|
623
|
+
);
|
|
624
|
+
let cloned = solution.clone();
|
|
625
|
+
assert!(Arc::ptr_eq(
|
|
626
|
+
&solution.state.assignment_group_fields,
|
|
627
|
+
&cloned.state.assignment_group_fields
|
|
628
|
+
));
|
|
629
|
+
for state in [&solution, &cloned] {
|
|
630
|
+
assert!(state.state.entities[0][0].fields.is_empty());
|
|
631
|
+
assert!(metadata.required_entity(state, 0));
|
|
632
|
+
assert!(!metadata.required_entity(state, 1));
|
|
633
|
+
assert_eq!(metadata.capacity_key(state, 0, 0), Some(7));
|
|
634
|
+
assert_eq!(metadata.capacity_key(state, 0, 1), None);
|
|
635
|
+
assert_eq!(metadata.position_key(state, 0), Some(13));
|
|
636
|
+
assert_eq!(metadata.sequence_key(state, 0, 0), Some(5));
|
|
637
|
+
assert!(!metadata.assignment_edge_allowed(state, 0, 0, 1, 0));
|
|
638
|
+
assert!(!metadata.assignment_edge_allowed(state, 1, 0, 0, 0));
|
|
639
|
+
assert!(metadata.assignment_edge_allowed(state, 0, 0, 1, 1));
|
|
640
|
+
}
|
|
641
|
+
});
|
|
642
|
+
}
|
|
643
|
+
}
|
|
@@ -3,6 +3,19 @@ use std::sync::Arc;
|
|
|
3
3
|
|
|
4
4
|
use crate::value::DynamicValue;
|
|
5
5
|
|
|
6
|
+
/// Immutable assignment row sources, resolved by group and hook once at import.
|
|
7
|
+
/// State clones share these values; planning-variable changes never rewrite them.
|
|
8
|
+
pub type AssignmentGroupFields = Vec<Vec<AssignmentRowFields>>;
|
|
9
|
+
|
|
10
|
+
#[derive(Debug, Clone, Default)]
|
|
11
|
+
pub struct AssignmentRowFields {
|
|
12
|
+
pub required_entity: Option<DynamicValue>,
|
|
13
|
+
pub capacity_key: Option<DynamicValue>,
|
|
14
|
+
pub position_key: Option<DynamicValue>,
|
|
15
|
+
pub sequence_key: Option<DynamicValue>,
|
|
16
|
+
pub same_value_conflicts: Option<DynamicValue>,
|
|
17
|
+
}
|
|
18
|
+
|
|
6
19
|
#[derive(Debug, Clone, Default)]
|
|
7
20
|
pub struct DynamicState {
|
|
8
21
|
pub entities: Vec<Vec<DynamicEntityRow>>,
|
|
@@ -10,6 +23,7 @@ pub struct DynamicState {
|
|
|
10
23
|
pub scalar_value_ranges: Vec<Vec<Option<Arc<[usize]>>>>,
|
|
11
24
|
pub list_elements: Vec<Vec<Option<Arc<[usize]>>>>,
|
|
12
25
|
pub solution_fields: Arc<BTreeMap<String, DynamicValue>>,
|
|
26
|
+
pub assignment_group_fields: Arc<AssignmentGroupFields>,
|
|
13
27
|
}
|
|
14
28
|
|
|
15
29
|
impl DynamicState {
|
|
@@ -12,7 +12,9 @@ use crate::schema::types::{
|
|
|
12
12
|
};
|
|
13
13
|
use crate::schema::DynamicSchema;
|
|
14
14
|
use crate::state::callback_view::PythonCallbackView;
|
|
15
|
-
use crate::state::entity_table::{
|
|
15
|
+
use crate::state::entity_table::{
|
|
16
|
+
AssignmentGroupFields, AssignmentRowFields, DynamicEntityRow, DynamicState,
|
|
17
|
+
};
|
|
16
18
|
use crate::state::PyDynamicSolution;
|
|
17
19
|
use crate::value::DynamicValue;
|
|
18
20
|
|
|
@@ -148,6 +150,7 @@ pub fn import_solution(
|
|
|
148
150
|
fact_objects.push(objects);
|
|
149
151
|
}
|
|
150
152
|
validate_assignment_group_field_metadata(schema, &entity_tables)?;
|
|
153
|
+
let assignment_group_fields = resolve_assignment_group_fields(schema, &entity_tables)?;
|
|
151
154
|
validate_list_element_metadata_field_sources(
|
|
152
155
|
schema,
|
|
153
156
|
&entity_tables,
|
|
@@ -163,6 +166,7 @@ pub fn import_solution(
|
|
|
163
166
|
scalar_value_ranges,
|
|
164
167
|
list_elements,
|
|
165
168
|
solution_fields: Arc::new(solution_fields),
|
|
169
|
+
assignment_group_fields: Arc::new(assignment_group_fields),
|
|
166
170
|
},
|
|
167
171
|
PythonCallbackView::from_import(
|
|
168
172
|
py_solution.clone().unbind(),
|
|
@@ -484,6 +488,57 @@ fn collect_list_metadata_row_fields(
|
|
|
484
488
|
}
|
|
485
489
|
}
|
|
486
490
|
|
|
491
|
+
fn resolve_assignment_group_fields(
|
|
492
|
+
schema: &DynamicSchema,
|
|
493
|
+
entity_tables: &[Vec<DynamicEntityRow>],
|
|
494
|
+
) -> PyResult<AssignmentGroupFields> {
|
|
495
|
+
schema
|
|
496
|
+
.assignment_scalar_groups
|
|
497
|
+
.iter()
|
|
498
|
+
.map(|group| {
|
|
499
|
+
let entity_index = schema
|
|
500
|
+
.entity_index_by_type(&group.entity_class)
|
|
501
|
+
.expect("assignment group entity validated");
|
|
502
|
+
let fields = [
|
|
503
|
+
group
|
|
504
|
+
.required_entity
|
|
505
|
+
.as_ref()
|
|
506
|
+
.and_then(MetadataSourceSchema::row),
|
|
507
|
+
group
|
|
508
|
+
.capacity_key
|
|
509
|
+
.as_ref()
|
|
510
|
+
.and_then(MetadataSourceSchema::row),
|
|
511
|
+
group
|
|
512
|
+
.position_key
|
|
513
|
+
.as_ref()
|
|
514
|
+
.and_then(MetadataSourceSchema::row),
|
|
515
|
+
group
|
|
516
|
+
.sequence_key
|
|
517
|
+
.as_ref()
|
|
518
|
+
.and_then(MetadataSourceSchema::row),
|
|
519
|
+
group.same_value_conflict_field.as_deref(),
|
|
520
|
+
];
|
|
521
|
+
entity_tables[entity_index]
|
|
522
|
+
.iter()
|
|
523
|
+
.enumerate()
|
|
524
|
+
.map(|(row_index, row)| {
|
|
525
|
+
let mut resolved = std::array::from_fn(|_| None);
|
|
526
|
+
for (index, field) in fields.iter().enumerate() {
|
|
527
|
+
if let Some(field) = field {
|
|
528
|
+
resolved[index] = Some(
|
|
529
|
+
assignment_metadata_field(row, &group.name, row_index, field)?
|
|
530
|
+
.clone(),
|
|
531
|
+
);
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
let [required_entity, capacity_key, position_key, sequence_key, same_value_conflicts] = resolved;
|
|
535
|
+
Ok(AssignmentRowFields { required_entity, capacity_key, position_key, sequence_key, same_value_conflicts })
|
|
536
|
+
})
|
|
537
|
+
.collect()
|
|
538
|
+
})
|
|
539
|
+
.collect()
|
|
540
|
+
}
|
|
541
|
+
|
|
487
542
|
fn validate_assignment_group_field_metadata(
|
|
488
543
|
schema: &DynamicSchema,
|
|
489
544
|
entity_tables: &[Vec<DynamicEntityRow>],
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|