mirror of
https://github.com/gristlabs/grist-core.git
synced 2024-10-27 20:44:07 +00:00
74 lines
3.0 KiB
Python
74 lines
3.0 KiB
Python
|
from collections import defaultdict
|
||
|
import six
|
||
|
from usertypes import get_referenced_table_id
|
||
|
|
||
|
class _RefUpdates(object):
|
||
|
def __init__(self):
|
||
|
self.removals = set()
|
||
|
self.additions = set()
|
||
|
|
||
|
def get_reverse_adjustments(row_ids, old_values, new_values, value_iterator, relation):
|
||
|
"""
|
||
|
Generates data for updating reverse columns, based on changes to this column
|
||
|
"""
|
||
|
|
||
|
# Stores removals and addons for each target row
|
||
|
affected_target_rows = defaultdict(_RefUpdates)
|
||
|
|
||
|
# Iterate over changes to source column (my column)
|
||
|
for (source_row_id, old_value, new_value) in zip(row_ids, old_values, new_values):
|
||
|
if new_value != old_value:
|
||
|
# Treat old_values as removals, and new_values as additions
|
||
|
for target_row_id in value_iterator(old_value):
|
||
|
affected_target_rows[target_row_id].removals.add(source_row_id)
|
||
|
for target_row_id in value_iterator(new_value):
|
||
|
affected_target_rows[target_row_id].additions.add(source_row_id)
|
||
|
|
||
|
# Now in affected_target_rows, we have the changes (deltas), now we are going to convert them
|
||
|
# to updates (full list of values) in target columns.
|
||
|
|
||
|
adjustments = []
|
||
|
|
||
|
# For each target row (that needs to be updated, and was change in our column)
|
||
|
for target_row_id, updates in six.iteritems(affected_target_rows):
|
||
|
# Get the value stored in that column by using our own relation object (which should store
|
||
|
# correct values - the same that are stored in that reverse column). `reverse_value` is the
|
||
|
# value in that reverse cell
|
||
|
reverse_value = relation.get_affected_rows((target_row_id,))
|
||
|
|
||
|
# Now make the adjustments using calculated deltas
|
||
|
for source_row_id in updates.removals:
|
||
|
reverse_value.discard(source_row_id)
|
||
|
for source_row_id in updates.additions:
|
||
|
reverse_value.add(source_row_id)
|
||
|
adjustments.append((target_row_id, sorted(reverse_value)))
|
||
|
|
||
|
return adjustments
|
||
|
|
||
|
|
||
|
def check_desired_reverse_col(col_type, desired_reverse_col):
|
||
|
if not desired_reverse_col:
|
||
|
raise ValueError("invalid column specified in reverseCol")
|
||
|
if desired_reverse_col.reverseCol:
|
||
|
raise ValueError("reverseCol specifies an existing two-way reference column")
|
||
|
ref_table_id = get_referenced_table_id(col_type)
|
||
|
if not ref_table_id:
|
||
|
raise ValueError("reverseCol may only be set on a column with a reference type")
|
||
|
if desired_reverse_col.tableId != ref_table_id:
|
||
|
raise ValueError("reverseCol must be a column in the target table")
|
||
|
|
||
|
|
||
|
def pick_reverse_col_label(docmodel, col_rec):
|
||
|
ref_table_id = get_referenced_table_id(col_rec.type)
|
||
|
ref_table_rec = docmodel.get_table_rec(ref_table_id)
|
||
|
|
||
|
# First try the source table title.
|
||
|
source_table_rec = col_rec.parentId
|
||
|
reverse_label = source_table_rec.rawViewSectionRef.title or source_table_rec.tableId
|
||
|
|
||
|
# If that name already exists (as a label), add the source column's name as a suffix.
|
||
|
avoid_set = set(c.label for c in ref_table_rec.columns)
|
||
|
if reverse_label in avoid_set:
|
||
|
return reverse_label + "-" + (col_rec.label or col_rec.colId)
|
||
|
return reverse_label
|