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 } }