In asp.net mvc2, how do I show the status of a user as "online"?
So if the user is actively working on the site, the status would be "online".
if the user stepped away from the site for about 5 minutes then it would be "5 minutes".
if the user stepped away from the site for about 10 minutes then it would be "10 minutes".
So on and so forth.
What is the best way to accomplish this? Any code sample would be very helpful to me.
The responses so far suggest that I used Ajax. If so, then how would I be able to query online users vs offline users. All my queries go against Database. Is it possible to query and display results joining Ajax results with Database queries?
I would think the most straight forward way of doing it would be with a session variable. You could add a line to your controllers (either in the action method, or the constructor, or even possibly with an action filter) which stashes the current Date/Time in the session. You could then use an ajax call to update the value on the screen at a specific interval. You would probably want to make the interval in minutes rather than seconds otherwise you would be displaying a counter (i.e. "1 second", "2 seconds", etc).
Some quick code samples:
// Somewhere in controller
Session["LastSeen"] = DateTime.Now;
// Now, an action that would return the amount of time since the user was last seen
public ViewResult GetLastSeenTime()
{
return Json(new { TimeAway = Date.Time.Now.Subtract((DateTime)Session["LastSeen"]).TotalMinutes});
}
// Then on your page, something like this
$.post("/Controller/GetLastSeenTime",null,function(result) {
if(result.LastSeen < 5)
$("#Status").Text("Online");
else if (result.LastSeen % 10 == 0)
$("#Status").Text(result.LastSeen + " minutes");
},"json");
Totally not tested, but should be close.
ckramer is right. I suggest expending his solution to make it js degradable.
/ Now, an action that would return the amount of time since the user was last seen
public ActionResult GetLastSeenTime()
{
if (Request.IsAjax) {
return Json(new { TimeAway = Date.Time.Now.Subtract((DateTime)Session["LastSeen"]).TotalMinutes});
}
ViewData.Add("LastSeen", Date.Time.Now.Subtract((DateTime)Session["LastSeen"]).TotalMinutes}));
return View("MyView")
}
Related
Can anyone help.
I have a simple google spreadsheet that enables me to book delegates onto a variety of courses at different venues and at different times and dates.
I used the same spreadsheet last year, kindly set up by Bob Rashkin! It worked perfectly and sent emails when delegates booked onto the course and also sent email reminders automatically when the course was 9 days or so from starting.
I have duplicated the spreadsheet for this term but the sheet refuses to send the reminder emails, I really could do with a little help on this?
A secondary matter is that when the script sends a booking confirmation email, it sometimes sends two of them which creates a lot of phone calls asking if they've booked on twice.
Help someone please
This is the reminder script that I'm using
function Reminder() {
var ss=SpreadsheetApp.getActiveSpreadsheet();
var s=ss.getActiveSheet();
var r1=s.getDataRange().getRow()+1;//Start past the header row!!! Doh!
var r2=s.getDataRange().getLastRow();
var mn,m,days,d=new Date(),coursedate=new Date(),dlen=8.64e7,i,course,r,year;//8.64e7
var subject="Just a gentle reminder that you or colleague(s) from your setting have a Paediatric First Aid course coming up in the next week or so. ";
var recipient, body, tail="Please be aware that Entrust (formerly Staffs Early Years) will make a charge for non attendance so";
tail+=" please make sure that you familiarise yourself with the times and dates of the course. ";
tail+="If you need help finding the venue then please follow the link below to find the venue and print off a map if required.";
tail+="\n\nhttp://www.blithfieldsafety.co.uk/venues/";
for (r=r1;r<=r2;r++) {
recipient=s.getRange(r,9).getValue();
course=s.getRange(r, 2).getValue();
body=subject+"\nCourse Details\n"+course+"\n\nDelegate Name: "+s.getRange(r,4).getValue()+"\n\n"+tail; //changed (r,2) to (r,4)
mn=course.match(/Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec/)[0];
m=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"].indexOf(mn);
days=course.match(/(\d{1,2})(st|nd|rd|th)/g);
year=d.getFullYear();
for (i in days) {
coursedate.setMonth(m);
coursedate.setDate(days[i].slice(0,-2));
if ((coursedate-d)/dlen<14 && s.getRange(r,16).getValue()!="mail sent") {
GmailApp.sendEmail(recipient, subject, body);
s.getRange(r,16).setValue("mail sent");//arbitrarily picked col 15
}
}
}
};
It looks like the time remaining until the course begins is checked with this line:
if ((coursedate-d)/dlen<14 && s.getRange(r,16).getValue()!="mail sent") {
That line is making a date calculation, and checking if the value of a cell had the text "mail sent" in it. If the cell didn't have mail sent AND the date calculation is true, then an email gets sent. Notice that the divisor dlen is a value that was hard coded.
var mn,m,days,d=new Date(),coursedate=new Date(),dlen=8.64e7,i,course,r,year;//8.64e7
The value of the variable dlen is set to a constant value of 8.64e7. But I'm guessing that the math no longer works right for some reason.
The value of dlen is 86400000.
There are 24 hours in a day, 60 minutes in an hour, and 60 seconds in a minute.
24 * 60 * 60 = 86,400 seconds in a day. So, obviously, the 86,400,000 number is a multiple of the number of seconds in a day.
I'm guessing that there is a new course every month?
You can add a Logger.log() statement to your code, and run it so see what the variable values are:
Logger.log('coursedate: ' + coursedate);
If you put that line in your code:
for (i in days) {
coursedate.setMonth(m);
Logger.log('coursedate: ' + coursedate);
coursedate.setDate(days[i].slice(0,-2));
Logger.log('coursedate: ' + coursedate);
Logger.log('d: ' + d);
if ((coursedate-d)/dlen<14 && s.getRange(r,16).getValue()!="mail sent") {
That would tell you what the values of the variables coursedate and d are right when the calculation is being made.
Then you could hopefully figure out why the math is the way it is. To view the Logger.log() output, click on the View menu, and choose the Logs menu item. A list of logged values will be in the window.
We are using parse.com service to manage the leaderboard of our Android game (Real Drift). It worked for few days (the game was released on July the 4th) but since July 15th Parse server doesn't respond anymore (ITS NOT A TIME OUT ERROR). Or better, it responds but the object is always empty. In the data browser, even if it says that there are more than 1 million of rows, no result is shown.
Is anybody else experiencing the same? I checked the analytics and it doesn't seem that any threshold has been passed (we are using the free account):
Max 3 reqs/sec (limit 30 req/sec),
File storage 0%,
Database storage 1%,
Data transfer 0%,
API reqs 200k/day (dont know if there is a limit here),
Objects count 1 mil (dont know if there is a limit here).
PARSE SUPPORT IS THE WORST SUPPORT I HAVE EVER SEEN IN MY LIFE. THERE IS NO SUPPORT EMAIL, THEIR INTERNAL FORUM HAS BEEN CLOSED (VERY HARD TO RECEIVE A REPLY EVEN TO SIMPLY QUESTION BTW WHEN IT WAS OPEN), THE ONLY WAY IS TO POST A QUESTION HERE ON STACK OVERFLOW, AND THE QUESTION CAN BE MODIFIED BY PARSE PEOPLE AS THEY WANT (FOSCO CHANGED THE TITLE OF THE QUESTION TO QUERY TIMING OUT EVEN IF IT WASN'T THE CASE).
MORE, WHEN IT BECOME CLEAR THAT IT WAS A PARSE PROBLEM, NOBODY REPLIED BACK, THEY JUST CHANGED THE QUESTION TO MAKE IT APPEAR AS MY FAULT (INEFFICIENT QUERY).
TO FOSCO AND ALL PARSE PERSONNEL: PLEASE DONT EDIT THIS QUESTION, OR IM GOING TO POST IT AGAIN AND AGAIN.
Anyway, this is the code:
public void LoadOnlineLeaderboard(string countryCode="")
{
loading=true;
ParseQuery<ParseObject> query = ParseObject.GetQuery("GameScore").WhereEqualTo("trackId",persistentData.tracks[indexLead].id).OrderByDescending("score").ThenByDescending("playerName").Limit(10).Skip(startingPoint);
if (countryCode!="") query=query.WhereEqualTo("countryCode", countryCode);
StartCoroutine(WaitForRequestLoad(query));
}
IEnumerator WaitForRequestLoad(ParseQuery<ParseObject> query)
{
var queryTask=query.FindAsync();
while (!queryTask.IsCompleted) yield return null;
if (queryTask.IsFaulted)
{
foreach(System.Exception e in queryTask.Exception.InnerExceptions) {
ParseException parseException = (ParseException) e;
Debug.LogError("lederboard loading Error");
Debug.LogError("Error message: " + parseException.Message);
Debug.LogError("Error code: " + parseException.Code);
}
leaderboardError.SetActive(true);
}
else
{
Debug.Log("lederboard loading OK");
RenderIEnumerableLeaderboard(queryTask.Result);
}
loading=false;
}
I am wanting to know a button is rendered on main window UI or not. This button rendering is depending on server response result (written in Objective C). If server response comes perfectly it becomes render perfectly (VISIBLE) otherwise it is not present there (INVISIBLE). And whenever it becomes visible I always tap on it for further next process.
I wrote code
UIATarget.localTarget().pushTimeout(200);
//My code
UIATarget.localTarget().popTimeout();
By the above code I have to wait till 200 sec but my concern is I want to wait but whenever object is on screen I don't want keep me busy in WAITING MODE.
How will I write code in automation?
Thanks
Ok, this might give you idea how to follow-up:
For your view implement an accessibilityValue method which returns a JSON formatted value:
- (NSString *)accessibilityValue
{
return [NSString stringWithFormat:
#"{'MyButtonisVisible':%#}",
self.MyButton.isHidden ? #"false" : #"true"];
}
Then somehow you can access it from your test javascript:
var thisproperty = eval("(" + element.value() + ")");
if (thisproperty.MyButtonisVisible) {
UIATarget.localTarget().tap({"x":100, "y":100});
}
Hope that helps.
If you make the name different when you enable the button you can do this:
var awesomeButton = target.frontMostApp().mainWindow().buttons()[0];
UIATarget.localTarget().pushTimeout(200);
awesomeButton.withName("My Awesome Button");
if (awesomeButton.isVisible()) {
UIALogger.logError("Error no awesome button!");
}
UIATarget.localTarget().popTimeout();
withName will repeatedly test the name and control will return to your script once the name matches or when the time out is reached.
Per Apple's Doc
withName:
Tests if the name attribute of the element has the given string value. If the match fails, the test is retried until the current timeout expires.
Timeout Periods:
If the action completes during the timeout period, that line of code returns, and your script can proceed. If the action doesn’t complete during the timeout period, an exception is thrown.
https://developer.apple.com/library/etc/redirect/xcode/ios/e808aa/documentation/DeveloperTools/Conceptual/InstrumentsUserGuide/UsingtheAutomationInstrument/UsingtheAutomationInstrument.html#//apple_ref/doc/uid/TP40004652-CH20
We have a HTTP end-point that takes a long time to run and can also be called concurrently by users. As part of this request, we update the model inside a synchronized block so that other (possibly concurrent) requests pick up that change.
E.g.
MyModel m = null;
synchronized (lockObject) {
m = MyModel.findById(id);
if (m.status == PENDING) {
m.status = ACTIVE;
} else {
//render a response back to user that the operation is not allowed
}
m.save(); //Is not expected to be called unless we set m.status = ACTIVE
}
//Long running operation continues here. It can involve further changes to instance "m"
The reason for the synchronized block is to ensure that even concurrent requests get to pick up the latest status. However, the underlying JPA does not commit my changes (m.save()) until the request is complete. Since this is a long-running request, I do not want to wait until the request is complete and still want to ensure that other callers are notified of the change in status. I tried to call "m.em().flush(); JPA.em().getTransaction().commit();" after m.save(), but that makes the transaction unavailable for the subsequent action as part of the same request. Can I just given "JPA.em().getTransaction().begin();" and let Play handle the transaction from then on? If not, what is the best way to handle this use-case?
UPDATE:
Based on the response, I modified my code as follows:
MyModel m = null;
synchronized (lockObject) {
m = MyModel.findById(id);
if (m.status == PENDING) {
m.status = ACTIVE;
} else {
//render a response back to user that the operation is not allowed
}
m.save(); //Is not expected to be called unless we set m.status = ACTIVE
}
new MyModelUpdateJob(m.id).now();
And in my job, I have the following line:
doJob() {
MyModel m = MyModel.findById(id);
print m.status; //This still prints the old status as-if m.save() had no effect...
}
What am I missing?
Put your update code in a job an call
new MyModelUpdateJob(id).now().get();
thus the update will be done in another transaction that is commited at the end of the job
ouch, as soon as you add more play servers, you will be in trouble. You may want to play with optimistic locking in your example or and I advise against it pessimistic locking....ick.
HOWEVER, looking at your code, maybe read the article Building on Quicksand. I am not sure you need a synchronized block in that case at all...try to go after being idempotent.
In your case if
1. user 1 and user 2 both call that method and it is pending, then it goes to active(Idempotent)
If user 1 or user 2 wins, well that would be like you had the synchronization block anyways.
I am sure however you have a more complex scenario not shown here, BUT READ that article Building on Quicksand as it really changes the traditional way of thinking and is how google and amazon and very large scale systems operate.
Another option for distributed transactions across play servers is zookeeper which the big large nosql guys use BUT only as a last resort ;) ;)
later,
Dean
I am Using WaitforComplete() in watiN but it doesnt seems to work well. As it executes the next statement even if you have given longer time to wait. I am using thread.sleep() to stop my application until it gets the desired page or element. But the thing is pages are so much dynamic that sometimes it takes much longer time as specified.
Any better solution. Any thing that will catch the page return dynamically and dont go to execute next statments in application.
Sample of Code
'Show Details page
Assert.AreEqual("Confirmation", _internetExplorer.Title)
If (_internetExplorer.Button(Find.ById(New Regex("btnFinish"))).Exists) Then
_internetExplorer.Button(Find.ById(New Regex("btnFinish"))).Click()
Else
Assert.Fail("Could not Find Finish Booking Button on Confirmation Page")
End If
System.Threading.Thread.Sleep(100000)
'Show Booking Summary page
Assert.AreEqual("Display Booking", _internetExplorer.Title)
I want something that detect the return of page dynamically. instead of giving some constant value.
WaitForComplete only works well if there is a postback after some action. Otherwise you have to find something else to wait for. Following an example on how to wait for the specified title:
_internetExplorer.Element("title", "Confirmation").WaitUntilExists();
I would always prefer to use one of the WaitXXX methods instead of Thread.Sleep cause the WaitXXX methods do only wait until the contraint is met. Where as Sleep waits for the time you specified. If its to long, time is waisted. If its to short, problems arise.
HTH,
Jeroen
The WaitForComplete method esentially moves on once the browser has set it's readystate to comllete and the busy state to false.
What I typically do is to try and access what you need to, then perform a thread.sleep for say half a second, then try again. I also have a global timeout that quits after say 10 seconds.
int timeout = 20;
bool controlFound = false;
for (int i = 0; i < timeout; i++)
{
if (_internetExplorer.Button(Find.ById(New Regex("btnFinish"))).Exists)
{
_internetExplorer.Button(Find.ById(New Regex("btnFinish"))).Click();
controlFound = true;
break;
}
else
{
System.Threading.Thread.Sleep(500);
}
}
if (!controlFound)
{
Assert.Fail("Control not found");
}
If it is executing the next statement, it should be finding the corresponding element. I suggest posting a sample of the code you are trying.