def update_worksheet(self, worksheet_info, new_items): ''' Set the worksheet to have items |new_items|. ''' worksheet_uuid = worksheet_info['uuid'] last_item_id = worksheet_info['last_item_id'] length = len(worksheet_info['items']) worksheet = self.model.get_worksheet(worksheet_uuid, fetch_items=False) check_has_all_permission(self.model, self._current_user(), worksheet) try: new_items = [worksheet_util.convert_item_to_db(item) for item in new_items] self.model.update_worksheet(worksheet_uuid, last_item_id, length, new_items) except UsageError: # Turn the model error into a more readable one using the object. raise UsageError('%s was updated concurrently!' % (worksheet,))
def update_worksheet_items(worksheet_info, new_items, convert_items=True): """ Set the worksheet to have items |new_items|. """ worksheet_uuid = worksheet_info['uuid'] last_item_id = worksheet_info['last_item_id'] length = len(worksheet_info['items']) worksheet = local.model.get_worksheet(worksheet_uuid, fetch_items=False) check_worksheet_has_all_permission(local.model, request.user, worksheet) worksheet_util.check_worksheet_not_frozen(worksheet) try: if convert_items: new_items = [worksheet_util.convert_item_to_db(item) for item in new_items] local.model.update_worksheet_items(worksheet_uuid, last_item_id, length, new_items) except UsageError: # Turn the model error into a more readable one using the object. raise UsageError('%s was updated concurrently!' % (worksheet,))
def update_worksheet_items(worksheet_info, new_items, convert_items=True): """ Set the worksheet to have items |new_items|. """ worksheet_uuid = worksheet_info['uuid'] last_item_id = worksheet_info['last_item_id'] length = len(worksheet_info['items']) worksheet = local.model.get_worksheet(worksheet_uuid, fetch_items=False) check_worksheet_has_all_permission(local.model, request.user, worksheet) worksheet_util.check_worksheet_not_frozen(worksheet) try: if convert_items: new_items = [worksheet_util.convert_item_to_db(item) for item in new_items] local.model.update_worksheet_items(worksheet_uuid, last_item_id, length, new_items) except UsageError: # Turn the model error into a more readable one using the object. raise UsageError('%s was updated concurrently!' % (worksheet,))
def add_with_prelude_items(old_bundle_uuid, new_bundle_uuid): ''' Add new_bundle_uuid to the current worksheet (along with the occurrences on the worksheet). Also add the prelude (items that occur right before old_bundle_uuid on a worksheet). Note that new_bundle_uuid might get multiple times. ''' host_worksheet_uuids = self.model.get_host_worksheet_uuids([old_bundle_uuid])[old_bundle_uuid] if len(host_worksheet_uuids) > 0: # Choose a single worksheet. if worksheet_uuid in host_worksheet_uuids: # If current worksheet is one of them, favor that one. host_worksheet_uuid = worksheet_uuid else: # Choose an arbitrary one host_worksheet_uuid = host_worksheet_uuids[0] if host_worksheet_uuid in worksheet_cache: worksheet_info = worksheet_cache[host_worksheet_uuid] else: worksheet_info = worksheet_cache[host_worksheet_uuid] = \ self.get_worksheet_info(host_worksheet_uuid, fetch_items=True) # Look for items that appear right before the old_bundle_uuid collect_items = [] for item in worksheet_info['items']: (bundle_info, subworkheet_info, value_obj, type) = item if type == worksheet_util.TYPE_BUNDLE and bundle_info['uuid'] == old_bundle_uuid: # Ended in the target old_bundle_uuid, flush the prelude gathered so far. for item2 in collect_items: self.add_worksheet_item(worksheet_uuid, worksheet_util.convert_item_to_db(item2)) self.add_worksheet_item(worksheet_uuid, worksheet_util.bundle_item(new_bundle_uuid)) collect_items = [] elif type == worksheet_util.TYPE_MARKUP and value_obj == '': collect_items = [] elif type == worksheet_util.TYPE_BUNDLE: collect_items = [] else: collect_items.append(item)
def mimic(self, old_inputs, old_output, new_inputs, new_output_name, worksheet_uuid, depth, shadow, dry_run): ''' old_inputs: list of bundle uuids old_output: bundle uuid that we produced new_inputs: list of bundle uuids that are analogous to old_inputs new_output_name: name of the bundle to create to be analogous to old_output (possibly None) worksheet_uuid: add newly created bundles to this worksheet depth: how far to do a BFS up from old_output. shadow: whether to add the new inputs right after all occurrences of the old inputs in worksheets. ''' check_worksheet_has_all_permission(self.model, self._current_user(), self.model.get_worksheet(worksheet_uuid, fetch_items=False)) #print 'old_inputs: %s, new_inputs: %s, old_output: %s, new_output_name: %s' % (old_inputs, new_inputs, old_output, new_output_name) # Build the graph (get all the infos). # If old_output is given, look at ancestors of old_output until we # reached some depth. If it's not given, we first get all the # descendants first, and then get their ancestors. infos = {} # uuid -> bundle info if old_output: bundle_uuids = [old_output] else: bundle_uuids = self.model.get_self_and_descendants(old_inputs, depth=depth) all_bundle_uuids = list(bundle_uuids) # should be infos.keys() in order for _ in range(depth): new_bundle_uuids = [] for bundle_uuid in bundle_uuids: if bundle_uuid in infos: continue # Already visited info = infos[bundle_uuid] = self.get_bundle_info(bundle_uuid) for dep in info['dependencies']: parent_uuid = dep['parent_uuid'] if parent_uuid not in infos: new_bundle_uuids.append(parent_uuid) all_bundle_uuids = new_bundle_uuids + all_bundle_uuids bundle_uuids = new_bundle_uuids # Make sure we have read access to all the bundles involved here. check_bundles_have_read_permission(self.model, self._current_user(), list(infos.keys())) # Now go recursively create the bundles. old_to_new = {} # old_uuid -> new_uuid downstream = set() # old_uuid -> whether we're downstream of an input (and actually needs to be mapped onto a new uuid) created_uuids = set() # set of uuids which were newly created plan = [] # sequence of (old, new) bundle infos to make for old, new in zip(old_inputs, new_inputs): old_to_new[old] = new downstream.add(old) # Return corresponding new_bundle_uuid def recurse(old_bundle_uuid): if old_bundle_uuid in old_to_new: #print old_bundle_uuid, 'cached' return old_to_new[old_bundle_uuid] # Don't have any more information (because we probably hit the maximum depth) if old_bundle_uuid not in infos: #print old_bundle_uuid, 'no information' return old_bundle_uuid # Get information about the old bundle. info = infos[old_bundle_uuid] new_dependencies = [{ 'parent_uuid': recurse(dep['parent_uuid']), 'parent_path': dep['parent_path'], 'child_uuid': dep['child_uuid'], # This is just a placeholder to do the equality test 'child_path': dep['child_path'] } for dep in info['dependencies']] # If there are no inputs or if we're downstream of any inputs, we need to make a new bundle. lone_output = (len(old_inputs) == 0 and old_bundle_uuid == old_output) downstream_of_inputs = any(dep['parent_uuid'] in downstream for dep in info['dependencies']) if lone_output or downstream_of_inputs: # Now create a new bundle that mimics the old bundle. # Only change the name if the output name is supplied. old_bundle_name = info['metadata']['name'] new_info = copy.deepcopy(info) new_metadata = new_info['metadata'] if new_output_name: if old_bundle_uuid == old_output: new_metadata['name'] = new_output_name else: # Just make up a name heuristically new_metadata['name'] = new_output_name + '-' + info['metadata']['name'] # Remove all the automatically generated keys cls = get_bundle_subclass(new_info['bundle_type']) for spec in cls.METADATA_SPECS: if spec.generated and spec.key in new_metadata: new_metadata.pop(spec.key) # Set the targets targets = [(dep['child_path'], (dep['parent_uuid'], dep['parent_path'])) for dep in new_dependencies] if dry_run: new_bundle_uuid = None else: if new_info['bundle_type'] not in ('make', 'run'): raise UsageError('Can\'t mimic %s since it is not make or run' % old_bundle_uuid) new_bundle_uuid = self._derive_bundle(new_info['bundle_type'], \ targets, new_info['command'], new_metadata, worksheet_uuid) new_info['uuid'] = new_bundle_uuid plan.append((info, new_info)) downstream.add(old_bundle_uuid) created_uuids.add(new_bundle_uuid) else: new_bundle_uuid = old_bundle_uuid old_to_new[old_bundle_uuid] = new_bundle_uuid # Cache it return new_bundle_uuid if old_output: recurse(old_output) else: # Don't have a particular output we're targetting, so just create # new versions of all the uuids. for uuid in all_bundle_uuids: recurse(uuid) # Add to worksheet if not dry_run: if shadow: # Add each new bundle in the "shadow" of the old_bundle (right after it). for old_bundle_uuid, new_bundle_uuid in old_to_new.items(): if new_bundle_uuid in created_uuids: # Only add novel bundles self.model.add_shadow_worksheet_items(old_bundle_uuid, new_bundle_uuid) else: def newline(): self.model.add_worksheet_item(worksheet_uuid, worksheet_util.markup_item('')) # A prelude of a bundle on a worksheet is the set of items that occur right before it (markup, directives, etc.) # Let W be the first worksheet containing the old_inputs[0]. # Add all items on that worksheet that appear in old_to_new along with their preludes. # For items not on this worksheet, add them at the end (instead of making them floating). if old_output: anchor_uuid = old_output elif len(old_inputs) > 0: anchor_uuid = old_inputs[0] host_worksheet_uuids = self.model.get_host_worksheet_uuids([anchor_uuid])[anchor_uuid] new_bundle_uuids_added = set() skipped = True # Whether there were items that we didn't include in the prelude (in which case we want to put '') if len(host_worksheet_uuids) > 0: # Choose a single worksheet. if worksheet_uuid in host_worksheet_uuids: # If current worksheet is one of them, favor that one. host_worksheet_uuid = worksheet_uuid else: # Choose an arbitrary one (in the future, have a better way of canonicalizing). host_worksheet_uuid = host_worksheet_uuids[0] # Fetch the worksheet worksheet_info = self.get_worksheet_info(host_worksheet_uuid, fetch_items=True) prelude_items = [] # The prelude that we're building up for item in worksheet_info['items']: (bundle_info, subworkheet_info, value_obj, item_type) = item just_added = False if item_type == worksheet_util.TYPE_BUNDLE: old_bundle_uuid = bundle_info['uuid'] if old_bundle_uuid in old_to_new: # Flush the prelude gathered so far. new_bundle_uuid = old_to_new[old_bundle_uuid] if new_bundle_uuid in created_uuids: # Only add novel bundles # Stand in for things skipped (this is important so directives have proper extent). if skipped: newline() # Add prelude and items for item2 in prelude_items: self.add_worksheet_item(worksheet_uuid, worksheet_util.convert_item_to_db(item2)) self.add_worksheet_item(worksheet_uuid, worksheet_util.bundle_item(new_bundle_uuid)) new_bundle_uuids_added.add(new_bundle_uuid) just_added = True if (item_type == worksheet_util.TYPE_MARKUP and value_obj != '') or item_type == worksheet_util.TYPE_DIRECTIVE: prelude_items.append(item) # Include in prelude skipped = False else: prelude_items = [] # Reset skipped = not just_added # Add the bundles that haven't been added yet for info, new_info in plan: new_bundle_uuid = new_info['uuid'] if new_bundle_uuid not in new_bundle_uuids_added: if skipped: newline() skipped = False self.add_worksheet_item(worksheet_uuid, worksheet_util.bundle_item(new_bundle_uuid)) return plan