#define __SCRAPING_ALLOWED__
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;
using System.ComponentModel;
using ReputationLib;
namespace RepWPF
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private bool m_initialized = false;
private string comma = CultureInfo.CurrentCulture.NumberFormat.NumberGroupSeparator;
//--------------------------------------------------------------------------------
public MainWindow()
{
string[] args = Environment.GetCommandLineArgs();
// Parameters to adjust database - because args[0] is the name of this
// program, we can safely ignore it, but that also means we have to account
// for it before trying to retrieve the real commandline arguments.
if (args.Length > 1)
{
for (int i = 1; i < args.Length; i++)
{
string arg = args[i];
switch (arg.ToLower())
{
case "db1" :
case "/db1" :
case "-db1" :
Globals.AdjustExistingDbTotals = true;
break;
}
}
}
RepSettings.Default.Reload();
Globals.UserID = RepSettings.Default.UserID;
Globals.ConnectionString = RepSettings.Default.ConnectionString;
InitializeComponent();
// retrieve our data from the database
#if __SCRAPING_ALLOWED__
WpfGlobals.Scraper.Reputations.GetData();
#else
WpfGlobals.Scraper.Reputations.TestFill(new DateTime(2011, 7, 1), 7741);
#endif
// add event handlers for the scraper
WpfGlobals.Scraper.ScrapeComplete += new ScraperEventHandler(Scraper_ScrapeComplete);
WpfGlobals.Scraper.ScrapeFail += new ScraperEventHandler(Scraper_ScrapeFail);
WpfGlobals.Scraper.ScrapeProgress += new ScraperEventHandler(Scraper_ScrapeProgress);
}
//--------------------------------------------------------------------------------
~MainWindow()
{
AddControlEventHandlers(false);
WpfGlobals.Scraper.ScrapeComplete -= new ScraperEventHandler(Scraper_ScrapeComplete);
WpfGlobals.Scraper.ScrapeFail -= new ScraperEventHandler(Scraper_ScrapeFail);
WpfGlobals.Scraper.ScrapeProgress -= new ScraperEventHandler(Scraper_ScrapeProgress);
}
//--------------------------------------------------------------------------------
private void Window_Loaded(object sender, RoutedEventArgs e)
{
InitControls();
AddControlEventHandlers(true);
m_initialized = true;
RenderChart();
CalculateTopSection();
}
#region "ChartConfig panel events"
//--------------------------------------------------------------------------------
void checkBoxTrendLines_Unchecked(object sender, RoutedEventArgs e)
{
if (m_initialized)
{
RenderChart();
}
}
//--------------------------------------------------------------------------------
void checkBoxTrendLines_Checked(object sender, RoutedEventArgs e)
{
if (m_initialized)
{
RenderChart();
}
}
//--------------------------------------------------------------------------------
void comboTimePeriod_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (m_initialized)
{
RenderChart();
}
}
//--------------------------------------------------------------------------------
void comboChart_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (m_initialized)
{
RenderChart();
}
}
//--------------------------------------------------------------------------------
void checkedLBCategories_CheckedChanged(object sender, Microsoft.Windows.Controls.CheckListBoxCheckedChangedEventArgs e)
{
if (m_initialized)
{
RenderChart();
}
}
#endregion "ChartConfig panel events"
#region "Stated Goal panel"
//--------------------------------------------------------------------------------
/// <summary>
/// Fired when the user selects a new goal date (via the calendar dropdown) in
/// the stated goal panel.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void dateTimePickerGoalDate_ValueChanged(object sender, SelectionChangedEventArgs e)
{
}
//--------------------------------------------------------------------------------
/// <summary>
/// Fired when the user clicks the recalculate button in the stated goal panel.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void buttonRecalcGoal_Click(object sender, RoutedEventArgs e)
{
int goalPoints;
string text = this.StatedGoalPanel.textBoxGoalPoints.Text.Replace(comma, "").Replace(",", "").Replace(".", "");
if (int.TryParse(text, out goalPoints))
{
DateTime goalDate = this.StatedGoalPanel.dateTimePickerGoalDate.SelectedDate.Value.Date;
DateTime now = DateTime.Now.Date;
int latestValue = WpfGlobals.Scraper.Reputations.GetLatestPointValue(RepCategory.Total);
int avgValue = WpfGlobals.Scraper.Reputations.GetDailyAverage(RepCategory.Total);
TimeSpan span = goalDate - now;
int futurePoints = latestValue + (avgValue * span.Days);
int pointsDiff = futurePoints - goalPoints;
// this needs to be calculated based on trend information instead of the
// current average.
if (pointsDiff > 1000)
{
this.StatedGoalPanel.labelGoalStatus.Content = "Excellent";
}
else if (pointsDiff > -1000 && pointsDiff < 1000)
{
this.StatedGoalPanel.labelGoalStatus.Content = "Good";
}
else
{
this.StatedGoalPanel.labelGoalStatus.Content = "Poor";
}
}
}
//--------------------------------------------------------------------------------
/// <summary>
/// Fired when the user changes the contents of the goal points in the stated
/// goal panel.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void textBoxGoalPoints_TextChanged(object sender, TextChangedEventArgs e)
{
string validchars = "0123456789,";
TextBox textbox = sender as TextBox;
string text = textbox.Text.Replace(comma, "").Replace(",", "").Replace(".", ""); ;
int points = 0;
if (!string.IsNullOrEmpty(text))
{
char lastChar = text.Last();
if (!validchars.Contains(lastChar))
{
text = text.Substring(0, text.Length - 1);
}
if (!string.IsNullOrEmpty(text))
{
points = Convert.ToInt32(text);
text = string.Format("{0:#,#}", points);
}
if (textbox.Text != text)
{
textbox.Text = text;
if (!string.IsNullOrEmpty(text))
{
textbox.SelectionStart = text.Length - 1;
}
}
}
this.StatedGoalPanel.buttonRecalcGoal.IsEnabled = (points > 0);
}
#endregion "Stated Goal panel"
#region "Config panel"
//--------------------------------------------------------------------------------
/// <summary>
/// Fired when the user changes the contents of the userID textbox in the config
/// panel.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void textBoxUserID_TextChanged(object sender, TextChangedEventArgs e)
{
string validchars = "0123456789,";
TextBox textbox = sender as TextBox;
string text = textbox.Text;
int id = 0;
if (!string.IsNullOrEmpty(text))
{
char lastChar = text.Last();
if (!validchars.Contains(lastChar))
{
text = text.Substring(0, text.Length - 1);
}
if (!string.IsNullOrEmpty(text))
{
id = Convert.ToInt32(text);
}
if (textbox.Text != text)
{
textbox.Text = text;
if (!string.IsNullOrEmpty(text))
{
textbox.SelectionStart = text.Length - 1;
}
}
}
#if (__SCRAPING_ALLOWED__)
this.ConfigPanel.buttonGetData.IsEnabled = (id > 0);
#else
this.ConfigPanel.buttonGetData.IsEnabled = false;
#endif
}
//--------------------------------------------------------------------------------
/// <summary>
/// Fired when the user clicks the manage service button in the configpanel.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void buttonManageSvc_Click(object sender, RoutedEventArgs e)
{
ExternalProcess.Run("RepSvcManager.exe", "", true);
}
//--------------------------------------------------------------------------------
/// <summary>
/// Fired when the user clicks the Get Data button in the config panel.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void buttonGetData_Click(object sender, RoutedEventArgs e)
{
if (!string.IsNullOrEmpty(ConfigPanel.textBoxUserID.Text))
{
Globals.ConnectionString = RepSettings.Default.ConnectionString;
int userID;
string text = this.ConfigPanel.textBoxUserID.Text;
if (!string.IsNullOrEmpty(text) && Int32.TryParse(text, out userID))
{
RepSettings.Default.UserID = userID;
Globals.UserID = RepSettings.Default.UserID;
}
WpfGlobals.Scraper.ScrapeWebPage();
}
}
#endregion "Config panel"
#region "Main Scraper events"
//--------------------------------------------------------------------------------
/// <summary>
/// Fired when the main scraper sends a scrape completed event.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void Scraper_ScrapeComplete(object sender, ScrapeEventArgs e)
{
Dispatcher.Invoke(DispatcherPriority.Normal, (Action)delegate
{
WpfGlobals.Scraper.Reputations.CalcChangedValues();
CalculateTopSection();
RenderChart(); // runs on UI thread
});
}
//--------------------------------------------------------------------------------
/// <summary>
/// Fired when the main scraper sends a scrape failed event.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void Scraper_ScrapeFail(object sender, ScrapeEventArgs e)
{
}
//--------------------------------------------------------------------------------
/// <summary>
/// Fired when the main scraper sends a progress event (not used in this app).
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void Scraper_ScrapeProgress(object sender, ScrapeEventArgs e)
{
throw new NotImplementedException();
}
#endregion "Main Scraper events"
#region "Leader scraper events"
//--------------------------------------------------------------------------------
/// <summary>
/// Fired when the leader scrape fails
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void leaderScraper_ScrapeFail(object sender, ScrapeEventArgs e)
{
ScrapeLeader scraper = sender as ScrapeLeader;
Dispatcher.Invoke(DispatcherPriority.Normal, (Action)delegate
{
UpdateLeaderInfo(sender as ScrapeLeader, false);
});
scraper.ScrapeFail -= new ScraperEventHandler(leaderScraper_ScrapeFail);
}
//--------------------------------------------------------------------------------
/// <summary>
/// Fired when the leader scrape completes successfully
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void leaderScraper_ScrapeComplete(object sender, ScrapeEventArgs e)
{
ScrapeLeader scraper = sender as ScrapeLeader;
Dispatcher.Invoke(DispatcherPriority.Normal, (Action)delegate
{
UpdateLeaderInfo(sender as ScrapeLeader, true);
});
scraper.ScrapeComplete -= new ScraperEventHandler(leaderScraper_ScrapeComplete);
}
#endregion "Leader scraper events"
#region "Other methods (initialization)"
//--------------------------------------------------------------------------------
/// <summary>
/// Adds event handlers for all of the necessary child controls of the compoent
/// user controls.
/// </summary>
/// <param name="add"></param>
private void AddControlEventHandlers(bool add)
{
// I wanted to handle all of the control events in the main window class, so
// all of the controls have the FieldModifier="public" attribute. I know -
// this isn't very OOPtastic, but it's a helluva lot more convenient.
if (add)
{
ConfigPanel.buttonGetData.Click += new RoutedEventHandler(buttonGetData_Click);
ConfigPanel.buttonManageSvc.Click += new RoutedEventHandler(buttonManageSvc_Click);
ConfigPanel.textBoxUserID.TextChanged += new TextChangedEventHandler(textBoxUserID_TextChanged);
StatedGoalPanel.textBoxGoalPoints.TextChanged += new TextChangedEventHandler(textBoxGoalPoints_TextChanged);
StatedGoalPanel.buttonRecalcGoal.Click += new RoutedEventHandler(buttonRecalcGoal_Click);
StatedGoalPanel.dateTimePickerGoalDate.SelectedDateChanged += new EventHandler<SelectionChangedEventArgs>(dateTimePickerGoalDate_ValueChanged);
ChartConfigPanel.checkedLBCategories.CheckedChanged += new Microsoft.Windows.Controls.CheckListBoxCheckedChangedEventHandler(checkedLBCategories_CheckedChanged);
ChartConfigPanel.comboChart.SelectionChanged += new SelectionChangedEventHandler(comboChart_SelectionChanged);
ChartConfigPanel.comboTimePeriod.SelectionChanged += new SelectionChangedEventHandler(comboTimePeriod_SelectionChanged);
ChartConfigPanel.checkBoxTrendLines.Checked += new RoutedEventHandler(checkBoxTrendLines_Checked);
ChartConfigPanel.checkBoxTrendLines.Unchecked += new RoutedEventHandler(checkBoxTrendLines_Unchecked);
ConfigPanel.buttonGetData.IsEnabled = false;
#if (!__SCRAPING_ALLOWED__)
ConfigPanel.buttonGetData.IsEnabled = false;
ConfigPanel.textBoxUserID.IsEnabled = false;
#endif
}
else
{
ConfigPanel.buttonGetData.Click -= new RoutedEventHandler(buttonGetData_Click);
ConfigPanel.buttonManageSvc.Click -= new RoutedEventHandler(buttonManageSvc_Click);
ConfigPanel.textBoxUserID.TextChanged -= new TextChangedEventHandler(textBoxUserID_TextChanged);
StatedGoalPanel.textBoxGoalPoints.TextChanged -= new TextChangedEventHandler(textBoxGoalPoints_TextChanged);
StatedGoalPanel.buttonRecalcGoal.Click -= new RoutedEventHandler(buttonRecalcGoal_Click);
StatedGoalPanel.dateTimePickerGoalDate.SelectedDateChanged -= new EventHandler<SelectionChangedEventArgs>(dateTimePickerGoalDate_ValueChanged);
ChartConfigPanel.checkedLBCategories.CheckedChanged -= new Microsoft.Windows.Controls.CheckListBoxCheckedChangedEventHandler(checkedLBCategories_CheckedChanged);
ChartConfigPanel.comboChart.SelectionChanged -= new SelectionChangedEventHandler(comboChart_SelectionChanged);
ChartConfigPanel.comboTimePeriod.SelectionChanged -= new SelectionChangedEventHandler(comboTimePeriod_SelectionChanged);
ChartConfigPanel.checkBoxTrendLines.Checked -= new RoutedEventHandler(checkBoxTrendLines_Checked);
ChartConfigPanel.checkBoxTrendLines.Unchecked -= new RoutedEventHandler(checkBoxTrendLines_Unchecked);
}
}
//--------------------------------------------------------------------------------
/// <summary>
/// Initializes the child controls in the required control panels.
/// </summary>
private void InitControls()
{
StatedGoalPanel.dateTimePickerGoalDate.SelectedDate = DateTime.Now.Date;
StatedGoalPanel.dateTimePickerGoalDate.SelectedDateFormat = DatePickerFormat.Short;
ChartConfigPanel.datePickerFrom.SelectedDate = DateTime.Now.Date;
ChartConfigPanel.datePickerFrom.SelectedDateFormat = DatePickerFormat.Short;
ChartConfigPanel.datePickerTo.SelectedDate = DateTime.Now.Date;
ChartConfigPanel.datePickerTo.SelectedDateFormat = DatePickerFormat.Short;
ConfigPanel.textBoxUserID.Text = Globals.UserID.ToString();
}
#endregion "Other methods (initialization)"
#region "Other methods (calculation and display)"
//--------------------------------------------------------------------------------
/// <summary>
/// Renders the desired chart with the selected criteria.
/// </summary>
private void RenderChart()
{
// get the selected display categories
DisplayCategories categories = new DisplayCategories();
foreach (CLBCategoryItem item in WpfGlobals.CLBCategories)
{
if (item.IsChecked)
{
categories.Add(item.EnumValue);
}
}
string chartName = ((ComboBoxItem)(this.ChartConfigPanel.comboChart.SelectedItem)).Content.ToString();
string timePeriod = ((ComboBoxItem)(this.ChartConfigPanel.comboTimePeriod.SelectedItem)).Content.ToString();
DateTime from = (timePeriod == "Specified Period") ? (DateTime)this.ChartConfigPanel.datePickerFrom.SelectedDate : new DateTime(0);
DateTime to = (timePeriod == "Specified Period") ? (DateTime)this.ChartConfigPanel.datePickerTo.SelectedDate : DateTime.Now;
this.ChartPanel.ShowChart(chartName, timePeriod, from.Date, to.Date, categories, (bool)(this.ChartConfigPanel.checkBoxTrendLines.IsChecked));
}
//--------------------------------------------------------------------------------
/// <summary>
/// Recalclates the data displayed in the listviews in the current and earned
/// period points panels.
/// </summary>
private void CalculateTopSection()
{
// recalculate the current points and daily averages for each category
WpfGlobals.CurrentPoints.Clear();
WpfGlobals.EarnedPoints.Clear();
foreach (RepCategory category in Enum.GetValues(typeof(RepCategory)))
{
if (category != RepCategory.Unknown)
{
WpfGlobals.CurrentPoints.Add(new CurrentPointsItem()
{
CategoryName = category.ToString(),
PointsAvg = string.Format("{0:#,#}", WpfGlobals.Scraper.Reputations.GetDailyAverage(category)),
PointsValue = string.Format("{0:#,#}", WpfGlobals.Scraper.Reputations.GetLatestPointValue(category))
});
}
}
// Calcuate the points for the current week, month, and year, and project how
// many points the user will have at the end of those periods
foreach (RepPeriod period in Enum.GetValues(typeof(RepPeriod)))
{
int value;
int projected;
WpfGlobals.Scraper.Reputations.GetCurrentPeriodPoints(period, RepCategory.Total, out value, out projected);
WpfGlobals.EarnedPoints.Add(new EarnedPointsItem()
{
PeriodName = period.ToString(),
PointsProjected = string.Format("{0:#,#}", projected),
PointsValue = string.Format("{0:#,#}", value)
});
}
#if __SCRAPING_ALLOWED__
ScrapeLeader scraper = new ScrapeLeader();
scraper.ScrapeComplete += new ScraperEventHandler(leaderScraper_ScrapeComplete);
scraper.ScrapeFail += new ScraperEventHandler(leaderScraper_ScrapeFail);
scraper.ScrapeWebPage();
#else
UpdateLeaderInfo(null, false);
#endif
}
//--------------------------------------------------------------------------------
/// <summary>
/// Calculates the date at which the user will overtake the user who is
/// currently in first place in the points.
/// </summary>
/// <param name="sender"></param>
/// <param name="success"></param>
void UpdateLeaderInfo(ScrapeLeader sender, bool success)
{
// get our existing data
string savedName = RepSettings.Default.LeaderName;
int savedPoints = RepSettings.Default.LeaderPoints;
DateTime savedDate = RepSettings.Default.LeaderDate;
// If the name is empty or not the same as what was already there, assume
// we need ro save everything
bool virgin = (string.IsNullOrEmpty(savedName) || savedName != sender.LeaderName);
if (success)
{
if (Globals.UserID == (Convert.ToInt32(sender.LeaderID)))
{
this.LeaderPanel.labelOvertake.Text = "YOU'RE THE CURRENT POINTS LEADER!!!";
}
else
{
if (virgin)
{
// save our current date and scraped points for use in the calcs
savedDate = DateTime.Now.Date;
savedPoints = sender.LeaderPoints;
// and save everything in the config file
RepSettings.Default.LeaderName = sender.LeaderName;
RepSettings.Default.LeaderDate = savedDate;
RepSettings.Default.LeaderPoints = savedPoints;
RepSettings.Default.Save();
}
// math
bool evenAvg = false;
int overtakeDays = 0;
{
// get user's current points
int yourPoints = WpfGlobals.Scraper.Reputations.GetLatestPointValue(RepCategory.Total);
// determine the difference between the users points and the leaders points. This is
// the number of points the user has to make up.
int pointsDelta = sender.LeaderPoints - yourPoints;
TimeSpan span = DateTime.Now - savedDate;
int leaderAvgPts = 0;
int yourAvgPts = 0;
if (span.TotalDays > 0)
{
leaderAvgPts = (int)(Math.Ceiling((sender.LeaderPoints - savedPoints) / span.TotalDays));
yourAvgPts = WpfGlobals.Scraper.Reputations.GetDailyAverage(RepCategory.Total);
evenAvg = (yourAvgPts == leaderAvgPts);
overtakeDays = (int)(Math.Ceiling((double)pointsDelta / (yourAvgPts - leaderAvgPts)));
}
}
if (overtakeDays > 0)
{
DateTime overtakeDate = DateTime.Now.AddDays(overtakeDays);
this.LeaderPanel.labelOvertake.Text = string.Format("You will overtake {0} ({1:#,##0} pts) on {2}", sender.LeaderName, sender.LeaderPoints, overtakeDate.ToString("dd MMM yyyy"));
}
else
{
if (evenAvg)
{
this.LeaderPanel.labelOvertake.Text = string.Format("Forget it. {0} ({1:#,##0} pts) is matching your pace. You'll never catch him if you don't participate more.", sender.LeaderName, sender.LeaderPoints);
}
else
{
this.LeaderPanel.labelOvertake.Text = string.Format("Forget it. {0} ({1:#,##0} pts) is pulling away from you. You'll never catch him at the rate you're going.", sender.LeaderName, sender.LeaderPoints);
}
}
}
} // if (success)
else
{
this.LeaderPanel.labelOvertake.Text = "Leader info scrape failed. No data.";
}
}
#endregion "Other methods (calculation and display)"
}
}