docs: def add_network(network,**kwargs):
      """
      Takes an entire network complex model and saves it to the DB.  This
      complex model includes links & scenarios (with resource data).  Returns
      the network's complex model.
  
      As links connect two nodes using the node_ids, if the nodes are new
      they will not yet have node_ids. In this case, use negative ids as
      temporary IDS until the node has been given an permanent ID.
  
      All inter-object referencing of new objects should be done using
      negative IDs in the client.
  
      The returned object will have positive IDS
  
      """
      db.DBSession.autoflush = False
  
      start_time = datetime.datetime.now()
      log.debug("Adding network")
  
      insert_start = datetime.datetime.now()
  
      proj_i = db.DBSession.query(Project).filter(Project.id == network.project_id).first()
      if proj_i is None:
          raise HydraError("Project ID is none. A project ID must be specified on the Network")
  
      existing_net = db.DBSession.query(Network).filter(Network.project_id == network.project_id, Network.name==network.name).first()
      if existing_net is not None:
          raise HydraError("A network with the name %s is already in project %s"%(network.name, network.project_id))
  
      user_id = kwargs.get('user_id')
      proj_i.check_write_permission(user_id)
  
      net_i = Network()
      net_i.project_id          = network.project_id
      net_i.name                = network.name
      net_i.description         = network.description
      net_i.created_by          = user_id
      net_i.projection          = network.projection
  
      if network.layout is not None:
          net_i.layout = network.get_layout()
  
      network.id = net_i.id
      db.DBSession.add(net_i)
      db.DBSession.flush()
      #These two lists are used for comparison and lookup, so when
      #new attributes are added, these lists are extended.
  
      #List of all the resource attributes
      all_resource_attrs = {}
  
      name_map = {network.name:net_i}
      network_attrs, network_defaults = _bulk_add_resource_attrs(net_i.id, 'NETWORK', [network], name_map)
      hdb.add_resource_types(net_i, network.types)
  
      all_resource_attrs.update(network_attrs)
  
      log.info("Network attributes added in %s", get_timing(start_time))
      node_id_map, node_attrs, node_datasets = _add_nodes(net_i, network.nodes)
      all_resource_attrs.update(node_attrs)
  
      link_id_map, link_attrs, link_datasets = _add_links(net_i, network.links, node_id_map)
      all_resource_attrs.update(link_attrs)
  
      grp_id_map, grp_attrs, grp_datasets = _add_resource_groups(net_i, network.resourcegroups)
      all_resource_attrs.update(grp_attrs)
  
      defaults = list(grp_datasets.values()) + list(link_datasets.values()) \
          + list(node_datasets.values()) + list(network_defaults.values())
  
      start_time = datetime.datetime.now()
  
      scenario_names = []
      if network.scenarios is not None:
          log.info("Adding scenarios to network")
          for s in network.scenarios:
  
              if s.name in scenario_names:
                  raise HydraError("Duplicate scenario name: %s"%(s.name))
  
              scen = Scenario()
              scen.name                 = s.name
              scen.description          = s.description
              scen.layout               = s.get_layout()
              scen.start_time           = str(timestamp_to_ordinal(s.start_time)) if s.start_time else None
              scen.end_time             = str(timestamp_to_ordinal(s.end_time)) if s.end_time else None
              scen.time_step            = s.time_step
              scen.created_by           = user_id
  
              scenario_names.append(s.name)
  
              #extract the data from each resourcescenario
              incoming_datasets = []
              scenario_resource_attrs = []
              for r_scen in s.resourcescenarios:
                  ra = all_resource_attrs[r_scen.resource_attr_id]
                  incoming_datasets.append(r_scen.dataset)
                  scenario_resource_attrs.append(ra)
  
              data_start_time = datetime.datetime.now()
  
              for default in defaults:
                  scen.add_resource_scenario(JSONObject(default),
                                             JSONObject({'id':default['dataset_id']}),
                                             source=kwargs.get('app_name'))
  
              datasets = data._bulk_insert_data(
                                                incoming_datasets,
                                                user_id,
                                                kwargs.get('app_name')
                                               )
  
              log.info("Data bulk insert took %s", get_timing(data_start_time))
              ra_start_time = datetime.datetime.now()
              for i, ra in enumerate(scenario_resource_attrs):
                  scen.add_resource_scenario(ra, datasets[i], source=kwargs.get('app_name'))
  
              log.info("Resource scenarios added in  %s", get_timing(ra_start_time))
  
              item_start_time = datetime.datetime.now()
              if s.resourcegroupitems is not None:
                  for group_item in s.resourcegroupitems:
                      group_item_i = ResourceGroupItem()
                      group_item_i.group = grp_id_map[group_item.group_id]
                      group_item_i.ref_key  = group_item.ref_key
                      if group_item.ref_key == 'NODE':
                          group_item_i.node = node_id_map[group_item.ref_id]
                      elif group_item.ref_key == 'LINK':
                          group_item_i.link = link_id_map[group_item.ref_id]
                      elif group_item.ref_key == 'GROUP':
                          group_item_i.subgroup = grp_id_map[group_item.ref_id]
                      else:
                          raise HydraError("A ref key of %s is not valid for a "
                                           "resource group item.",\
                                           group_item.ref_key)
  
                      scen.resourcegroupitems.append(group_item_i)
              log.info("Group items insert took %s", get_timing(item_start_time))
              net_i.scenarios.append(scen)
  
      log.info("Scenarios added in %s", get_timing(start_time))
      net_i.set_owner(user_id)
  
      db.DBSession.flush()
      log.info("Insertion of network took: %s",(datetime.datetime.now()-insert_start))
  
      return net_i