def PushSourceIfNeeded(ToSave, force=False):
    """
    Pushes a single wave object as a binary file if it doesn't already exist
    (and if force is False). Saves the full note, as well as 'DataY'. 
    
    Args:

         ToSave: Wave object (See ProcessSingleWave) to save out. Most likely,
         this will be a concatenation; columns of (e.g.) time, distance, force

         force: if true, force creates the file

    Returns:
        None  
    """
    # check if the file already exists
    FileName = BinaryHDF5Io.GetFileSaveName(ToSave.GetNoteElement())
    # get the pull path (to the actual database...)
    FullPath = IgorUtil.getDatabaseFile(FileName)
    if (pGenUtil.isfile(FullPath) and not force):
        # don't do anything
        return
    else:
        # save the file out, using a blank string for the file name
        # (ie: just the folder)
        DatabasePath = IgorUtil.getDatabaseFolder()
        # XXX TODO: check that path exists to file?
        try:
            BinaryHDF5Io.\
                SaveWaveGroupAsTimeSepForceHDF5(FolderPath=DatabasePath,
                                                WaveGroup=ToSave)
        except:
            print("XXX file saving disabled [off campus?]")
    def __init__(self, MetaIds, WaveGroupObj, ParamVals, ParamIds):
        """
        Args:
        MetaIds: a dict with <table>/<ids> pairs for the The ids for all of the 
        tables associated with the User Meta (e.g User, TipType, etc), 
        as well as the experiment and model
        
        WaveGroupObj: AssociatedWaveData we want to save out

        Returns: A newly constructed traceObj
        """

        self.MetaIds = MetaIds
        self.WaveObj = WaveGroupObj
        self.ParamVals = ParamVals
        self.ParamIds = ParamIds
        self.SaveName = BinaryHDF5Io.GetFileSaveName(WaveGroupObj.\
                                                     GetNoteElement())
        self.idDot = ap.Namespace(**copy.deepcopy(self.MetaIds))
def PushToDatabase(ModelName, AssociatedWaveData, MetaViewParams,
                   ParameterDescriptions, CurrentParams, SqlObj):
    """
    In a single operation, adds (or updates) a trace to the database. 
    If necessary (ie: if trace never added before), saves out the binary file.
    Essentially a Model-friendly wrapper for  SyncTraceWithDatabase

    Args:
        ModelName: String name of the model to use. Inserted if new.

        AssociatedWaveData: See Model, WaveDataGroup. Includes the dictionary 
        of name:waveobj pairs to insert, Converted to Sep:Force on insert.

        MetaViewParams: a dict of <table>:<ids> associated with the (user) meta 
        information tables

        ParameterDescriptions: the meta information associated with the
        parameters
        
        CurrentParams: The current parameters (vals)associated. sorted to match
        ParameterDescriptions. 

        SqlObj: The connection object to use

    Returns:
        updated id namespace object. For all tables (except parameters)
        calling "NamespaceObject.<tablename>" will give the integer id used 
        for this insert.
    """
    # check that we actualy have data to push
    if (len(AssociatedWaveData) == 0):
        raise ValueError("Nothing to push to database.")
    # POST: something to push
    # Get all of the meta data for the wave (e.g. pulling speed, etc)
    # (XXX assume it is the same for all associated waves)
    WaveNames = AssociatedWaveData.keys()
    # convert all the view parameters to ids
    toIdStr = lambda x: ("id" + x)
    allIds = dict([(toIdStr(table), MetaViewParams[table][toIdStr(table)])
                   for table in MetaViewParams])
    # if the source already exists for this model,
    # then we want to *update* everything, not insert again
    ModelData = InsertOrGetModelId(ModelName, SqlObj=SqlObj)
    ModelId = ModelData.idModel
    # tableName is a helper function that gets a table name from a
    # SqlAlchemy object (e.g., that which is returned from a query)
    idName = lambda SqlAlch: toIdStr(SqlAlch.__table__.name)
    # add the model ID
    allIds[idName(ModelData)] = ModelId
    # Get the rows in the database associated with each parameter
    # XXX inefficient use of SqlObj (repeated instantiation). Fix later...
    paramIds = ParameterDescriptions.GetSqlParamIds(ModelRow=ModelData,
                                                    SqlObj=SqlObj)
    # get the name of the file to save the data as
    ElementForName = AssociatedWaveData.GetNoteElement()
    SourceFile = BinaryHDF5Io.GetFileSaveName(ElementForName)
    # get the ID associated with the experiment
    Experiment = InsertOrGetSourceFile(SourceFile, SqlObj=SqlObj)
    idExpMeta = Experiment.idExpMeta
    allIds[idName(Experiment)] = idExpMeta
    # Go ahead and concatenate all of the associated waves
    # XXX TODO deal with high resolution waves?
    # We now have everything needed to push this data to the database:
    # (0) the actual data is in WaveData
    # (1) all meta information needed by TraceMeta is in WaveData.Note
    # (2) all Model information is in ModelData
    # (3) all Parameter information is in paramIds (for meta) and just the
    # parameter values we have saved
    # (4) The many 'user meta' data points (e.g. tip type) are in
    # We now pass all of this information to our SqlModel, which takes
    # care of most of the hairy details. (e.g. linking, etc)
    #  XXX TODO: move most of above to the Sql model?
    mObj = TraceObj(allIds, AssociatedWaveData, CurrentParams, paramIds)
    idDot = SyncTraceWithDatabase(mObj, SqlObj=SqlObj)
    idDot.idExpMeta = idExpMeta
    idDot.idModel = ModelId
    return idDot