Chart does not load in first run - c#

I create a chart based on data of a GridView, so if a user select a row in my grid view I execute the chart create as:
private void dgvUserActivity_CellClick(object sender, DataGridViewCellEventArgs e)
{
var dg = (DataGridView)sender;
if (e.RowIndex == -1) return;
var selectedrowindex = dg.SelectedCells[0].RowIndex;
var selectedRow = dg.Rows[selectedrowindex];
var selectedUserName = selectedRow.Cells["UserName"].Value.ToString();
UserActivityAuditModel = UserActivityModel.UserActivityAuditList.Where(x => x.UserName == selectedUserName).ToList();
ClearChartPoints();
userChart.Titles.Clear();
LoadChart(UserActivityAuditModel);
}
Then I load chart as:
private void LoadChart(IList<UserActivityAuditViewModel> model)
{
//Filter info
var selectedTime = new List<TimeSpan>();
if (rdoLogOn.Checked)
{
selectedTime = model.Select(x => x.AverageLogOn).ToList();
}
else if (rdoLogOff.Checked)
{
selectedTime = model.Select(x => x.AverageLogOff).ToList();
}
else
{
selectedTime = model.Select(x => x.AverageTotalHours).ToList();
}
Axis XA = userChart.ChartAreas[0].AxisX;
Axis YA = userChart.ChartAreas[0].AxisY;
Series S1 = userChart.Series[0];
S1.ChartType = SeriesChartType.Line;
//Add or change Title
var title = new Title();
title.Font = new Font("Arial", 14, FontStyle.Bold);
title.Text = Helpers.FirstCharToUpper(model.Select(x => x.UserName).FirstOrDefault());
userChart.Titles.Add(title);
var dates = model.Select(x => x.ActivityDate).ToList();
var currentRegister = 0;
foreach (DateTime d in dates)
{
var yValue = selectedTime[currentRegister].ToString();
S1.Points.AddXY(d, yValue);
currentRegister++;
}
var dt = DateTime.Now;
S1.LegendText = "Year " + dt.Year;
// move to the bottom center:
userChart.Legends[0].Docking = Docking.Bottom;
userChart.Legends[0].Alignment = StringAlignment.Center;
if (!rdoTotalHours.Checked)
{
S1.YValueType = ChartValueType.Time;
XA.LabelStyle.Format = "mm:ss";
}
else
{
}
S1.XValueType = ChartValueType.Date;
XA.MajorGrid.Enabled = false;
XA.LabelStyle.Format = "MMM";
XA.IntervalType = DateTimeIntervalType.Months;
XA.Interval = 1;
YA.IsInterlaced = true;
//YA.MajorGrid.Enabled = false;
YA.InterlacedColor = Color.FromArgb(31, Color.LightSeaGreen);
}
For some reason in my first clic of DataGridView it executes the chart create,I debug it and the problem is in this foreach clause :
foreach (DateTime d in dates)
{
var yValue = selectedTime[currentRegister].ToString();
S1.Points.AddXY(d, yValue);
currentRegister++;
}
The value is there, after S1.Points.AddXY(d, yValue); is executed, I debug Points Y value is always 0 but the yValue I set has the correct number!. This is really weird.
Pictures:
As you can see value is there, but when I press F10 to continue debugging:
It added as 0 instead my value
Note: As I say before, this is only first time load, if I clic again in any row, it load correctly, someone have an idea of what is happening there? Regards
----EDIT----
I get the yValue from my global model:
public IList<UserActivityAuditViewModel> UserActivityAuditModel { get; set; } = new List<UserActivityAuditViewModel>();
Then I assign it depending of some radio buttons on the beginning of chart method:
var selectedTime = new List<TimeSpan>();
if (rdoLogOn.Checked)
{
selectedTime = model.Select(x => x.AverageLogOn).ToList();
}
else if (rdoLogOff.Checked)
{
selectedTime = model.Select(x => x.AverageLogOff).ToList();
}
else
{
selectedTime = model.Select(x => x.AverageTotalHours).ToList();
}
and I use that TimeSpan list into yValue
I create a simple test, instead load my Y value with TimeSpan I create an int list as:
var testList = new List<int>();
testList.Add(1);
testList.Add(2);
testList.Add(3);
testList.Add(4);
testList.Add(5);
testList.Add(6);
testList.Add(7);
testList.Add(8);
testList.Add(9);
testList.Add(10);
testList.Add(11);
testList.Add(12);
testList.Add(13);
Then I use in foreach as
foreach (DateTime d in dates)
{
var yValue = testList[currentRegister].ToString();
S1.Points.AddXY(d, yValue);
currentRegister++;
}
And now chart load in first load, but I can not understand why is not working with TimeSpan in first load, can someone have an idea of what is happening and a solution for this?

I found the issue
Charts does not support TimeSpan, so for some reason at the first render of chart it conflicts with Chart data. So the simplest solution I found is to convert TimeSpan to DateTime, at the end of the day we use YValueType as Time so it will take the time of the DateTime and display instead Date:
foreach (DateTime d in dates)
{
var datetime = new DateTime(0).AddSeconds(selectedTime[currentRegister].TotalSeconds);
S1.Points.AddXY(d, datetime);
currentRegister++;
}

Related

How to get total annual sales

In my ASP.NET Core 6 Web API, I have used the code below to calculate Total Annual Sales (from Transactions):
private async Task<decimal> GetAllTotalMonthlyTransactions()
{
string transactionMonth = DateTime.Now.ToString("MM");
decimal totalMonthlyTransactions = 0;
var sales = await _dbContext.Sales.ToListAsync();
foreach (var item in sales)
{
var salesDate = item.CreatedAt.ToString();
var salesMonth = salesDate.Substring(3, 2);
if (transactionMonth == salesMonth)
{
totalMonthlyTransactions += item.Amount;
}
}
return totalMonthlyTransactions;
}
How to I re-write the code above to get TotalAnnualSales (Transactions)?
private async Task<decimal> GetAllTotalMonthlyTransactions()
{
int currentYear = DateTime.Now.Year;
return (await dbContext.Sales.Where(t=>t.CreatedAt.Year==currentYear).ToListAsync()).Sum(item=>item.Amount);
}
private async Task<decimal> GetAllTotalMonthlyTransactions()
{
decimal totalAnnualTransactions = 0;
int _year = DateTime.Now.Year;
var sales = await dbContext.Sales.Where(y=>y.CreatedAt.Year==_year).ToListAsync();
// The first method
foreach (var item in sales)
{
totalAnnualTransactions += item.Amount;
}
// The second method
//double totalAnnualTransactions= sales.Sum(item => item.Amount);
return totalAnnualTransactions;
}
Instead of converting the datetimes to strings, try accessing them as ints. You can do this with .Month or .Year respectively
You can try to calculate from and to date range based on a specified month, then query/filter sales data with these from and to date range, like below.
var date1 = DateTime.Now;
var days = DateTime.DaysInMonth(date1.Year, date1.Month);
var from = new DateTime(date1.Year, date1.Month, 1, 0, 0, 0);
var to = new DateTime(date1.Year, date1.Month, days, 23, 59, 59);
var totalMonthlyTransactions = sales.AsEnumerable().Where(s => s.CreatedAt >= from && s.CreatedAt <= to).Select(s => s.Amount).Sum();
I hope following code will work for you...
private async Task<IEnumerable<object>> GetMonthlyTransactions(int year)
{
var starting = new DateTime(year, 1, 1).Date;
var sales = await context.Sales.Where(f => f.SalesDate >= starting).Select(s => new
{
Date = s.SalesDate.ToString("MM-yyyy"),
Amount = s.NetAmount
}).ToListAsync();
var finaldata = sales.GroupBy(s => s.Date, (d, a) => new { Date = d, Am = a }).Select(l => new
{
Date = l.Date,
Amount = l.Am.Sum(h => h.Amount)
});
return finaldata;
}

how to calculate sum of times in a column using foreach loop ASP.NET MVC

enter image description here
suppose A is my Table and inside this table i have one column like times
inside column name times i have n number of times
Example :-1st:-02:30
2nd:-03:25
3rd:-00:45
i want output like TotalTime=06:40
i got out put using jquery but i want how to do inside a controller using foreach loop please help me
my controller code:-
[HttpPost]
public ActionResult getTimeSheetByBasit(DateTime? CurrentDate, string ActivityTime)
//obj.UserDocumentList = ThidDb.UserDocument.Where(x => x.CreatedBy == UserId).ToList();
{
VM_TimeSheet ObjVM_TimeSheet = new VM_TimeSheet();
int LoggedUser = User.KEY();
string LoggedUserName = User.UserName();
string UserEmail = User.EmailID();
DateTime TimeIn, TimeOut;
string TimeInn, TimeOuut, TotalTime;
//code add here fot adding text box time with total houres enter by user select time Sheet
using (SecurenetDB SecurenetDB = new SecurenetDB())
{
ObjVM_TimeSheet.TimesheetList = SecurenetDB.AD_TimeSheet.Where(x => DbFunctions.TruncateTime(x.ActivityDate.Value) == DbFunctions.TruncateTime(CurrentDate) && x.UserKEY == LoggedUser).ToList();
TimeIn = SecurenetDB.AD_CardPunching.Where(x => DbFunctions.TruncateTime(x.EventDate) == DbFunctions.TruncateTime(CurrentDate) && x.UserName == LoggedUserName).Select(x => x.Time_In).FirstOrDefault();
TimeOut = SecurenetDB.AD_CardPunching.Where(x => DbFunctions.TruncateTime(x.EventDate) == DbFunctions.TruncateTime(CurrentDate) && x.UserName == LoggedUserName).Select(x => x.Time_Out).FirstOrDefault();
TimeInn = TimeIn.ToString("hh:mm tt");
TimeOuut = TimeOut.ToString("hh:mm tt");
TotalTime = SecurenetDB.AD_CardPunching.Where(x => DbFunctions.TruncateTime(x.EventDate) == DbFunctions.TruncateTime(CurrentDate) && x.UserName == LoggedUserName).Select(x => x.TotalHours).FirstOrDefault();
// ObjVM_TimeSheet.TimesheetList=SecurenetDB.AD_TimeSheet.Where(x=>x.Hours== TextTime && x.UserKEY == LoggedUser).ToList();
var sum = "00:00";
foreach(var iteam in ActivityTime)
{
sum = sum + iteam;
}
}
return Json(new
{
TimeSheetData = this.RenderPartialViewToString("TimeSheetData", ObjVM_TimeSheet.TimesheetList),
TimeIn = TimeInn,
TimeOut = TimeOuut,
TotalTime = TotalTime
}, JsonRequestBehavior.AllowGet);
}
enter image description here
Use TimeSpan
string[] times = new string[] {"02:30", "03:25", "00:45"};
TimeSpan totalTime = new TimeSpan(0);
foreach (string time in times)
{
TimeSpan ts = TimeSpan.Parse(time);
totalTime += ts;
}
Console.WriteLine(totalTime.ToString(#"hh\:mm"));
Output
06:40

Match data from two list<T> where date from one list falls between dates from second list

I have a list of objects (Pulled from SQL DB) with a TransactionDate for each object “alarmHistoryList”.
I have another list of objects (Pulled from SQL DB) and each object has a StartDate a FinishDate and an ID “RunLogList”.
There will be a Many to One relationship where “List1” will be the many and “RunLogList” the one. Each Run may have many Alarms.
I want every object in “alarmHistoryList” returned with the ID of the object in “RunLogList” where the TransactionDate fall between the StartDate and the FinishDate.
private void MatchRunData()
{
foreach (var alarm in _alarmHistoryList)
{
var AlarmTransTime = alarm.TransactionTime;
var filteredData = _FARunLogList.Where(t =>
t.TrackInTime > AlarmTransTime && t.TrackOutTime < AlarmTransTime);
}
}
Run logs with alarms matching the run log time window:
var runLogAlarms = new Dictionary<RunLog, IList<Alarm>>();
foreach (var alarm in _alarmHistoryList)
{
var alarmTransTime = alarm.TransactionTime;
var filteredData = _FARunLogList
.Where(t => t.TrackInTime > alarmTransTime && t.TrackOutTime < alarmTransTime)
.ToList();
foreach (var runLog in filteredData)
{
if (runLogAlarms.TryGetValue(runLog, out var alarmsValue))
{
alarmsValue.Add(alarm);
}
else
{
runLogAlarms[runLog] = new List<Alarm> { alarm };
}
}
}
I came up with an answer before Prolog that works. I am sure the answer Prolog gave works as well and is cleaner but I am posting my answer since it is the one I will be using.
private void MatchRunData()
{
foreach (var alarm in _alarmHistoryList)
{
var AlarmTransTime = alarm.TransactionTime;
foreach (var run in _FARunLogList)
{
var TrackInTime = run.TrackInTime;
var TrackOutTime = run.TrackOutTime;
var ID = run.LogId;
if (AlarmTransTime > TrackInTime && AlarmTransTime < TrackOutTime)
{
_MergedalarmHistoryList.Add
(new AlarmHistoryDefinition()
{ AlarmDesc = alarm.AlarmDesc, AlarmID = alarm.AlarmID, ToolID = alarm.ToolID,
TransactionTime = alarm.TransactionTime, GlobalToolID = alarm.GlobalToolID,
RUnLogID = run.LogId });
}
}
_MergedalarmHistoryList.Add(new AlarmHistoryDefinition()
{ AlarmDesc = alarm.AlarmDesc, AlarmID = alarm.AlarmID, ToolID = alarm.ToolID,
TransactionTime = alarm.TransactionTime, GlobalToolID = alarm.GlobalToolID,
RUnLogID = 00000 });
}
}
Can try this
private void MatchRunData()
{
foreach (var alarm in _alarmHistoryList)
{
var filteredData = _FARunLogList.Where(t =>
t.TrackInTime > alarm.TransactionTime && t.TrackOutTime < alarm.TransactionTime);
alarm.RunLogListId = filteredData.RunLogListId;
}
}

How to remove a range from a list within a loop?

Greetings Please keep in mind there is no database and these are fake functions to make the component work for testing, i have a List which makes a 24 hours based on 15 minutes scale and produces from this method:
public List<ScaleLine> GetHoursAndScales(ScaleLine.Scales scale = ScaleLine.Scales.Fiftheen)
{
int _scale = Convert.ToInt32(scale);
int _count = _scale * 24;
int _scaleCount = 60 / _scale;
List<ScaleLine> _list = new List<ScaleLine>();
var start = DateTime.Today;
var clockQuery = from offset in Enumerable.Range(1, _count)
select TimeSpan.FromMinutes(_scaleCount * offset);
foreach (var time in clockQuery)
{
_list.Add(new ScaleLine() { Id = _list.Count, Hours = (start + time).ToString("HH:mm"), Scale = _scale });
}
return _list;
}
And i have another list which is called Reserved hours which is produces on this method:
public List<Reservedhours> AllReservedHours()
{
return new List<Reservedhours>
{
new Reservedhours() { Id = 1, Date = DateTime.Now, StartPoint = "08:00", EndPoint = "10:00" },
new Reservedhours() { Id = 2, Date = DateTime.Now, StartPoint = "14:00", EndPoint = "16:00" },
new Reservedhours() { Id = 3, Date = DateTime.Now, StartPoint = "20:00", EndPoint = "22:00" },
new Reservedhours() { Id = 4, Date = DateTime.Now.AddDays(1), StartPoint = "07:00", EndPoint = "11:00" },
new Reservedhours() { Id = 5, Date = DateTime.Now.AddDays(1), StartPoint = "13:00", EndPoint = "15:00" },
new Reservedhours() { Id = 6, Date = DateTime.Now.AddDays(1), StartPoint = "15:00", EndPoint = "18:00" },
new Reservedhours() { Id = 7, Date = DateTime.Now.AddDays(1), StartPoint = "18:00", EndPoint = "22:00" },
};
}
Now i have another list that produces the available hours based on reserved hours and first list that produces 24 hours:
public List<ScaleLine> GetAvailableHours(DateTime date, ScaleLine.Scales scale = ScaleLine.Scales.Fiftheen)
{
List<Reservedhours> _timeLine = AllReservedHours().Where(x => x.Date.Date == date.Date)
.Select( a => new Reservedhours {StartPoint = a.StartPoint, EndPoint = a.EndPoint } )
.ToList();
List<ScaleLine> _available = GetHoursAndScales();
//scale convert:
int _scale = Convert.ToInt32(scale);
foreach (var _item in _timeLine)
{
int index = _available.Where(x => x.Hours == _item.StartPoint)
.SingleOrDefault().Id;
//Convert to datetime
DateTime _opening = DateTime.ParseExact(_item.StartPoint, "HH:mm", System.Globalization.CultureInfo.InvariantCulture);
DateTime _closing = DateTime.ParseExact(_item.EndPoint, "HH:mm", System.Globalization.CultureInfo.InvariantCulture);
//Getting duration time
TimeSpan duration = _closing.Subtract(_opening);
double _duration = duration.TotalMinutes;
//getting coverage
int timeScale = 60 / _scale;
int coverage = Convert.ToInt32(_duration) / timeScale;
//remove unavailable timespots
_available.RemoveRange(index, coverage);
}
return _available;
}
But problem is when the Foreach loop starts it removes the first range correctly based on its index, lets say if _available list has 96 members it removes 8 of them, so second time it should have 88 members and find the index within those 88 members but it doesn't and gets the index wrong (it takes the index as if the list still had 96 members) and so goes for every other action within the loop. how can i fix this issue? is there a way i can get the available list without doing a foreach loop?
The problem is your determination of the index. Instead of asking the list for the index of the desired object, you ask for the property value of an object and using this as index:
int index = _available.Where(x => x.Hours == _item.StartPoint)
.SingleOrDefault()
.Id;
Either you ask really for the index by calling IndexOf():
var matchingItem = _available.Where(x => x.Hours == _item.StartPoint)
.First();
var index = _available.IndexOf(matchingItem);
Or you replace your .RemoveRange() by something else, that really removes your desired elements.

I'm can't access the key and value from a function that returns an IQueryable in winform that i took from a repository class

i'm new in programming and i'm working on a Windows form application. I made a repostory class for all my dataBase work. I made a function in the repository that returns an IQueryable- it returns a key value pair.(key=dayOfWeek and value=revenue). Then when i called this function in the Form, because i wanted the information to be printed on labels, i cannot access the key seperately and the value seperately. It only gives me the option of the the whole key value pair.
this is my code in the repository class:
public class ReportsRepository
{
FruitStoreDataContext db;
public IQueryable RevenuePerDayOfWeek(DateTime startDate, DateTime endDate)
{
db = new FruitStoreDataContext();
var sumPerday = from s in db.OrderDetails
where s.Order.OrderDate >=startDate && s.Order.OrderDate <=endDate
select new
{
day = s.Order.OrderDate.DayOfWeek,
revenue = s.Price * s.Quantity
};
var totalSumPerday = from f in sumPerday
group f.revenue by f.day into g
select new
{
Day= g.Key,
Sum = g.Sum()
};
return totalSumPerday;
}
private void Report1Form_Load(object sender, EventArgs e)
{
ReportsRepository report = new ReportsRepository();
var totalSumPerday = report.RevenuePerDayOfWeek(dateToStart, dateToEnd);
int[]numOfDays = new int[7];
for (DateTime day = dateToStart; day <= dateToEnd; day = day.AddDays(1))
{
dayOfWeek = Convert.ToInt32(day.DayOfWeek);
numOfDays[dayOfWeek]++;
}
Label label;
List<Label> labels = new List<Label>();
int t = 0;
foreach(var totalSum in totalSumPerday)
{
if (numOfDays[dayOfWeek] == 0)
numOfDays[dayOfWeek] = 1;
int y = (38 * t) + 60;
label = new Label();
label.Location = new Point(34, y);
label.Visible = true;
label.Size = new Size(450, 35);
label.BackColor = Color.Gray;
label.ForeColor = Color.White;
label.Font = new Font("Lucida Console", 16);
dayOfWeek = Convert.ToInt16(totalSum.Day.Key);
//on the line below the word 'Day' and 'Sum' are underlined red...it doesn't give me the option to do that. I can only access the whole thing together(key,value)
label.Text = totalSum.Day.ToString() + " : " + (totalSum.Sum / numOfDays[dayOfWeek]).ToString();
labels.Add(label);
panel1.Controls.Add(label);
t++;
}
First, You can do one query to achieve your result. Second, you don't need to return IQueryable as you want in-memory data only (with not further querying). So what you can do is:
public Dictionary<int, decimal> RevenuePerDayOfWeek(DateTime startDate, DateTime endDate)
{
db = new FruitStoreDataContext();
var sumPerday = (from s in db.OrderDetails
where s.Order.OrderDate >= startDate && s.Order.OrderDate <= endDate
group s by s.Order.OrderDate.DayOfWeek into grp
select new
{
Day = grp.Key,
Sum = grp.Sum(a => a.Price * a.Quantity)
}).ToDictionary(x => x.Day, x => x.Sum);
return sumPerday;
}
Usage in Report1Form_Load:
var totalSumPerday = report.RevenuePerDayOfWeek(dateToStart, dateToEnd);
foreach (var totalSum in totalSumPerday)
{
DayOfWeek dayOfWeek = totalSum.Key;
decimal sum = totalSum.Value;
// continue your code
}

Categories

Resources