using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.SharePoint;
using System.Collections;
namespace FusionCharts.WebParts
{
///
/// Converts a SharePoint List (SPList) into a Plots dataset
///
public class ListReader
{
#region PrivateMembers
private ITrace _tracer; // printf style debugging stream (into comment in page html) for developers
private string _listName; // The name of the SPList to transform
private string _viewName; // The name of the SPView to use
private SPList _list; //The SPList to transform
private SPView _view; // The SPView to use
private SPListItemCollection _items; // Items in the SPList
#endregion
#region Constructors
///
/// Constructor
///
/// The SharePoint list to tranform into XML for Fusion Charts
/// The SharePoint view to use with the list
public ListReader(ITrace tracer, string listName, string viewName)
{
// We initialize the private members
_tracer = tracer;
_listName = listName;
_viewName = viewName;
}
#endregion
#region PrivateMethods
///
/// Retrieves a SPList from its name
///
private void GetListAndView()
{
if (string.IsNullOrEmpty(_listName))
{
throw (new Exception("List name cannot be empty"));
}
// We open the SPList
Guid siteID = SPContext.Current.Site.ID;
Guid webID = SPContext.Current.Web.ID;
using (SPSite site = new SPSite(siteID))
{
using (SPWeb web = site.OpenWeb(webID))
{
// We get the list from its name
try
{
_list = web.Lists[_listName];
}
catch
{
throw (new Exception("Cannot find specified list name: " + _listName));
}
// We get the view from its name or get the default one
if (!string.IsNullOrEmpty(_viewName))
{
try
{
_view = _list.Views[_viewName];
}
catch
{
throw (new Exception("Cannot find specified view name: " + _viewName));
}
}
else
{
_view = _list.DefaultView;
}
}
}
}
///
/// Check that requested fields are in list (or else throw exception)
///
private void ValidateFields(string xFieldName, List yFieldNames, GroupAction action)
{
ValidateFieldNameInList(xFieldName);
if (action == GroupAction.COUNT)
{
// does not use y field
return;
}
foreach (string fieldName in yFieldNames)
{
ValidateFieldNameInList(fieldName);
}
}
///
/// Check that specified nameis in list (or else throw exception)
///
private void ValidateFieldNameInList(string fieldName)
{
if (!_list.Fields.ContainsField(fieldName))
{
string message = "The column \""
+ fieldName + "\" does not exist in the list \""
+ _list.Title + "\" or in the specified view \""
+ _view.Title + "\".
Check the view you specified and make sure that the column exists.";
message += ListValidColumns();
throw (new Exception(message));
}
}
private string FieldRefNullable(string fieldname)
{
// fieldname should be internal name
string intname = fieldname; // passing in internal name
return String.Format("", intname);
}
///
/// Get the list of items in the list based on the view
///
private void QueryList(string xFieldName, List yFieldNames)
{
SPQuery query = new SPQuery(_view);
string viewFields = FieldRefNullable(xFieldName);
foreach (string fieldName in yFieldNames)
{
if (!string.IsNullOrEmpty(fieldName))
{
viewFields += FieldRefNullable(fieldName);
}
}
query.ViewFields = viewFields;
_items = _list.GetItems(query);
}
///
/// Returns a list of valids column name that can be used.
///
///
private string ListValidColumns()
{
StringBuilder sb = new StringBuilder();
sb.Append("
Valid columns are:
");
foreach (string field in _view.ViewFields)
{
sb.Append(" - " + field + "
");
}
return sb.ToString();
}
private void TraceLine(string msg)
{
if (_tracer != null) _tracer.TraceLine(msg);
}
#endregion
#region PublicProperties
public SPList ParsedList { get { return _list; } }
public SPView ParsedView { get { return _view; } }
#endregion
#region PublicMethods
public void FindListView()
{
// We perform startup tasks
GetListAndView();
}
///
/// Transforms the list of items into something easily usable (a Plots object).
/// Performs also operations such as SUM, COUNT over GROUPBY
///
/// The name of the column to use as xValue
/// Nonempty list of column names for y values
///
public Plots GetPlots(string xFieldName, List yFieldNames, GroupAction action)
{
if (yFieldNames.Count == 0) throw new Exception("Empty yFieldNames list passed to GetPlots");
ValidateFields(xFieldName, yFieldNames, action);
QueryList(xFieldName, yFieldNames);
int nseries = yFieldNames.Count;
Plots plots = new Plots(nseries);
// We loop through our SPList and get all the x & y values
foreach (SPListItem item in _items)
{
// We get the X and Y values from the list via ItemValueReader
// which contains code to handle lookup fields, currency fields, and user fields
string x = ItemValueReader.GetItemValue(item, xFieldName);
if (x == null)
continue;
List yvalues = new List(nseries);
List yctvalues = null; // only assign it if we need it (GroupAction.Average)
for (int i = 0; i < nseries; ++i)
{
string fieldvalue = ItemValueReader.GetItemValue(item, yFieldNames[i]);
double y = Double.NaN;
if (!ReadNumericValue(fieldvalue, out y))
{
y = Double.NaN;
}
yvalues.Add(y);
}
// The value is already in our array
if (plots.Contains(x))
{
int pos = plots.XIndexOf(x);
if (action == GroupAction.NOTHING)
{
// Add new point at same x value
plots.Add(x, yvalues);
}
else
{
// modify existing point at x value
List oldvalues = plots.GetY(pos);
if (action == GroupAction.COUNT)
{
// Count, we add +1 to the existing series point for each series point we have
for (int i = 0; i < nseries; ++i)
{
double newvalue = yvalues[i];
if (newvalue != Double.NaN)
{
oldvalues[i] += 1;
}
}
}
else if (action == GroupAction.SUM)
{
// Sum, we add the value to the existing value
for (int i = 0; i < nseries; ++i)
{
double newvalue = yvalues[i];
if (newvalue != Double.NaN)
{
oldvalues[i] += newvalue;
}
}
}
else if (action == GroupAction.AVERAGE)
{
// Sum yvalues, we add the value to the existing value
for (int i = 0; i < nseries; ++i)
{
double newvalue = yvalues[i];
if (newvalue != Double.NaN)
{
oldvalues[i] += newvalue;
}
}
List oldyctvalues = plots.GetYct(pos);
// Count them in yctvalues, we add +1 to the existing series point for each series point we have
for (int i = 0; i < nseries; ++i)
{
double newvalue = yvalues[i];
if (newvalue != Double.NaN)
{
oldyctvalues[i] += 1;
}
}
}
// we modified the existing y data (oldvalues) in place
}
}
else // The value is a new one in our array
{
if (action == GroupAction.COUNT)
{
// Count, the first yValue is 1 (if we have it, else 0)
for (int i = 0; i < nseries; ++i)
{
double newvalue = yvalues[i];
yvalues[i] = (newvalue != Double.NaN ? 1 : 0);
}
}
else if (action == GroupAction.SUM)
{
// Sum, the first yValue is y (if we have it, else 0)
for (int i = 0; i < nseries; ++i)
{
double newvalue = yvalues[i];
yvalues[i] = (newvalue != Double.NaN ? newvalue : 0);
}
}
else if (action == GroupAction.AVERAGE)
{
yctvalues = new List(nseries);
for (int i = 0; i < nseries; ++i)
{
double newvalue = yvalues[i];
if (newvalue == Double.NaN)
{
yvalues[i] = 0;
yctvalues.Add(0);
}
else
{
yvalues[i] = newvalue;
yctvalues.Add(1);
}
}
}
else
{
// anything else than Count, we just add the plot
// which is already populated in yvalues
}
if (yctvalues != null)
{
plots.AddWithCounts(x, yvalues, yctvalues);
}
else
{
plots.Add(x, yvalues);
}
}
}
if (action == GroupAction.AVERAGE)
{
// Update yvalues in place - calculate arithmetic mean
for (int pos = 0; pos < plots.Count(); ++pos)
{
List yvalues = plots.GetY(pos);
List yctvalues = plots.GetYct(pos);
for (int i = 0; i < nseries; ++i)
{
if (yctvalues[i] > 0)
{
double newvalue = yvalues[i] / (double)yctvalues[i];
yvalues[i] = newvalue;
}
}
}
}
return plots;
}
///
/// Parse numeric value from string, allowing for currency marks (e.g., $10,000)
///
private bool ReadNumericValue(string value, out double numeric)
{
return Double.TryParse(value, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.CurrentCulture.NumberFormat, out numeric);
}
#endregion
}
public enum GroupAction
{
NOTHING = 0,
SUM,
COUNT,
AVERAGE
}
}