SCRIPTS GOOGLE_SHEETS write_avg_density_to_google_sheetΒΆ

# -*-Python-*-
# Created by eldond at 2020-08-20 19:18

"""
This script demonstrates use of OMFITexperimentTable to write avg density & betan to shots listed in a Google sheet

The hard part is getting keys set up to access the table. After that, instructions
for the write_mds_data_to_table() method must be prepared. The rest is trivial.

How to prepare a Google Sheet for use with these tools:
-------------------------------------------------------
1: Make a sheet in Google drive
2: Make a project at https://console.developers.google.com/
3: Add and enable both Google drive API and Google sheets API
4: Add credentials: create credentials for a service account. Give it the Project: editor role.
5. Add a JSON key to the service account, download it, and place it somewhere accessible to this script.
6: Note the service account's email address (the form is <SERVICE-NAME>@<PROJECT>.iam.gserviceaccount.com)
7: Share the sheet with the service account

Connection should then be possible if OMFITgoogleSheet or one of its subclasses is
initialized w/ the right keyfile & sheet_name.


TABLE SETUP FOR THIS EXAMPLE
----------------------------
1. Include these column headers: 'Device', 'Shot', 't min', 't max', 'density'
2. In the next row, write 'units' in the 'Device' row.
3. Write down 'DIII-D' in the following row, and enter a shot number.
4. Fill in a time range, in ms, in the 't min' and 't max' columns.

The test sheet should already configured like this.

defaultVars parameters
----------------------
:param keyfile: str
    Filename of the JSON file containing the key to the Google service account, including the path

:param sheet_name: str
    Name of the spreadsheet to connect to
"""

try:
    import pygsheets
except ImportError:
    OMFITx.End()

sample_path = os.sep.join([OMFITsrc, '..', 'samples', ''])

defaultVars(keyfile=sample_path + 'omfit-test-gsheet_key.json', sheet_name='OmfitDataSheetTestsheet')

out = root['OUTPUTS']['debug_write_avg_density_to_google_sheet'] = OMFITtree()

# Initialize the OMFITexperimentTable instance
xtable = OMFITexperimentTable(keyfile=keyfile, sheet_name=sheet_name)

# Give instructions for using alternative servers for some devices
device_servers = {'EAST': 'EAST_US'}
# Unlisted devices just default to having server = device, so device='DIII-D' would result
# in server='DIII-D' with this set up. If being unnecessarily explicit makes you feel
# better, here's what that looks like:
device_servers_explicit = {'DIII-D': 'DIII-D', 'EAST': 'EAST_US'}
# If none of your servers are customized, don't bother constructing this thing or passing anything to
# write_mds_data_to_table()'s device_servers keyword.

# -----------------------------------------------------------------------------------------------------------------
# EXAMPLE SIGNAL REQUEST SET UP
# You can put a bunch of these in a list, but we'll do a few signals for example, and show
# variations on how to set up one signal.
# -----------------------------------------------------------------------------------------------------------------

# This sample handles a column of density measurements for a mix of devices. The measurements are averaged between
# tmin and tmax values given by other columns in the table; this setup provides the column headers so those can be
# looked up.
sample_signal = dict(
    column_name='density',  # The sheet must have a column named this
    TDI=dict(EAST=r'\dfsdev'),  # No TDI for d3d b/c TDI defaults to column_name, which is a valid d3d ptname
    treename=dict(EAST='PCS_EAST'),  # DIII-D isn't listed b/c default is None, which goes to PTDATA
    factor={'DIII-D': 1e-13},  # This particular EAST pointname doesn't require a factor; defaults to 1.0
    tfactor=dict(EAST=1e3),  # d3d times are already in ms
    tmin='t min',  # str means look up col w/ header exactly matching this string & reads timing values from there
    tmax='t max',
)
# This sample should work with the test sheet.

sample_signal_2 = dict(
    column_name='betan',  # The sheet must have a column named this
    TDI=dict(EAST=r'\betan'),  # d3d's TDI isn't listed b/c default TDI (column_name) will be a valid d3d pointname
    treename=dict(EAST='EFIT_EAST'),  # DIII-D isn't listed b/c default is None, which goes to PTDATA
    tfactor=dict(EAST=1e3),  # d3d times are already in ms
    tmin='t min',  # str means look up col w/ header exactly matching this string & reads timing values from there
    tmax='t max',
)

# Here's the same thing, but it fills in everything explicitly instead of letting the defaults take care of it.
# Filling it out this much is unnecessary, but we did it here to illustrate what is happening.
explicit_sample_signal_request = dict(
    column_name='density',
    TDI={'DIII-D': 'density', 'EAST': r'\dfsdev'},
    treename={'DIII-D': None, 'EAST': 'PCS_EAST'},
    factor={'DIII-D': 1e-13, 'EAST': 1.0},
    tfactor={'DIII-D': 1.0, 'EAST': 1e3},
    tmin='t min',  # This should exactly match a column header in the sheet
    tmax='t max',  # This should exactly match a column header in the sheet
    operation='mean',
)

# If the sheet in question had ONLY DIII-D data, the same signal request could be accomplished via:
simple_sample_signal_request = dict(column_name='density', factor=1e-13, tmin='t min', tmax='t max')

# If all of your shots should be processed over the same time range, you can use floats instead of strings:
simple_sample_signal_request_uniform_timing = dict(column_name='density', factor=1e-13, tmin=1500.0, tmax=5000.0)

# Once the signals are set up, the call is simple
# -----------------------------------------------------------------------------------------------------------------
xtable.write_mds_data_to_table([sample_signal, sample_signal_2], overwrite=False, device_servers=device_servers)

# EFIT data are even easier to handle because their device-specific name variations follow predictable patterns,
# as do the tree arrangements. So, for EFITs, you can get away with just a list of pointnames and some timing
# commands using the write_efit_result() method. However, this method ASSUMES THAT THE COLUMN NAMES EXACTLY MATCH
# EFIT POINTNAMES. This command does the same thing as putting sample_signal_request2 into
# write_mds_data_to_table(), but it's easier to set up:
xtable.write_efit_result(pointnames=['betan'], tmin='t min', tmax='t max', device_servers=dict(EAST='EAST_US'))