I'm writing a background service which pulls historical data about products while my web server is running. I can only make requests at a specific rate, so for that reason, I need to create an executionQueue:
Creating this list of tasks is verbose and I don't see why it should be like this.
Maybe I'm just over complicating things:
public List<Task<Task<List<Candle>>>> BuildHistoricalDataTaskList(CryptoInfoContext context, string productId, DateTime totalStartDate, DateTime totalEndDate, Granularity[] granularities, int maxResults = 300)
{
var product = this.GetProductFromId(context, productId);
var executionQueue = new List<Task<Task<List<Candle>>>>();
foreach (var granularity in granularities)
{
var granularityMinutes = (int)granularity;
var startDate = totalStartDate;
var maxRangeMinutes = maxResults * granularityMinutes;
while (startDate <= totalEndDate)
{
var endDate = startDate.AddMinutes(maxRangeMinutes);
var task = new Task<Task<List<Candle>>>(() =>
{
return ProcessHistoricalDataAsync(productId, startDate, endDate, granularity);
});
executionQueue.Add(task);
startDate = endDate;
}
}
return executionQueue;
}
My return type seems a little verbose, is there a way i'm able to squash these Tasks down?
Maybe an observable is the way to go but I'm not sure how.
It looks like the middle Task is just behaving as a late execution closure. So you could replace that with a Func<Task<List<Candle>>> and get the same effect. That doesn't help anything, but it reduces the confusion introduced by Task<Task<...>>.
I'm assuming outside of this is something that processes the queue, so could you slim it down to an IEnumerable<Func<Task<List<Candle>>>>?
eg.
public IEnumerable<Func<Task<List<Candle>>>> BuildHistoricalDataTaskList(CryptoInfoContext context, string productId, DateTime totalStartDate, DateTime totalEndDate, Granularity[] granularities, int maxResults = 300)
{
var product = this.GetProductFromId(context, productId);
foreach (var granularity in granularities)
{
var granularityMinutes = (int)granularity;
var startDate = totalStartDate;
var maxRangeMinutes = maxResults * granularityMinutes;
while (startDate <= totalEndDate)
{
var endDate = startDate.AddMinutes(maxRangeMinutes);
yield return () =>
{
return ProcessHistoricalDataAsync(productId, startDate, endDate, granularity);
};
startDate = endDate;
}
}
}
it's not much better from a signature perspective. But it's slightly smaller.. Also you're not using product at all, that's likely costing you something.
Related
I have a datasource that returns dates and I have to find where the months falls within the month and day range buckets. The months and day range buckets are predefined so I put it in a Dictionary (not sure if that is even a good idea). I am using linq to find the min and Max dates and extracting the month from them. I need to find month from the dictionary where that month extracted falls within the range. For Example
Dictionary<int, int> MonthDayBuckets = new Dictionary<int, int>() { { 3,31 }, { 6,30 }, { 9,30 }, { 12,31 } };
var MinyDate = _dataSource.Min(x => x.values[0]);
var MaxDate = _dataSource.Max(x => x.values[0]);
var startMonth = Convert.ToDateTime(MinyDate).ToString("MM");
var endMonth = Convert.ToDateTime(MaxDate).ToString("MM");
Say startmonth return Jan so I want to be able to go to the dictionary and return only march (03.31) and if I get 10 for the Max (October) I am trying to return (12,31) December
If my understanding is correct, your MonthDayBuckets variable is meant to represent date ranges:
3/31 - 6/30
6/30 - 9/30
9/30 - 12/31
12/31 - 3/31
...and given a month, you're wanting to see what the end date is of the interval that the first of that month falls between? Like you gave the example of October returning 12/31.
This problem can be simplified since you'll get the same result saying "what's the next occurring date after this given date?" The next occurring date for 10/01 would be 12/31. So here's how you could rearrange your data:
var availableDates = new List<string> { "03/31", "06/30", "09/30", "12/31" };
Now you'll be able to find a match by finding the index of the first one that's greater than your given date. Note how I made the month/day combos lexicographical orderable.
var startMonth = Convert.ToDateTime(MinyDate).ToString("MM");
var startDate = startMonth + "/01";
var endMonth = Convert.ToDateTime(MaxDate).ToString("MM");
var endDate = endMonth + "/01";
// Wrap around to the first date if this falls after the end
var nextStartDate = availableDates.FirstOrDefault(d => d.CompareTo(startDate) >= 1) ?? availableDates[0];
var nextEndDate = availableDates.FirstOrDefault(d => d.CompareTo(endDate) >= 1) ?? availableDates[0];
You could use Linq for the purpose. For example,
var nearestKey = MonthDayBuckets.Keys.Where(x => x >= endMonth.Month).Min();
var nearestDate = new DateTime(DateTime.Now.Year,nearestKey,MonthDayBuckets[nearestKey]); // or whatever the year it needs to be represent
Though above query would get you the result, I would suggest you define a structure to store the Range itself, rather than using Dictionary
For example,
public class Range
{
public MonthDate StartRange{get;set;}
public MonthDate EndRange{get;set;}
public Range(MonthDate startRange,MonthDate endRange)
{
StartRange = startRange;
EndRange = endRange;
}
}
public class MonthDate
{
public MonthDate(int month,int date)
{
Month = month;
Date = date;
}
public int Month{get;set;}
public int Date{get;set;}
//Depending on if your Ranges are inclusive or not,you need to decide how to compare
public static bool operator >=(MonthDate source, MonthDate comparer)
{
return source.Month>= comparer.Month && source.Date>=comparer.Date;
}
public static bool operator <=(MonthDate source, MonthDate comparer)
{
return source.Month<= comparer.Month && source.Date<=comparer.Date;
}
}
Now you could define ranges as
var dateRanges = new Range[]
{
new Range(new MonthDate(12,31),new MonthDate(3,31)),
new Range(new MonthDate(3,31),new MonthDate(6,30)),
new Range(new MonthDate(6,30),new MonthDate(12,31)),
};
var result = dateRanges.First(x=>x.StartRange <= new MonthDate(endMonth.Month,endMonth.Day) && x.EndRange >= new MonthDate(endMonth.Month,endMonth.Day));
DateTime currentDate = DateTime.Now;
var query = (from m in db.Users where m.EmailAddress == emailAddress select m.Last_Login_Date).Single();
DateTime lastLoginDate = Convert.ToDateTime(query);
double diffDays = (currentDate - lastLoginDate).TotalDays;
if (diffDays < 72) {
Security.LoginUser(u.Name, u.Role, rememberMe);
u.Last_Login_Date = currentDate;
} else {
Response.Redirect("ResetPwd.aspx");
}
I tried to perform the calculation to find the difference in total days. So if the difference is >72 days, they will be forced to do the Resetting password. But now the formula can't be performed. I think that my code have some errors. so currentDate and the db.Users Last_Login_Date are in DateTime type. Anyone has any ideas how to do the calculation, or should I change my UserDB Last_Login_Date to string type?
Please try following:
//DateTime.Subtract() method will return a 'TimeSpan' value.
var timeSpan = currentDate.Subtract(lastLoginDate);
var diffDays = timeSpan.Days;
if(diffDays < 72)
{
//Code here.
}
Working solution is in the below link:
dotnetfiddle
So, I found a solution to my question. I changed to TimeSpan and it all worked now.Thanks for giving contribution.
DateTime currentDate = DateTime.Now;
var query = (from m in db.Users
where m.EmailAddress == emailAddress
select m.Last_Login_Date).Single();
DateTime lastLoginDate = Convert.ToDateTime(query);
TimeSpan diffDays = currentDate.Subtract(lastLoginDate);
if (diffDays.TotalDays> 72)
{
Response.Redirect("ResetPwd.aspx");
}
else
{
Security.LoginUser(u.Name, u.Role, rememberMe);
u.Last_Login_Date = currentDate;
}
My function is :
public async Task<IHttpActionResult> RecentAddedTags(int daysago)
{
TimeSpan duration = DateTime.UtcNow - DateTime.Today.AddDays(-daysago);
DateTime days = DateTime.UtcNow - duration;
var ret = from tag in db.Tags
where tag.time.Equals(days)
select new
{
postedById = tag.AspNetUser.Id,
postedByName = tag.AspNetUser.UserName,
name = tag.name,
info = tag.info,
time = tag.time,
id = tag.Id,
};
return Ok(ret);
}
If I call the function as RecentAddedTags(2) it should return all tags created 2 days ago. But it gives the error:
Unable to create a constant value of type 'System.Object'. Only
primitive types or enumeration types are supported in this context.
There is some issue with days object. What I am doing wrong?
Compare Date in DateTime Object it will work for you
var ret = from tag in db.Tags.ToList()
where tag.time.Date == days.Date
select new
{
postedById = tag.AspNetUser.Id,
postedByName = tag.AspNetUser.UserName,
name = tag.name,
info = tag.info,
time = tag.time,
id = tag.Id,
};
You need to change
tag.time.Equals(days)
to
tag.time == days;
(not entirely sure why it works)
So I have a set of data from an API call that I need to use. I filter to correct subset and access a specific field with the code below. Is there a better way of getting currentDate and beforeCurrentDate?
DateTime beforeCurrentDate, currDate;
var curr = from c in GlobalResults<FinancialYear>.Results
where c.IsCurrentYear = true
select c;
var prev = from c in GlobalResults<FinancialYear>.Results
where c.ID < curr.FirstOrDefault().ID && c.YearEnd == curr.FirstOrDefault ().YearStart.AddDays(-1)
select c;
foreach (var cfy in curr)
{
currDate = cfy.YearEnd;
}
foreach (var pfy in prev)
{
beforeCurrentDate = pfy.YearStart.AddDays (-1);
}
I know the foreach is the wrong way, so what should I use?
EDIT: What the API results contain is a set of dates, with one having the IsCurrent field set to true. I want the EndDate field of the IsCurrent = true result, and the StartDate field of the previous result. Previous ito of the StartDate to EndDate period. The ID field is no use since a previous date range could be captured after the current date range.
var curr = GlobalResults<FinancialYear>.Results.FirstOrDefault(c => c.IsCurrentYear);
var prev = GlobalResults<FinancialYear>.Results.FirstOrDefault(c => c.ID < curr.ID && c.YearEnd == curr.YearStart.AddDays(-1));
var currDate = curr.YearEnd;
var beforeCurrentDate = prev.YearStart.AddDays(-1);
You can get the current Date for the financial year by constraining the year and choosing the Last(), which replaces your foreach loop:
var currYear = GlobalResults<FinancialYear>.Results.Where(p=>p.IsCurrentYear == true).Last();
var currDate = currYear.YearEnd;
Similarly, you can use the currYear to get the previous information:
var prevYear = GlobalResults<FinancialYear>.Results.Where(p=>p.YearEnd == currDate.AddDays(-1)).FirstOrDefault();
var beforeCurrentDate = prevYear.YearStart.AddDays(-1);
Overlap exists between DateRange1 and DateRange2
DateRange1: |-----------|
DateRange2: |-------|
Overlap does not exist between DateRange3 and DateRange4
DateRange3: |------|
DateRange4: |-------|
I have a query that checks if there is an overlap, but it's a little hairy. It involves checking each DateRange border and uses a lot of <'s and >'s.
I was wondering if you could say like, if DateRange1.Union(DateRange2) != null, do stuff.
Just something to make my code more readable and less hairy.
Thanks!
If you can add a IncludedDates property (IEnumerable<DateTime>), it should be pretty easy.
DateRange1.IncludedDates.Intersect(DateRange2.IncludedDates).Any()
I'd create an extension method:
public static bool OverlappedTo(this DateRange range1, DateRange range2)
{
return range1.OverlappedTo(range2.Start, range2.End);
}
// Just an example, implement as you prefer...
public bool OverlappedTo(this DateRange range1, DateRange range2)
{
var duration1 = range1.End - range1.Start;
var duration2 = range2.End - range2.Start;
var minStart = range1.Start < range2.Start ? range1.Start : range2.Start;
var maxEnd = range1.End > range2.End ? range1.End : range2.End;
var totalDuration = maxEnd - minStart;
// if the sum of the 2 durations is less than
// the total duration --> overlapped
return duration1 + duration2 < totalDuration;
}
Usage:
bool overlapped = range1.OverlappedTo(range2);
P.S.
My DateRange class resemble to this:
(perhaps you have another implementation)
public class DateRange
{
public DateTime Start { get; private set; }
public DateTime End { get; private set; }
public DateRange(DateTime start, DateTime end)
{
this.Start = start;
this.End = end;
}
}
EDIT:
(according to the comments)
var outSideRange = new DateRange(start, end);
var filteredRows =
dt.AsEnumerable()
.Where(x => outSideRange.OverlappedTo(x.Field<DateTime>("StartCol"),
x.Field<DateTime>("EndCol")));