Update Spreadsheet

Posts: 15
Hi,

i have one problem after the build with spreadsheet path dynamic. Before this build, i have a script for point calculation and i update the spreadsheet in script after race and calculation to have a unofficial classification of championship.

This script not work anymore now.

Have you an idea to do this ?
* a spreadsheet with classification of championship before race
* a script to update this
* a widget to display this update


next post for integrate example this evening.

Thanks in advance
Posts: 15
for example:

DataTable _iUnofficialResult = null;
_iUnofficialResult = item.Theme.Spreadsheets.Find("IFRN_Unofficial.csv").Table.Data.Copy();

//point calculation

item.Theme.Spreadsheets.Find("IFRN_Unofficial_Result.csv").Table.Data.Clear();  // this line generate error

item.Theme.Spreadsheets.Find("IFRN_Unofficial_Result.csv").Table.Data.Load(_sortedDT.CreateDataReader());
item.Theme.Widgets.Find("W17_UnofficialRankingData1").Ticker.UpdateRepeatedSubWidgets(true,false);

Edited (2 times)
Posts: 785
If I understood correctly you want to update the data in a spreadsheet with new data while your theme is running?

I think what should work is to use a dynamic spreadsheet (set "External filepath" property) and call the Parse function to make it reload the data.

So for your spreadsheet set External Filepath to some path. Then after the race, you put an actual spreadsheet in that location (make sure it has the same columns as the original). Then in a script simply call
var spreadsheet = item.Theme.Spreadsheets.Find("IFRN_Unofficial_Result.csv")
spreadsheet.Parse();


This should make it re-load the data, and since you set the External Filepath it should read it from the file you just placed.
Posts: 15
Nick thanks for your answere but it is not totaly the functionality that i search.

y es i want update spreadsheet, but the update is a script to do that.

i have spreadsheet with championship classification, and after the race, a script update this classification with calculation of point that driver have in the race.

So the first spreadsheet is the classification before race.
The script calcul point and other in datatable.
after calculation is ok, i order the datatable and i want to display this in widget with ticker function and pagination

so the widget dataset is a spreadsheet, i don't think i can use other to do that.

Before external filepath build, the code you can see, work but now didn't work.

Error: 16/10/2018 19:40:06 (UTC)
Unhandled exception
System.Data.RowNotInTableException: Cette ligne a été supprimée d'une table et ne contient pas de données. BeginEdit() permettra la création de nouvelles données dans cette ligne.
  à System.Data.DataRow.GetDefaultRecord()
  à System.Data.DataRow.get_Item(Int32 columnIndex)
  à ATVO.ThemeEditor.ThemeModels.DataSets.SpreadsheetDataBinding.<>c__DisplayClass0_0.<.ctor>b__0(DataRow c) dans F:\Appgineer.in\Appgineer.in TV Overlay for iRacing\ATVO.ThemeEditor\ThemeModels\DataSets\SpreadsheetDataSet.cs:ligne 65
  à ATVO.ThemeEditor.Data.DataSet`1.GetData(DataBindingBase bindingBase, ISimulation sim, IDataOrder order, DataClassIndex classIndex, Int32 offset) dans F:\Appgineer.in\Appgineer.in TV Overlay for iRacing\ATVO.ThemeEditor.Data\DataSet.cs:ligne 94
  à ATVO.ThemeEditor.Data.DataSet`1.GetData(String bindingKey, ISimulation sim, IDataOrder order, DataClassIndex classIndex, Int32 offset) dans F:\Appgineer.in\Appgineer.in TV Overlay for iRacing\ATVO.ThemeEditor.Data\DataSet.cs:ligne 122
  à ATVO.ThemeEditor.ThemeModels.DataBinderContainer.GetBindingValue(DataBindingBase binding, DataSetBase dataset, ISimulation sim) dans F:\Appgineer.in\Appgineer.in TV Overlay for iRacing\ATVO.ThemeEditor\ThemeModels\DataBinderContainer.cs:ligne 75
  à ATVO.ThemeEditor.ThemeModels.DataBinderContainer.SetValueAndUpdate(ISimulation sim) dans F:\Appgineer.in\Appgineer.in TV Overlay for iRacing\ATVO.ThemeEditor\ThemeModels\DataBinderContainer.cs:ligne 101
  à ATVO.ThemeEditor.ThemeModels.ThemeDataUpdater.UpdateDataBinders(List`1 databinders, ISimulation sim, Boolean designMode, DebugModes debugMode, Double time, Double deltatime) dans F:\Appgineer.in\Appgineer.in TV Overlay for iRacing\ATVO.ThemeEditor\ThemeModels\ThemeDataUpdater.cs:ligne 246
  à ATVO.ThemeEditor.ThemeModels.ThemeDataUpdater.Update(ISimulation sim, Boolean designMode, DebugModes debugMode) dans F:\Appgineer.in\Appgineer.in TV Overlay for iRacing\ATVO.ThemeEditor\ThemeModels\ThemeDataUpdater.cs:ligne 51
  à ATVO.Theme.Code.EditorTheme.<SimOnDataUpdatedEvent>b__27_0() dans F:\Appgineer.in\Appgineer.in TV Overlay for iRacing\ATVO.Theme\Code\EditorTheme.cs:ligne 122
  à System.Windows.Threading.DispatcherOperation.InvokeDelegateCore()
  à System.Windows.Threading.DispatcherOperation.InvokeImpl()
--- Fin de la trace de la pile à partir de l'emplacement précédent au niveau duquel l'exception a été levée ---
  à System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
  à System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
  à System.Windows.Threading.DispatcherOperation.Wait(TimeSpan timeout)
  à System.Windows.Threading.Dispatcher.InvokeImpl(DispatcherOperation operation, CancellationToken cancellationToken, TimeSpan timeout)
  à System.Windows.Threading.Dispatcher.Invoke(Action callback, DispatcherPriority priority, CancellationToken cancellationToken, TimeSpan timeout)
  à System.Windows.Threading.Dispatcher.Invoke(Action callback)
  à ATVO.Theme.Code.EditorTheme.SimOnDataUpdatedEvent(Object sender, ISimulation simulation) dans F:\Appgineer.in\Appgineer.in TV Overlay for iRacing\ATVO.Theme\Code\EditorTheme.cs:ligne 119
  à ATVO.Data.Simulation.UpdateData() dans F:\Appgineer.in\Appgineer.in TV Overlay for iRacing\ATVO.Data\Simulation.cs:ligne 253





sorry for my bad english, i work on it ^^
Edited (3 times)
Posts: 785
Ok so you want to directly replace the data in the dataset which is shown on the widget?

Perhaps you can grab each DataRow for each driver and update the value for the appropriate column. I think that may work, but I cannot test it right now.

Something like this. Suppose your identifier is the car number and it is in column 0, and suppose the champ points is in column 5:
// Columns
var colId = 0;
var colPoints = 5;

// Spreadsheet
var spreadsheet = item.Theme.Spreadsheets.Find("x.csv");

// Loop through results
foreach (var result in results)
{
// Find row for this car
DataRow row = spreadsheet.FindRow(result.Entity.Car.CarNumber, colId);

// Calculate new points somehow
var newPoints = 123;

// Set the value for the appropriate column
row[colPoints] = newPoints;
}

This will reset whenever the spreadsheet is reloaded (theme restart or you somehow change any of its properties via a script).
Posts: 15
i wil try but not sure is that functionnality that i search.

the theme is running at practice to end of race and we display the unofficial championship classification. The the calculation run and we display without restart theme.

If i try your solution, we need to refresh widget to update the update information of spreadsheet and we need to ordered this.

I return to you
Posts: 15
delete
Edited (3 times)
Posts: 15
It is ok for update, but the order is not good
Posts: 785
How were you changing the order previously? Simply replacing the data in the spreadsheet Table object won't change the order I'm pretty sure...

The error you are getting I think is because you are clearing the DataTable but some rows are still being used (probably due to the existing data bindings in the widget). DataRows don't like being separated from their DataTable and you can get this error.

An alternative way that may work is to make a separate Spreadsheet (and therefore separate DataTable) which is not connected to the original. Instead of clearing the data from the original spreadsheet, you simply add it to an empty spreadsheet instead.

For this to work you should also make a separate Widget for the final championship standings. For this you use a Spreadsheet as the data set (not iRacing standings). The spreadsheet should have columns that you want to display in the standings, so probably driver name, car number, champ points at least (maybe more). During theme development you can leave the data empty, just make sure the columns are there.

In the template SubWidget you can now bind labels to each column. The ticker will create a subwidget for each data row.

When running the theme, after the race you should be able to manually fill this spreadsheet with the relevant data. After filling the data the champ standings widget should update (or maybe you have to call UpdateRepeatedSubWidgets).

To fill the new spreadsheet I think you can do just like you did before, by making a new DataTable in memory and calling spreadsheet.Table.Data.Load(your new datatable).

The difference here is that you're not clearing the data from the original DataTable, you just put new data in another empty DataTable, which is being shown in a different Widget.
Posts: 15
I try this but i have no data displayed.

Maybe you have an idea to ordered data in spreadsheet or for display in widget before the data displayed ?
Posts: 785
Can you post your full script?
Posts: 15
this evening yes, i post it
Posts: 15
using System;
using ATVO.ThemesSDK;
using ATVO.ThemeEditor.ThemeModels;
using ATVO.ThemeEditor.Scripting.DotNET;
using ATVO.ThemesSDK.Data;
using ATVO.ThemesSDK.Data.Results;
using ATVO.ThemesSDK.Data.Entity;
using ATVO.ThemesSDK.Data.Enums;
using System.ComponentModel;
using System.Data;

namespace Scripts
{
public class SC03_CalculUnofficial : IScript
{
public object Execute(ThemeContentItem item, object value, string parameter, ISimulation sim)
{
int[] _PointOfChamp = new int[]{0,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1};

bool _iCourseOK = false;

int _DriverMostPositionUp=0;
int _iMostPositionUp=0;

int _DriverMostLapLead=0;
int _iMostLapLead=0;

int _Poleman=0;

float _iPoleTime107=0;
float _i107Pourcent = float.Parse("1,07");

DataRow _iDriverSpreadsheet = null;
DataTable _iUnofficialResult = null;

Spreadsheet _iUnoff_Spread = item.Theme.Spreadsheets.Find("IFRN_Unofficial.csv");

// search driver in spreadsheet
foreach (DataRow row in _iUnoff_Spread.Table.Data.Rows)
{

row[2] = row[2].ToString().Replace("\"", "");
row[4] = row[4].ToString().Replace("\"", "");
row[5] = row[5].ToString().Replace("\"", "");
row[9] = row[9].ToString().Replace("\"", "");

}


// Get qual and race results
ISessionResult _Course = sim.Session.GetLatestRace();
ISessionResult _Qualif = sim.Session.GetQualification();


//_iUnofficialResult = item.Theme.Spreadsheets.Find("IFRN_Unofficial.csv").Table.Data.Copy();

Console.WriteLine("SC03.1");

// Check if Session is null
if (sim.Session == null)
{
Console.WriteLine("Session is null.");
return value;
}

// Check for null
if (_Qualif == null)
{
Console.WriteLine("Qual session is null.");
return value;
}
if (_Course == null)
{
Console.WriteLine("Race session is null.");
return value;
}

// Check FastestLapDriver
if (_Qualif.Leader == null)
{
Console.WriteLine("Leader is null.");
return value;
}


Console.WriteLine("Poleman: " + _Qualif.Leader.Name);
_Poleman = _Qualif.Leader.Id;
_iPoleTime107 = _Qualif.FastestLapTime * _i107Pourcent;
_iCourseOK = true;




Console.WriteLine("SC03.2");

// Race Information
if(_iCourseOK)
{
foreach (IEntitySessionResult _EntitySessionResult in _Course.Results)
{
int _nbPointAdd = 0;
_iDriverSpreadsheet=null;


IEntity _iPiloteTraite = _EntitySessionResult.Entity;
// Check Pilote traité is not null
if (_iPiloteTraite == null)
{
Console.WriteLine("Pilote Traité is null.");
return value;
}


float _DriverFastQualifyLap;

IEntitySessionResult _iPiloteTraite_Qualif = _iPiloteTraite.Results.GetResult(SessionType.Qualify);
// Check Driver Qualif is not null
if (_iPiloteTraite_Qualif == null)
{
Console.WriteLine("Pilote Traité Qualify is null for " + _iPiloteTraite.Name);
_DriverFastQualifyLap = 999;
}
else
{
_DriverFastQualifyLap = _iPiloteTraite_Qualif.FastestLapTime;
}


_iDriverSpreadsheet = _iUnoff_Spread.FindRow(_EntitySessionResult.Entity.Id.ToString(), 0);


if (_iDriverSpreadsheet == null)
{
_iDriverSpreadsheet = _iUnofficialResult.NewRow();
_iDriverSpreadsheet[0]=_iPiloteTraite.Id;
_iDriverSpreadsheet[1]=_iPiloteTraite.Car.Id;
_iDriverSpreadsheet[2]=_iPiloteTraite.Car.Number;
_iDriverSpreadsheet[9]=_iPiloteTraite.Name;
_iDriverSpreadsheet[10]=0;
_iDriverSpreadsheet[17]=0;
_iUnofficialResult.Rows.Add(_iDriverSpreadsheet);
}



// part of point of most position up (qualify time != 0 and 107% of pole time)
if(_DriverFastQualifyLap > 0)
{
if (_DriverFastQualifyLap <= _iPoleTime107)
{
if (_EntitySessionResult.StartPosition - _EntitySessionResult.LivePosition > _iMostPositionUp)
{
_iMostPositionUp = _EntitySessionResult.StartPosition - _EntitySessionResult.LivePosition;
_DriverMostPositionUp = _iPiloteTraite.Id;
}
}
}


// Point of lead lap (+1 point)
if(_EntitySessionResult.LapsLed > 0 )
{
_nbPointAdd += 1;
}


// points of most lead lap (+1 point)
if(_EntitySessionResult.LapsLed > _iMostLapLead)
{
_DriverMostLapLead = _iPiloteTraite.Id;
_iMostLapLead =_EntitySessionResult.LapsLed;
}


// points of race
_nbPointAdd += _PointOfChamp[_EntitySessionResult.Position];


// qualify pole
if (_Poleman!=0)
{
if (_Poleman == _iPiloteTraite.Id)
{
_nbPointAdd += 1;
}
}


// add previous point
if ( _iDriverSpreadsheet[17].ToString() != "0")
{
if (_iDriverSpreadsheet[10].GetType() == typeof(int))
{
_iDriverSpreadsheet[10] = int.Parse(_iDriverSpreadsheet[10].ToString()) + _nbPointAdd;
}
else
{
_iDriverSpreadsheet[10] =_nbPointAdd;
}
}
else
{
_iDriverSpreadsheet[10] = 0;
}
}
}


Console.WriteLine("SC03.3");
// add point of most lap lead and most position up
foreach (DataRow row in _iUnoff_Spread.Table.Data.Rows)
{
int _id =0;
if (int.TryParse(row[0].ToString(), out _id))
{
if (_DriverMostLapLead==_id )
{
Console.WriteLine("Best Tour Mené : " + row[9].ToString());
if (row[17].ToString() != "0")
{
row[10] = int.Parse(row[10].ToString()) + 1;
}
}
if (_DriverMostPositionUp == _id )
{
Console.WriteLine("Meilleur rémonté : " + row[9].ToString());
if (row[17].ToString() != "0")
{
row[10] = int.Parse(row[10].ToString()) + 1;
}
}
}
}


Console.WriteLine("SC03.4");
// sort spreadsheet
DataView _dv = _iUnoff_Spread.Table.Data.DefaultView;
_dv.Sort = _iUnoff_Spread.Table.Data.Columns[10].ColumnName + " DESC";
DataTable _sortedDT = _dv.ToTable();


Console.WriteLine("SC03.5");
int _i = 1;
int _PointLeader = 0;
foreach (DataRow row in _sortedDT.Rows)
{
DataRow _RowDriver = _iUnoff_Spread.FindRow(row[0].ToString(),0);

if (_PointLeader==0)
{
try
{
_PointLeader = int.Parse(_RowDriver[10].ToString());
} catch {}
}
else
{
try{
_RowDriver[10] = int.Parse(_RowDriver[10].ToString()) - _PointLeader;
} catch {}
}

try
{
_RowDriver[18] = int.Parse(_RowDriver[8].ToString()) - _i;

if (_RowDriver[18].ToString() == "0")
{
_RowDriver[18] = "";
_RowDriver[19] = "egal";
}
else if (int.Parse(_RowDriver[18].ToString()) < 0 )
{
_RowDriver[18] = int.Parse(_RowDriver[18].ToString()) * -1;
_RowDriver[19] = "moins";
}
else if (int.Parse(_RowDriver[18].ToString()) > 0 )
{
_RowDriver[19] = "plus";
}
}
catch
{
_RowDriver[18] = "";
_RowDriver[19] = "new";
}

_RowDriver[8] = _i;
_i += 1;
}




Console.WriteLine("SC03.6");
//item.Theme.Spreadsheets.Find("IFRN_Unofficial.csv").Table.Data.Clear();
_iUnoff_Spread.SortColumnIndex = 8;
_iUnoff_Spread.SortDirection = ListSortDirection.Ascending;
_iUnoff_Spread.Table.Sort();

//item.Theme.Widgets.Find("W17_UnofficialRankingPosGain").Ticker.UpdateRepeatedSubWidgets(true,false);
//item.Theme.Widgets.Find("W17_UnofficialRankingData1").Ticker.UpdateRepeatedSubWidgets(true,false);
//item.Theme.Widgets.Find("W17_UnofficialRankingData2").Ticker.UpdateRepeatedSubWidgets(true,false);

Console.WriteLine("Calcul Unofficial OK");

return value;
}
}
}
Posts: 785
Sorry Nicolas, I don't know where the problem lies exactly.

I am thinking about solving this problem for you in another way: by adding Championship standings and points to IEntity class. That way we can also give you a new Data Order to sort by Championship standings. Instead of creating a new DataTable manually you would then just set the champ standings and points and display them in a Widget with the new Data Order and everthing should work. Do you think this would work?
Posts: 15
i think so if we can select in widget this information and select the ordered it was perfect.

just last information, before the build of dynamic path for spreadsheet this line works and the update and sort was ok in display:
//item.Theme.Spreadsheets.Find("IFRN_Unofficial.csv").Table.Data.Clear();

but now we have an error on this line.