/// <summary>
        /// The export events.
        /// </summary>
        /// <returns>
        /// The <see cref="FileResult"/>.
        /// </returns>
        public FileResult ExportEvents()
        {
            PagedOwnedEvents pagedEvents = EventServices.GetMyEvents();

            CsvActionResult <Event> events = new CsvActionResult <Event>(pagedEvents.Events, "my_events.csv");

            return(events);
        }
        /// <summary>
        /// The export attendees.
        /// </summary>
        /// <param name="eventId">
        /// The event id.
        /// </param>
        /// <returns>
        /// The <see cref="FileResult"/>.
        /// </returns>
        public FileResult ExportAttendees(string eventId)
        {
            PagedAttendees pagedAttendees = AttendeesService.GetAttendeesByEventId(eventId);

            CsvActionResult <Attendee> attendees = new CsvActionResult <Attendee>(pagedAttendees.Attendees, "attendees.csv");

            return(attendees);
        }
        public CsvActionResult ExportTimeSeriesAsCsv(String cachedResourceId, String portfolioName, 
            String resultsTimestamp, String currency, String classifier, String culture)
        {
            const String DateFormat = "MMM dd yyyy";

            var errorResult = new CsvActionResult("Error.csv", "An error occurred.".CsvEncode());


            /* Check the args. */

            // Check that the connecting user is logged on.
            if (!SessionState.IsUserLoggedOn)
                return errorResult;

            // Get the cached resource data from session state (and remove from session state).
            var resourceData = SessionState.GetAndRemoveCachedResource(cachedResourceId);
            if (resourceData == null)
                return errorResult;

            // Parse the cached resource representation and its media type name, and check that it's a valid
            // time series resource in JSON format.
            var resourceResult = GetWebApiResourceResult(resourceData.Item1, resourceData.Item2, null).TypedData;
            if ((resourceResult == null) || (!resourceResult.success) || (resourceResult.type != "timeSeries") ||
                (!resourceResult.isJson))
            {
                return errorResult;
            }

            // Check that the mandatory args have been specified.
            if ((String.IsNullOrWhiteSpace(portfolioName)) || (String.IsNullOrWhiteSpace(resultsTimestamp)) ||
                (String.IsNullOrWhiteSpace(currency)) || (String.IsNullOrWhiteSpace(classifier)))
            {
                return errorResult;
            }

            // Parse the JSON time series resource representation.
            JObject jObj;
            try
            {
                jObj = JObject.Parse(resourceResult.representation);
            }
            catch (Exception)  // not sure what exceptions Newtonsoft.Json can throw, so will have to catch any/all
            {
                return errorResult;
            }


            /* Prepare to export the data as a string. */

            // Get the export culture.
            if (String.IsNullOrWhiteSpace(culture))
                culture = "en-US";
            CultureInfo exportCulture = null;
            try
            {
                exportCulture = new CultureInfo(culture);
            }
            catch (CultureNotFoundException)
            {
                exportCulture = new CultureInfo("en-US");
            }

            // Get the culture's list separator string.
            var separator = exportCulture.TextInfo.ListSeparator;

            // Reconstitute the results timestamp's RFC 3339 format and parse.
            resultsTimestamp = resultsTimestamp.Replace(' ', ':');
            var resultsTime = XmlConvert.ToDateTime(resultsTimestamp, XmlDateTimeSerializationMode.Utc);

            // Get information from the JSON representation.
            var jTimeSeries = jObj["timeSeries"];
            var jMeasures = (JArray)jTimeSeries["measures"];
            var jDataPoints = jTimeSeries["dataPoints"];
            var jItems = (JArray)jDataPoints["items"];
            var periodic = ((String)jDataPoints["type"] == "periodic");

            // Create the string builder (capacity: header = approx. 500 characters +
            // approx. 20 characters per field per data point).
            var numDataPoints = jItems.Count;
            var numFields = jMeasures.Count + (periodic ? 2 : 1);
            var output = new StringBuilder(500 + (20 * numFields * numDataPoints));


            /* Add the header. */

            // Add header lines.
            AppendCsvExportLine(output, separator, "Source", "StatPro Revolution Web API");
            AppendCsvExportLine(output, separator, "Exported at (GMT)", DateTime.UtcNow.ToString("F", exportCulture));
            AppendCsvExportLine(output, separator, "Dates/numbers exported in", exportCulture.DisplayName);
            AppendCsvExportLine(output, separator, "Results timestamp (GMT)", resultsTime.ToString("F", exportCulture));
            AppendCsvExportLine(output, separator, "Portfolio", portfolioName.Base64Decode(Encoding.UTF8, "n/a"));
            AppendCsvExportLine(output, separator, "Segment", (String)jTimeSeries["segmentName"]);
            AppendCsvExportLine(output, separator, "Classifier", classifier == "null" ? "--" : classifier);
            AppendCsvExportLine(output, separator, "Series type", (String)jTimeSeries["seriesType"]);
            AppendCsvExportLine(output, separator, "Start date", Iso8601DateToFormattedDate(
                (String)jTimeSeries["startDate"], DateFormat, exportCulture));
            AppendCsvExportLine(output, separator, "End date", Iso8601DateToFormattedDate(
                (String)jTimeSeries["endDate"], DateFormat, exportCulture));
            output.AppendLine();


            /* Add the line that contains the field headers for each data point line. */

            if (periodic)
                AppendCsvExportData(output, separator, "Start date", "End date");
            else
                AppendCsvExportData(output, separator, "Date");

            AppendCsvExportLine(output, separator, jMeasures.Select(measureId =>
                {
                    var id = (String)measureId;
                    var name = Resources.GetTimeSeriesMeasure(id);
                    if (name.Length > 0)
                        return name.Replace("[CUR]", currency);
                    else
                        return id;
                }).ToArray());


            /* Add the lines for the data points. */

            foreach (var item in jItems)
            {
                // Dates.
                if (periodic)
                {
                    AppendCsvExportData(output, separator,
                        Iso8601DateToFormattedDate((String)item["s"], DateFormat, exportCulture),
                        Iso8601DateToFormattedDate((String)item["e"], DateFormat, exportCulture));
                }
                else
                {
                    AppendCsvExportData(output, separator,
                        Iso8601DateToFormattedDate((String)item["d"], DateFormat, exportCulture));
                }

                // Values.
                AppendCsvExportLine(output, separator,
                    ((JArray)item["m"]).Select(value => ((Double?)value).NullableDoubleToString(exportCulture))
                                       .ToArray());
            }


            // Return the CSV data in an appropriately-named file.
            return new CsvActionResult("RevolutionWebAPITimeSeries.csv", output.ToString());
        }
        public async Task<CsvActionResult> ExportSegmentsTreeNodeAsCsv(String cachedResourceId, String portfolioName,
            String resultsTimestamp, String currency, String statsFrequency, String culture)
        {
            var errorResult = new CsvActionResult("Error.csv", "An error occurred.".CsvEncode());


            /* Check the args. */

            // Check that the connecting user is logged on.
            if (!SessionState.IsUserLoggedOn)
                return errorResult;

            // Get the cached resource data from session state (and remove from session state).
            var resourceData = SessionState.GetAndRemoveCachedResource(cachedResourceId);
            if (resourceData == null)
                return errorResult;

            // Parse the cached resource representation and its media type name, and check that it's a valid
            // segments tree node resource in JSON format.
            var resourceResult = GetWebApiResourceResult(resourceData.Item1, resourceData.Item2, null).TypedData;
            if ((resourceResult == null) || (!resourceResult.success) || (resourceResult.type != "segmentsTreeNode") ||
                (!resourceResult.isJson))
            {
                return errorResult;
            }

            // Check that the mandatory args have been specified.
            if ((String.IsNullOrWhiteSpace(portfolioName)) || (String.IsNullOrWhiteSpace(resultsTimestamp)) ||
                (String.IsNullOrWhiteSpace(currency)) || (String.IsNullOrWhiteSpace(statsFrequency)))
            {
                return errorResult;
            }


            /* Get a complete Segments Tree Node object (i.e. one that includes all children, which may be spread over
             * a number of 'pages'), and then get information that we need about the node.
             */

            var node = await GetCompleteSegmentsTreeNodeFromJsonAsync(resourceResult.representation);
            if (node == null)
                return errorResult;

            var segmentName = node.Segment.Name;
            var segmentPeriodicMeasures = node.Segment.PeriodicMeasures;
            var children = node.GetChildren();
            var allTimePeriodCodes = node.GetAllTimePeriodCodes();
            var allMeasureIds = node.GetAllMeasureIds();
            var fieldsCount = 1 + (allMeasureIds.Count * allTimePeriodCodes.Count);


            /* Prepare to export the data as a string. */

            // Get the export culture.
            if (String.IsNullOrWhiteSpace(culture))
                culture = "en-US";
            CultureInfo exportCulture = null;
            try
            {
                exportCulture = new CultureInfo(culture);
            }
            catch (CultureNotFoundException)
            {
                exportCulture = new CultureInfo("en-US");
            }

            // Get the culture's list separator string.
            var separator = exportCulture.TextInfo.ListSeparator;

            // Reconstitute the results timestamp's RFC 3339 format and parse.
            resultsTimestamp = resultsTimestamp.Replace(' ', ':');
            var resultsTime = XmlConvert.ToDateTime(resultsTimestamp, XmlDateTimeSerializationMode.Utc);

            // Create the string builder.
            var output = new StringBuilder(2048);

            // Callback to get a measure value as text from a list of measures-for-period, for a specified time
            // period code and measure id (or the empty string if not found).  We expect 'code' and 'id' to be
            // valid, and don't check them for null, empty, whitespace, etc.  Look-up of time period code and measure
            // id is done case-sensitively.  Conversion of double and integer values to text is done using default
            // formatting and the 'exportCulture' culture.
            Func<List<MeasuresForPeriod>, String, String, String> getMeasureAsText = (measuresForPeriod, code, id) =>
                {
                    if (measuresForPeriod == null)
                        return String.Empty;

                    var measure = measuresForPeriod.Where(mfp => mfp.TimePeriodCode == code)
                                                   .Select(mfp => mfp.Measures.FirstOrDefault(m => m.Id == id))
                                                   .FirstOrDefault();
                    if (measure == null)
                        return String.Empty;

                    switch(measure.TypeIndicator)
                    {
                        case 'r' :
                            return measure.Value.NullableDoubleToString(exportCulture);
                        case 'i' :
                            return measure.IntegerValue.NullableInt32ToString(exportCulture);
                        case 's' :
                            return measure.StringValue ?? String.Empty;
                        default :
                            return String.Empty;
                    }
                };


            /* Add the header. */

            // Add header lines.
            AppendCsvExportLine(output, separator, "Source", "StatPro Revolution Web API");
            AppendCsvExportLine(output, separator, "Exported at (GMT)", DateTime.UtcNow.ToString("F", exportCulture));
            AppendCsvExportLine(output, separator, "Numbers exported in", exportCulture.DisplayName);
            AppendCsvExportLine(output, separator, "Results timestamp (GMT)", resultsTime.ToString("F", exportCulture));
            AppendCsvExportLine(output, separator, "Portfolio", portfolioName.Base64Decode(Encoding.UTF8, "n/a"));
            AppendCsvExportLine(output, separator, "Segment", segmentName);
            if (children != null)
                AppendCsvExportLine(output, separator, "Children", children.Item2);
            output.AppendLine();


            /* Add the line that contains the field headers. */

            var fields = new List<String>(fieldsCount);
            fields.Add("Name");
            allMeasureIds.ForEach(id =>
            {
                allTimePeriodCodes.ForEach(code =>
                {
                    fields.Add(GetSegmentsTreeMeasureName(id, currency, statsFrequency) + " - " + code);
                });
            });
            AppendCsvExportLine(output, separator, fields.ToArray());


            /* Add the lines for the segment and children (if any). */

            // Segment.
            fields.Clear();
            fields.Add(segmentName);
            allMeasureIds.ForEach(id =>
            {
                allTimePeriodCodes.ForEach(code =>
                {
                    fields.Add(getMeasureAsText(segmentPeriodicMeasures, code, id));
                });
            });
            AppendCsvExportLine(output, separator, fields.ToArray());

            // Children.
            if (children != null)
            {
                var list = children.Item1;
                list.ForEach(child =>
                {
                    fields.Clear();
                    fields.Add(child.Name);
                    allMeasureIds.ForEach(id =>
                    {
                        allTimePeriodCodes.ForEach(code =>
                        {
                            fields.Add(getMeasureAsText(child.PeriodicMeasures, code, id));
                        });
                    });
                    AppendCsvExportLine(output, separator, fields.ToArray());
                });
            }


            // Return the CSV data in an appropriately-named file.
            return new CsvActionResult("RevolutionWebAPISegmentsTreeNode.csv", output.ToString());
        }