the-blue-alliance / the-blue-alliance

A webapp for accessing information about the FIRST Robotics Competition.
https://www.thebluealliance.com
MIT License
394 stars 177 forks source link

Implement OPR calulator #86

Closed josephbisch closed 12 years ago

josephbisch commented 12 years ago

Write a script to calculate OPR for each event. Maybe calculate it every 20 minutes while an event is in progress. Display OPR for each team in a table on event page.

josephbisch commented 12 years ago

I got a script started. I see Greg posted on Chief Delphi asking about calculating OPR a while back. Did you run into any issues in implementing it, Greg?

tombot commented 12 years ago

I have something implemented that uses SciPy. Unfortunately this isn't supported in GAE. I have some work that will use an offsite server to read the FMS twitter feed, parse the results, calculate OPR, and push to TBA. Obviously something that runs native on GAE is preferred.

josephbisch commented 12 years ago

I wrote the matrix decomposition and solver functions without the use of SciPy. I am currently not using any external libraries. I am still getting it working with match data to calculate the OPR, but I did test it with some matrices and the decomposition and solve functions worked.

tombot commented 12 years ago

Cool. I'm not sure of the easiest way to share not-yet-ready code on Github, but I'd love to see it.

We will probably need to add a TeamOPR to the model. I think it would be cool to keep track of each events OPRs as well as a yearly global total.

On Tue, Mar 20, 2012 at 12:43 PM, josephbisch < reply@reply.github.com

wrote:

I wrote the matrix decomposition and solver functions without the use of SciPy. I am currently not using any external libraries. I am still getting it working with match data to calculate the OPR, but I did test it with some matrices and the decomposition and solve functions worked.


Reply to this email directly or view it on GitHub:

https://github.com/gregmarra/the-blue-alliance/issues/86#issuecomment-4603822

josephbisch commented 12 years ago

I guess I'll put it on Github sometime this week as a gist. It should definitely be possible to keep track of the OPRs for each event. A yearly global total would be cool, but it would be resource intensive to perform the matrix operations on such large matrices. We'll see how it goes.

I know Greg has been trying to reduce the RPCs by removing the TBAVideo and YoutubeVideo classes from the model. Maybe it is possible to store OPR without adding it as a class to the model, maybe not. I don't know.


From: tombot reply@reply.github.com To: josephbisch josephbisch@yahoo.com Sent: Tuesday, March 20, 2012 7:26 PM Subject: Re: [the-blue-alliance] Implement OPR calulator (#86)

Cool. I'm not sure of the easiest way to share not-yet-ready code on Github, but I'd love to see it.

We will probably need to add a TeamOPR to the model. I think it would be cool to keep track of each events OPRs as well as a yearly global total.

On Tue, Mar 20, 2012 at 12:43 PM, josephbisch < reply@reply.github.com

wrote:

I wrote the matrix decomposition and solver functions without the use of SciPy. I am currently not using any external libraries. I am still getting it working with match data to calculate the OPR, but I did test it with some matrices and the decomposition and solve functions worked.


Reply to this email directly or view it on GitHub:

https://github.com/gregmarra/the-blue-alliance/issues/86#issuecomment-4603822


Reply to this email directly or view it on GitHub: https://github.com/gregmarra/the-blue-alliance/issues/86#issuecomment-4607840

josephbisch commented 12 years ago
[[5.3255398402749545], [16.804879660999518], [3.4965398231577858], [10.228314144210314], [7.747690262939376], [3.917439103089698], [2.822634049783968], [-1.729399177095085], [3.0623227067413525], [3.483275542425408], [-5.865964185812673], [5.582971345942014], [16.03863265655758], [0.5261670768227804], [7.411761241109714], [-5.947031322662208], [5.536942461664336], [14.887045158302115], [4.021540214124888], [9.379176735769333], [3.5541680260108763], [4.449495972202594], [6.310801341439662], [1.576092740710855], [-2.023498570313588], [4.18193105122992], [7.565952963992945], [11.797230013108686], [2.7726217590921896], [5.322380469962735], [3.7817146346390556], [4.276071945022793], [7.108432433303598], [5.985016770141396]]

The above is the OPRs I am getting from the qualification matches from the 2012 WPI regional. These are not the same OPRs that Ed Law over at Chief Delphi got.

By the way, the script executed in 2.85 seconds loading the data in from local csv files. I will put the script and the csv files online tomorrow.

josephbisch commented 12 years ago

I put the code over at: http://gist.github.com/2147749

Don't forget to download the csv files I put in the comment!

I got the code working. I forgot to multiple two matrices together. Now I am getting the same exact results as Ed Law.

I also got it to execute in 0.45 seconds now. Yes, that is less than a second.

You brought up an interesting point about storing the OPR data. There is one OPR per team per event, or we can think of it as one per event per team. Since a global OPR would be stored in the team object (one global OPR per team), it might be best to store all the OPRs in team objects. Each team would have a dictionary with eventids as the key and OPR as the value. the global OPR can be stored as its own separate value.

I don't really have any experience with GAE. Can you comment on why a TeamOPR class is necessary?

tombot commented 12 years ago

I like the idea of storing a json blob of team oprs in the Event class. This way we can just pull that out on the event page and pass it into the templating engine.

The main issue with this is getting ALL the oprs in a sorted order may be hard. (We really want a top 50 teams by OPR page, right?)

Perhaps also add a TeamStats class which has a Team, a year, and a stat? We would only have to fetch these for the overall OPR page, which would get hit way more often and is really cacheable.

Greg, thoughts?

On Wed, Mar 21, 2012 at 8:06 AM, josephbisch < reply@reply.github.com

wrote:

I put the code over at: gist.github.com/2147749

Don't forget to download the csv files I put in the comment!

I got the code working. I forgot to multiple two matrices together. Now I am getting the same exact results as Ed Law.

I also got it to execute in 0.45 seconds now. Yes, that is less than a second.

You brought up an interesting point about storing the OPR data. There is one OPR per team per event, or we can think of it as one per event per team. Since a global OPR would be stored in the team object (one global OPR per team), it might be best to store all the OPRs in team objects. Each team would have a dictionary with eventids as the key and OPR as the value. the global OPR can be stored as its own separate value.

I don't really have any experience with GAE. Can you comment on why a TeamOPR class is necessary?


Reply to this email directly or view it on GitHub:

https://github.com/gregmarra/the-blue-alliance/issues/86#issuecomment-4619141

josephbisch commented 12 years ago

So you're saying that you don't see any problems with storing the regional OPRs in the Event class. If so, we should move forward with that, for now.

The global OPR is a big issue, since, due to the nature of OPR and the fact that it is global, it must be calculated for all teams no matter the number of teams we want to display.

We need Greg to comment on the feasibility of global OPR. It was not even something I had in mind when I started writing the script.

tombot commented 12 years ago

It's feasible, for sure. Just need to feed a list of all the matches in. There are some CSVs of all the 2011 results on Chief you can test against. I imagine we would only run the global cumulative OPR calculations once a week. We could tack these on to the Team class as a json blob

Team : stats = [[year:2012, [type="opr", stat=35.5]]]

Or something like that. May make it hard to lookup and sort oprs.

On Wed, Mar 21, 2012 at 9:11 AM, josephbisch < reply@reply.github.com

wrote:

So your saying that you don't see any problems with storing the regional OPRs in the Event class. If so, we should move forward with that, for now.

The global OPR is a big issue, since, due to the nature of OPR and the fact that it is global, it must be calculated for all teams no matter the number of teams we want to display.

We need Greg to comment on the feasibility of global OPR. It was not even something I had in mind when I started writing the script.


Reply to this email directly or view it on GitHub:

https://github.com/gregmarra/the-blue-alliance/issues/86#issuecomment-4620728

gregmarra commented 12 years ago

We should look into some of the ListProperty classes Models can have. They may make some of this easier than storing JSON blobs.

Storing an Event's OPRs in a JSON blob seems acceptable. I also wouldn't feel terrible about having a separate EventStats class for this, since doing the extra RPC per event is not terrible, and we don't need these stats in Event listviews, only detail views?

josephbisch commented 12 years ago

I looked at ListPropetry, and it seems like a ListProperty of floats is all we need. It is going to take a while, but I started writing some of the code for the display of the opr data. I called the ListProperty oprs. It is part of Event. The oprs will be stored in ascending order by team number. That eliminates the need to store the team numbers with the oprs.

gregmarra commented 12 years ago

Hmm, I don't know if I feel good about relying on the order of the teams. That is unstable, because teams can end up not attending events..

On Wed, Mar 21, 2012 at 2:57 PM, josephbisch < reply@reply.github.com

wrote:

I looked at ListPropetry, and it seems like a ListProperty of floats is all we need. It is going to take a while, but I started writing some of the code for the display of the opr data. I called the ListProperty oprs. It is part of Event. The oprs will be stored in ascending order by team number. That eliminates the need to store the team numbers with the oprs.


Reply to this email directly or view it on GitHub:

https://github.com/gregmarra/the-blue-alliance/issues/86#issuecomment-4628173

http://grgmrr.com 860.263.9018

josephbisch commented 12 years ago

How does TBA handle teams not showing up for an event? The event detail page lists teams attending events. Does it correct the list of teams if a team does not attend?

gregmarra commented 12 years ago

We have to manually edit it out :-/ We haven't done a lot of this data cleanup.

On Wed, Mar 21, 2012 at 3:17 PM, josephbisch < reply@reply.github.com

wrote:

How does TBA handle teams not showing up for an event? The event detail page lists teams attending events. Does it correct the list of teams if a team does not attend?


Reply to this email directly or view it on GitHub:

https://github.com/gregmarra/the-blue-alliance/issues/86#issuecomment-4628533

http://grgmrr.com 860.263.9018

josephbisch commented 12 years ago

Alright, so we will store 2 ListPropertys, 1 with the list of teams, the other with the corresponding OPRs. Since I was planning on pulling the list of teams from TBA, I'll modify the OPR script to validate the list of teams against the match data. Any team that has not played a match gets removed from the list.

gregmarra commented 12 years ago

is it possible to store a list of tuples, so we only need one list element? slightly cleaner.

On Wed, Mar 21, 2012 at 4:47 PM, josephbisch < reply@reply.github.com

wrote:

Alright, so we will store 2 ListPropertys, 1 with the list of teams, the other with the corresponding OPRs. Since I was planning on pulling the list of teams from TBA, I'll modify the OPR script to validate the list of teams against the match data. Any team that has not played a match gets removed from the list.


Reply to this email directly or view it on GitHub:

https://github.com/gregmarra/the-blue-alliance/issues/86#issuecomment-4629807

http://grgmrr.com 860.263.9018

josephbisch commented 12 years ago

It appears that is it not possible to store a list of tuples directly in a ListProperty.

A number of methods of storing tuples are presented at the following link: http://stackoverflow.com/questions/5044653/what-is-the-most-performant-way-to-store-a-list-of-tuples-in-app-engine

I don't know if any of them are any good or if storing 2 separate lists is better than those alternatives.

gregmarra commented 12 years ago

Storing two lists is basically method 2. Let's do that.

On Wed, Mar 21, 2012 at 5:05 PM, josephbisch < reply@reply.github.com

wrote:

It appears that is it not possible to store a list of tuples directly in a ListProperty.

A number of methods of storing tuples are mention at the following link:

http://stackoverflow.com/questions/5044653/what-is-the-most-performant-way-to-store-a-list-of-tuples-in-app-engine

I don't know if any of them are any good or if storing 2 separate lists is better than those alternatives.


Reply to this email directly or view it on GitHub:

https://github.com/gregmarra/the-blue-alliance/issues/86#issuecomment-4630032

http://grgmrr.com 860.263.9018

josephbisch commented 12 years ago

I am setting a deadline for myself to get this done in time for week 5 regionals (March 29).

How will we display the OPRs? The teams are already listed on the event detail page. Should we get rid of the current listing and display a table with team name, team number and OPR?We can use javascript to allow the user to change whether the table is sorted by team number or OPR. That way the list of teams attending the regional ordered by team number is still available. By default we can sort by OPR for users without javascript or with javascript disabled.


From: Greg Marra reply@reply.github.com To: josephbisch josephbisch@yahoo.com Sent: Wednesday, March 21, 2012 8:08 PM Subject: Re: [the-blue-alliance] Implement OPR calulator (#86)

Storing two lists is basically method 2. Let's do that.

On Wed, Mar 21, 2012 at 5:05 PM, josephbisch < reply@reply.github.com

wrote:

It appears that is it not possible to store a list of tuples directly in a ListProperty.

A number of methods of storing tuples are mention at the following link:

http://stackoverflow.com/questions/5044653/what-is-the-most-performant-way-to-store-a-list-of-tuples-in-app-engine

I don't know if any of them are any good or if storing 2 separate lists is better than those alternatives.


Reply to this email directly or view it on GitHub:

https://github.com/gregmarra/the-blue-alliance/issues/86#issuecomment-4630032

http://grgmrr.com 860.263.9018


Reply to this email directly or view it on GitHub: https://github.com/gregmarra/the-blue-alliance/issues/86#issuecomment-4630071

tombot commented 12 years ago

I would just add another pane (Info, Matches, OPR, Teams) that shows a table of Teams vs OPRS. By default sort them by OPR, but make it sortable by team number or reverse or whatever. A quick google found this: https://github.com/micha/jquery-tablesort but I'm sure there are others.

On Thu, Mar 22, 2012 at 3:25 PM, josephbisch < reply@reply.github.com

wrote:

How will we display the OPRs? The teams are already listed on the event detail page. Should we get rid of the current listing and display a table with team name, team number and OPR?We can use javascript to allow the user to change whether the table is sorted by team number or OPR. That way the list of teams attending the regional ordered by team number is still available. By default we can sort by OPR for users without javascript or with javascript disabled.

josephbisch commented 12 years ago

I got the opr calculation and display sort-of working. The opr data gets calculated just for WPI using TBA data.

I need to get this working for all events and get the sorting by opr working and then I will send a pull request.

We need to make sure the opr only gets calculated when all the qualification matches have been played.

wpiopr

gregmarra commented 12 years ago

Why can we only calculate OPR after all quals? We can make a cron job update them nightly - is there a reason doing it after one day is bad? It will be partial data, but is that ok?

Do we want to do something to make the OPRs more legible? Like color-code bands of them, or cut them off after two decimals?

-Greg

On Sun, Mar 25, 2012 at 1:34 PM, josephbisch < reply@reply.github.com

wrote:

I got the opr calculation and display sort-of working. The opr data gets calculated just for WPI using TBA data.

I need to get this working for all events and get the sorting by opr working and then I will send a pull request.

We need to make sure the opr only gets calculated when all the qualification matches have been played.

wpiopr


Reply to this email directly or view it on GitHub:

https://github.com/gregmarra/the-blue-alliance/issues/86#issuecomment-4684433

http://grgmrr.com 860.263.9018

josephbisch commented 12 years ago

The nightly cron job is a good idea. After the first day of quals, all teams that showed up should have played at least one match. We can have the contentTopBlocker read "OPR as of match 50" or whatever the match number is.

I'll try adding zebra striping to the table and cut OPRs off after two decimals. We can see how that looks.

gregmarra commented 12 years ago

Awesome. Probably should zebra stripe all of our tables, so may reuse that class on all infotables.

josephbisch commented 12 years ago

We're so close to getting this fully implemented!

I'm having an issue with the cron job. I only want to calculate the OPR for events that have at least 1 match (if there are no matches, we get an error). I have the following.

events = Event.all()
        for event in events:
            if (event.match_set is not None) and (len(event.match_set)!=0):
                event_key = event.get_key_name()
                opr,teams = OprHelper.opr(event_key)
                oprs.append((opr,teams))
                event.oprs = opr
                event.oprteams = teams
                event.put()

The if statement should evaluate false for events without matches, but it is not. It is evaluating true for 2012ca, which does not have any matches on my test instance.

gregmarra commented 12 years ago

Look at how we do _set counts in match_controller.py

https://github.com/gregmarra/the-blue-alliance/blob/master/controllers/match_controller.py#L43

match_set is a Query object, so you actually need to use it's count() method to get the size of the set.

-Greg

On Tue, Mar 27, 2012 at 3:16 PM, Joseph Bisch < reply@reply.github.com

wrote:

We're so close to getting this fully implemented!

I'm having an issue with the cron job. I only want to calculate the OPR for events that have at least 1 match (if there are no matches, we get an error). I have the following.

events = Event.all()
       for event in events:
           if (event.match_set is not None) and (len(event.match_set)!=0):
               event_key = event.get_key_name()
               opr,teams = OprHelper.opr(event_key)
               oprs.append((opr,teams))
               event.oprs = opr
               event.oprteams = teams
               event.put()

The if statement should evaluate false for events without matches, but it is not. It is evaluating true for 2012ca, which does not have any matches on my test instance.


Reply to this email directly or view it on GitHub:

https://github.com/gregmarra/the-blue-alliance/issues/86#issuecomment-4734223

http://grgmrr.com 860.263.9018

gregmarra commented 12 years ago

also can you change oprteams to opr_teams to match our other model properties?

gregmarra commented 12 years ago

Implemented. A couple other issues to fix up.