Ive been having this issue for a couple of days now and its something i cant seem to get past, If i post a one day event anytime during British Summer Time then the event when it renders in FullCalendar shows as a Two day event and puts the date back one hour and also moves the day (please see images below), the SQL is how it should be and showing the correct datetime but the calendar renders incorrectly.
I think that the issue is that the Json is coming out as ASP.net datetime and not ISO8601, I have tried all i can to change this but im new to .net and MVC so i just cant wrap my head around what to do.
I cant post a link to the picture of the event rendered as i dont have enough rep....
Pic of the data coming out in the object -
JsonObject
DB shot of the correct DateTime in SQL -
SQL DateTime
Heres how this setup is working for our MCV5 / FullCalendar project
Model -
public int Id { get; set; }
//Auto Populate
public string Subject { get; set; }
public string Description { get; set; }
public DateTime? StartTime { get; set; }
public DateTime? EndTime { get; set; }
public bool? AllDay { get; set; }
public bool? isAMLeave { get; set; }
public bool? isPMLeave { get; set; }
public string ETADType { get; set; }
public string ETADSubType { get; set; }
[ForeignKey("AssignedRole")]
public int SelectRole { get; set; }
public bool? AllRoles { get; set; }
public int StaffID { get; set; }
public int EventStatus { get; set; }
/*Approval Details*/
public int? ApporovedId { get; set; }
public DateTime? ApprovedDate { get; set; }
public DateTime EventDate { get; set; }
/*soft audit*/
public double Duration { get; set; }
public string EventAction { get; set; }
public virtual AssignedRoles AssignedRole { get; set; }
}
JsonResult -
public JsonResult GetDiaryEventsDivOne()
{
if (User.IsInRole("Global Admin"))
{
var date = new Date(jsonDate);
var _DivisionOneList = from e in db.DiaryEvent
join lt in db.EventStatus on e.EventStatus equals lt.Id
join t in db.ETAD_EventTypes on e.ETADType equals t.ETADID.ToString()
join st in db.ETAD_EventSubType on e.ETADSubType equals st.ETADSubID.ToString()
join sid in db.AssignedRoles on e.StaffID equals sid.UserID
join tid in db.Teams on sid.TeamId equals tid.TeamID
join div in db.Divisions on sid.DivisionId equals div.DivisionID
join sd in db.SubDivisions on sid.SubDivisionId equals sd.SubDivisionID
join sp in db.JobRoles on sid.JobRoleID equals sp.JobRoleID
where div.DivisionID == 1
select new
{
id = e.Id,
title = e.Subject,
description = e.Description,
start = e.StartTime,
end = e.EndTime,
etad = t.ETADDescription.ToString(),
etadsub = st.Description.ToString(),
color = t.Colours.HexCode,
user = sid.StaffProfiles.Fullname,
jobtitle = sp.JobTitle.ToString(),
allroles = e.AllRoles,
division = div.DivisionName,
subdivision = sd.SubDivisionName,
allDay = false
};
var rows = _DivisionOneList.ToArray();
return Json(rows, JsonRequestBehavior.AllowGet);
And finally heres my JS
$(document).ready(function () {
$('#calendar').fullCalendar({
header: {
left: 'prev,next today',
center: 'title',
right: 'month,agendaWeek,agendaDay'
},
eventSources: [
source1,
source2
],
timezone: 'Europe/London',
defaultView: 'month',
editable: false,
contentHeight: 700,
selectable: true,
eventClick: function (event, jsEvent, view) {
$("#startTime").html(moment(event.start).format('DD-MM-YYYY HH:mm'));
$("#endTime").html(moment(event.end).format('DD-MM-YYYY HH:mm'));
$('#modalTitle').html(event.title);
$('#user').text(event.user);
$('#modalBody').text(event.description);
$('#etad').html(event.etad);
$('#etadsub').html(event.etadsub);
$('#fullCalModal').modal('show');
$("#removeBtn").click(function () {
$.ajax({
type: 'POST',
url: "/BookingTwo/DeleteEvent",
data: {
"id": event.id
},
success: function () {
$('#calendar').fullCalendar('removeEvents', event.id);
$('#fullCalModal').modal('hide');
},
statusCode: {
202: function (reponose) {
$('#calendar').fullCalendar('refetchEvents');
$('#alert').addClass('alert alert-success').removeClass('alert-warning');
$("#alert").removeClass("hidden");
$("P").replaceWith("Success - Event Deleted")
$("#alert").fadeTo(5000, 500).slideUp(500, function () {
$('#alert').addClass("hidden");
})
},
409: function (reponse) {
$('#calendar').fullCalendar('refetchEvents');
$("#alert").removeClass("hidden");
$("P").replaceWith("Event already actioned.")
$("#alert").fadeTo(5000, 500).slideUp(500, function () {
$('#alert').addClass("hidden");
})
},
400: function (reponse) {
$('#calendar').fullCalendar('refetchEvents');
$("#alert").removeClass("hidden");
$("P").replaceWith("Access Denied - Not your leave request.")
$("#alert").fadeTo(5000, 500).slideUp(500, function () {
$('#alert').addClass("hidden");
})
}
}
});
});
},
Ive fixed the issue by setting
TimeZone; 'local'
I thought i had already set this in the paramaters but i had put Europe/London
For anyone having the same issue please look at your timezone settings in your JS for FullCalendar.
Related
I have a vue3 datagrid and I want to fill the data in this grid with filter by API. At the same time, I want to send the filter fields in the grid to the API as JSON and execute them according to this filter on the API side. How can I do this with AutoQuery?
[Route("/GetConnectors", "POST")]
public class GetConnectors : QueryDb<Connector>
{
public string Id { get; set; }
public string PageParameterJson { get; set; }
}
public class Connector
{
[PrimaryKey]
[AutoIncrement]
public long PKey { get; set; }
public string Id { get; set; }
public string Name { get; set; }
}
public class PageParameters
{
public string Field { get; set; }
public string Operand { get; set; }
public string Value { get; set; }
public string Type { get; set; }
}
It's an example PageParameter JSON;
[
{
"Field":"Name",
"Operand":"cn"//Contains
"Value":"test",
"Type":"string"
},
{
"Field":"Id",
"Operand":"eq"//Equal
"Value":"2",
"Type":"string"
}
]
public async Task<object> Any(GetConnectors query)
{
using var db = AutoQuery.GetDb(query, base.Request);
var filters = query.PageParameters.FromJson<List<PageParameter>>();
//How can I execute query with for CreateQuery?
var q = AutoQuery.CreateQuery(query, Request, db);
var sql = q.PointToSqlString();
return await AutoQuery.ExecuteAsync(query, q, base.Request, dbConnection);
}
Best Regards
i can't execute dynamic filters from server-side datagrid
The AutoQuery.CreateQuery returns OrmLite's Typed SqlExpression which has a number of filtering options inc .Where(), .And(), .Or(), etc.
So you should be able to populate it with something like:
foreach (var filter in filters)
{
var type = filter.Type switch
{
"string" => typeof(string),
_ => throw new NotSupportedException($"Type {filterType}")
};
var value = filter.Value.ConvertTo(type);
if (filter.Operand == "eq")
{
q.And(filter.Field + " = {0}", value)
}
else if (filter.Operand == "cn")
{
q.And(filter.Field + " LIKE {0}", $"%{value}%")
}
else throw new NotSupportedException(filter.Operand);
}
Note: I've rewritten API to be async as you should never block on async methods.
I have 3 entities:
[CompositeIndex(nameof(Url), nameof(TargetDomainRecordId), nameof(UserAuthCustomId), Unique = true)]
public class WatchedUrlRecord
{
[AutoIncrement]
public long Id { get; set; }
public string Url { get; set; }
public string Provider { get; set; }
public string DomainKey { get; set; }
public WatchedUrlScanStatus WatchedUrlScanStatus { get; set; }
public bool NoFollow { get; set; }
public HttpStatusCode HttpStatusCode { get; set; }
public DateTime? LastScanTime { get; set; }
public WatchedUrlScanResult LastScanData { get; set; }
public string Anchors { get; set; }
public int? OutboundLinks { get; set; }
[ForeignKey(typeof(TargetDomainRecord), OnDelete = "CASCADE")]
public long TargetDomainRecordId { get; set; }
[ForeignKey(typeof(UserAuthCustom), OnDelete = "CASCADE")]
public long UserAuthCustomId { get; set; }
}
[CompositeIndex(nameof(Url), nameof(TargetDomainRecordId), nameof(UserAuthCustomId), Unique = true)]
public class WatchedUrlQueue
{
[PrimaryKey]
public long WatchedUrlRecordId { get; set; }
[Index]
public string Url { get; set; }
[Index]
public string DomainKey { get; set; }
[Index]
public long TargetDomainRecordId { get; set; }
public string TargetDomainKey { get; set; }
[Index]
public DateTime CreateDate { get; set; } = DateTime.UtcNow;
public int Tries { get; set; }
[Index]
public DateTime? DeferUntil { get; set; }
[Index]
public long UserAuthCustomId { get; set; }
[Index]
public bool FirstScan { get; set; }
}
[CompositeIndex(nameof(Url), nameof(UserAuthCustomId), Unique = true)]
public class TargetDomainRecord
{
[AutoIncrement]
public long Id { get; set; }
public string Url { get; set; }
public string DomainKey { get; set; }
public DateTime CreateDate { get; set; } = DateTime.Now;
public DateTime? DeleteDate { get; set; }
public bool IsDeleted { get; set; }
public bool Active { get; set; } = true;
public DomainType DomainType { get; set; }
[ForeignKey(typeof(UserAuthCustom), OnDelete = "CASCADE")]
public long UserAuthCustomId { get; set; }
}
I am trying to insert queue objects based on IDs of WatchedUrlRecords so I came up with this query:
var q = db.From<WatchedUrlRecord>()
.Where(x => Sql.In(x.Id, ids))
.Join<TargetDomainRecord>((w, t) => w.TargetDomainRecordId == t.Id)
.Select<WatchedUrlRecord, TargetDomainRecord>((w, t) => new WatchedUrlQueue()
{
UserAuthCustomId = w.UserAuthCustomId,
DomainKey = w.DomainKey,
CreateDate = DateTime.UtcNow,
DeferUntil = null,
FirstScan = firstScan,
TargetDomainKey = t.DomainKey,
Tries = 0,
TargetDomainRecordId = w.TargetDomainRecordId,
Url = w.Url,
WatchedUrlRecordId = w.Id
});
var inserted = db.InsertIntoSelect<WatchedUrlQueue>(q, dbCmd => dbCmd.OnConflictIgnore());
This doesn't work and gives error:
variable 'w' of type 'Project.ServiceModel.WatchedUrl.Entities.WatchedUrlRecord' referenced from scope '', but it is not defined
If I try anonymous object like new {} instead of new WatchedUrlQueue then InsertIntoSelect() throws error:
'watched_url_record"."user_auth_custom_id' is not a property of 'WatchedUrlQueue'
I have looked in documentation and can see SelectMulti() method but I don't think that is suitable as it will involve me creating a tuple list to combine into the new object. The passed list can be quite large so I just want to send the correct SQL statement to PostgreSQL which would be along lines of:
insert into watched_url_queue (watched_url_record_id, url, domain_key, target_domain_record_id, target_domain_key, create_date, tries, defer_until, user_auth_custom_id)
select wur.id watched_url_record_id,
wur.url url,
wur.domain_key,
wur.target_domain_record_id,
tdr.domain_key,
'{DateTime.UtcNow:MM/dd/yyyy H:mm:ss zzz}' create_date,
0 tries,
null defer_until,
wur.user_auth_custom_id
from watched_url_record wur
join target_domain_record tdr on wur.target_domain_record_id = tdr.id
where wur.id in (323,3213123,312312,356456)
on conflict do nothing ;
I currently have a lot of similar type queries in my app and it is causing extra work maintaining them, would be really nice to be able to have them use fluent api without reducing performance. Is this possible?
Custom select expression can't be a typed projection (i.e. x => new MyType { ... }), i.e. you'd need to use an anonymous type expression (i.e. new { ... }) which captures your query's Custom SELECT Projection Expression.
You'll also need to put your JOIN expressions directly after FROM (as done in SQL) which tells OrmLite it needs to fully qualify subsequent column expressions like Id which would otherwise be ambiguous.
I've resolved an issue with field resolution of custom select expressions in this commit where your query should now work as expected:
var q = db.From<WatchedUrlRecord>()
.Join<TargetDomainRecord>((w, t) => w.TargetDomainRecordId == t.Id)
.Where(x => Sql.In(x.Id, ids))
.Select<WatchedUrlRecord, TargetDomainRecord>((w, t) => new {
UserAuthCustomId = w.UserAuthCustomId,
DomainKey = w.DomainKey,
CreateDate = DateTime.UtcNow,
DeferUntil = (DateTime?) null,
FirstScan = firstScan,
TargetDomainKey = t.DomainKey,
Tries = 0,
TargetDomainRecordId = w.TargetDomainRecordId,
Url = w.Url,
WatchedUrlRecordId = w.Id
});
var inserted = db.InsertIntoSelect<WatchedUrlQueue>(q, dbCmd=>dbCmd.OnConflictIgnore());
This change is available from v5.10.5 that's now available on MyGet.
So here's what I'm getting back from the oData service...
{
"odata.metadata":"http://server.ca/Mediasite/Api/v1/$metadata#UserProfiles",
"value":[
{
"odata.id":"http://server.ca/Mediasite/Api/v1/UserProfiles('111111111111111')",
"QuotaPolicy#odata.navigationLinkUrl":"http://server.ca/Mediasite/Api/v1/UserProfiles('111111111111111')/QuotaPolicy",
"#SetQuotaPolicyFromLevel":{
"target":"http://server.ca/Mediasite/Api/v1/UserProfiles('111111111111111')/SetQuotaPolicyFromLevel"
},
"Id":"111111111111111",
"UserName":"testuser",
"DisplayName":"testuser Large",
"Email":"testuser#testuser.ca",
"Activated":true,
"HomeFolderId":"312dcf4890df4b129e248a0c9a57869714",
"ModeratorEmail":"testuser#testuserlarge.ca",
"ModeratorEmailOptOut":false,
"DisablePresentationContentCompleteEmails":false,
"DisablePresentationContentFailedEmails":false,
"DisablePresentationChangeOwnerEmails":false,
"TimeZone":26,
"PresenterFirstName":null,
"PresenterMiddleName":null,
"PresenterLastName":null,
"PresenterEmail":null,
"PresenterPrefix":null,
"PresenterSuffix":null,
"PresenterAdditionalInfo":null,
"PresenterBio":null,
"TrustDirectoryEntry":null
}
]
}
I want to deserialize this into a simple class, like just the important stuff (Id, Username, etc...to the end).
I have my class create, but for the life of me I can't figureout how to throw away all the wrapper objects oData puts around this thing.
Can anyone shed some light?
You can use JsonObject do dynamically traverse the JSON, e.g:
var users = JsonObject.Parse(json).ArrayObjects("value")
.Map(x => new User
{
Id = x.Get<long>("Id"),
UserName = x["UserName"],
DisplayName = x["DisplayName"],
Email = x["Email"],
Activated = x.Get<bool>("Activated"),
});
users.PrintDump();
Or deserialize it into a model that matches the shape of the JSON, e.g:
public class ODataUser
{
public List<User> Value { get; set; }
}
public class User
{
public long Id { get; set; }
public string UserName { get; set; }
public string DisplayName { get; set; }
public string Email { get; set; }
public bool Activated { get; set; }
public string HomeFolderId { get; set; }
public string ModeratorEmail { get; set; }
public bool ModeratorEmailOptOut { get; set; }
public bool DisablePresentationContentCompleteEmails { get; set; }
public bool DisablePresentationContentFailedEmails { get; set; }
public bool DisablePresentationChangeOwnerEmails { get; set; }
public int TimeZone { get; set; }
}
var odata = json.FromJson<ODataUser>();
var user = odata.Value[0];
Given the following definitions from a ServiceStack endpoint:
public class LoanQueue
{
public int LoanId { get; set; }
public DateTime Submitted { get; set; }
public DateTime Funded { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int Fico { get; set; }
public int Fraud { get; set; }
public int CDS { get; set; }
public int IDA { get; set; }
public string Income { get; set; }
public string Liabilities { get; set; }
public string Agent { get; set; }
public string Status { get; set; }
public string State { get; set; }
public string Product { get; set; }
public string Comment { get; set; }
}
public enum DateType
{
None,
Submitted,
Funded
}
[Route("/loan/queue/search", "GET")]
public class LoanQueueQueryGet : QueryBase<LoanQueue>
{
public DateType DateType { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public string AgentUserName { get; set; }
public Languange Languange { get; set; }
public bool WorkingLoan { get; set; }
public bool MicrobusinessLoan { get; set; }
public LoanStatus LoanStatus { get; set; }
}
public object Get(LoanQueueQueryGet request)
{
if (request == null) throw new ArgumentNullException("request");
var profiler = Profiler.Current;
using (profiler.Step("LoanServices.LoanQueue"))
{
SqlExpression<LoanQueue> q = AutoQuery.CreateQuery(request, Request.GetRequestParams());
QueryResponse<LoanQueue> loanQueueResponse = AutoQuery.Execute(request, q);
return loanQueueResponse;
}
}
My question is this, "Is it even possible to run conditional logic based on the request object in the service impl"? e.g.
If DateType == DateType.Submitted
then query the Submitted property on the LoanQueue with a BETWEEN clause (StartDate/EndDate) or
If DateType == DateType.Funded
then query the Funded property on the LoanQueue with a BETWEEN clause (StartDate/EndDate).
My guess is that I'm trying to bend AutoQuery too far and would be better served just coding it up the old fashion way. I really like the baked-in features of the AutoQuery plugin and I'm sure there will be times when it will suit my needs.
Thank you,
Stephen
AutoQuery will ignore any unmatched fields so you're able to use them to extend your populated AutoQuery with additional custom logic, e.g:
public object Get(LoanQueueQueryGet request)
{
var q = AutoQuery.CreateQuery(request, Request.GetRequestParams());
if (request.DateType == DateType.Submitted)
{
q.And(x => x.Submitted >= request.StartDate && x.Submitted < request.EndDate);
}
else
{
q.And(x => x.Funded >= request.StartDate && x.Funded < request.EndDate);
}
var loanQueueResponse = AutoQuery.Execute(request, q);
return loanQueueResponse;
}
Here are three classes in my domain:
public class Quote : IEntity, IAggregateRoot {
public int QuoteId { get; set; }
public IEnumerable<Price> Prices { get; set; }
}
public class Price : IEntity {
public int PriceId { get; set; }
public Carrier Carrier { get; set; }
public decimal? Price { get; set; }
public Quote Quote { get; set; }
}
public class Carrier : IEntity, IAggregateRoot {
public int CarrierId { get; set; }
public string Name { get; set; }
}
I want to be able to select a projection based on the Prices in the Quote. The return type should be an IEnumerable<[anonymous object]>. I have to start the query from the Quote because it is the root domain object. Here is what I have so far:
session.Linq<Quote>()
.Expand("Prices")
.Where(q => q.QuoteId == 1)
.Select(q => {
//this is where I don't know what to do.
//Maybe somthing like this:
return q.Prices.Select(p => {
new { CustomerName = p.Customer.Name, Price = p.Price }
});
});
The mappings would be:
Quote.Prices > HasMany (one-to-many)
Price.Quote > References (many-to-one)
Price.Carrier > References (one-to-one)
I found my answer. I completely forgot about the SelectMany Linq expression. Here is my solution.
session.Linq<Quote>()
.Where(q => q.QuoteId == 1)
.SelectMany(q => q.Prices, (q, p) => new { CustomerName = p.Customer.Name });