teijo / jquery-bracket

jQuery Bracket library for organizing single and double elimination tournaments
http://aropupu.fi/bracket/
MIT License
484 stars 253 forks source link

Save callback and match information #27

Open FoxxMD opened 9 years ago

FoxxMD commented 9 years ago

First let me say this is an awesome plugin! Nothing else I've found comes close to providing the extent of features jquery-bracket offers. Thanks for making this!

I have a bracketing system on the back-end of my app. My app uses a restful API so I can make calls to progress a bracket like POST /api/tournament/5/match/4.

Currently I've written code for the back-end that let's me generate output formatted for jquery-bracket as well as consume json to construct a bracket. So, technically, I could use the data parameter from save and just send the whole bracket back to the my app to be constructed and then stored -- however this inefficient for me since I have the ability to update individual matches.

I'm going to take a few days to read through your code and hopefully hack something together but I was hoping you could push me in the right direction for the things I'd like to do:

Why can't a match array also have match information when edit mode in on?

In init:

if (opts.save && (opts.onMatchClick || opts.onMatchHover))
        $.error('Match callbacks may not be passed in edit mode (in conjunction with save callback)')

Without match callbacks jquery-bracket does not include match information in each match array in results, even if it is part of the original data.

Building off the above, how could I include the modified bracket as a parameter in the save callback?

If I can achieve these things then I can use my match id (stored as match information) and the scores to update individual matches through my API.

Thanks!

teijo commented 9 years ago

Good to hear you've found the project useful :)

Without match callbacks jquery-bracket does not include match information in each match array in results, even if it is part of the original data.

This sounds like a bug.

I can't really recall the exact reason why that specific init check was made to begin with. I could've had a plan to implement customizable edit support for the match data (similar to score and team names), and therefore restrict users from using the overlapping 'read-only handlers' in edit mode. However, I never got that far.

Building off the above, how could I include the modified bracket as a parameter in the save callback?

Unfortunately I haven't actively worked on this project in a while so you'll just have to crawl through the code :/

I don't see a problem first enabling pass-through of the match data from init to save callback. Edit hook for match data would definitely be handy too.

FoxxMD commented 9 years ago

Thanks for the reply! It's good to hear that the things I want to do are feasible and not counter to the design but rather side effects of the current state of the code as you left it.

Also I understand that you haven't worked with the code for awhile so no worries, I'll muddle through it! I'll send a PR when I've got my features in, hopefully they'll be able to help others!

I do have a few questions though -- if you can't remember specifics no problem, it'd be helpful just to get your insight or learn your thought process. Thanks!

This sounds like a bug.

I figured, here's the offending block in mkMatch()

  if (!opts.save) {
    var matchUserData = (results ? results[2] : null)

    if (opts.onMatchHover)
      teamCon.hover(function() { opts.onMatchHover(matchUserData, true) }, function() { opts.onMatchHover(matchUserData, false) })

    if (opts.onMatchClick)
      teamCon.click(function() { opts.onMatchClick(matchUserData) })
  }

So if you don't include save function match data is not rendered! I'll fix this.


Two main questions:

How and where is score input saved?

There are several places where you get values from the DOM inputs, but I can't figure out which one is relevant and how you are storing those inputs to the correct places.

For instance, in the mkMatch() function under the teamElement() function there are two instances where input is saved -- once under input.blur() and once under the editor in done_fn.

Am I correct in thinking the editor function is for team name and the input.blur is for the score value?

Also, I don't understand how the score for team is saved to the whole data structure when all you've passed is one TeamBlock into the teamElement() function. How does this manipulate the entire data structure (originally passed in as an array of arrays) to set that one score?

What data structure is most relevant for adding extra information to matches and where is this rendered?

In order for this to work with my api I need to have the match id, the team id(or rather a unique id for each seat so I can tell them apart), and the score for that team(seat). Something like

{
  "id": 2, //match id
  "teamA": 4, //teamId is teamA, score is 4
  "teamB": 5
}

I could do this one of two ways:

Which do you think would be the beneficial? Would the second option be viable?

For the second, I'm a bit confused on which interface I need to edit in order to change the match info rendered. You have interfaces for Round, Match, and TeamBlock all of which have id properties but none of which are rendered to the final data structure.

It looks like when rendering the logic flows from mkBracket() -> mkRound() -> mkMatch() and then eventually to the results function for mkMatch

    results: function() {
      return [match.a.score, match.b.score]
    }

Am I correct in thinking this is the data that is finally produced? If so then I can edit this result to return more info..

Thanks for reading through all my rambling. Any feedback you can give would be helpful!

FoxxMD commented 9 years ago

I've hacked together a working build that passes the changed match information (hardcoded using my variables) through renderAll() and into the save hook as a third parameter. You can see it here.

It's fortunate that both current match data and the team associated with that seat are available on input.blur() since this makes it easy to cobble together what data has changed. I think I will continue with this route and standardize the data returned.

teijo commented 9 years ago

Yes, the results method implemented by each part of the hierarchy creates the final result. Having the third element there should probably get it working. Just having pass-through of the third element would improve it. To make it more practical, there should be something along the lines of edit decorator, to allow creating case specific form to edit the match data.

The design idea behind the API is that user passes a data structure in init:, then if editing occurs, the save: function will receive the full new state of the data (which can then be input again for init: next time). I'm not sure if indicating what has changed should be the library's concern (like what if user could edit two fields at a time, you'd need the complete new state after finishing the edit).

junhuawa commented 9 years ago

Teijo, Merry Christmas! :)

raghavgupta10may commented 9 years ago

"Currently I've written code for the back-end that let's me generate output formatted for jquery-bracket as well as consume json to construct a bracket" FoxxMD : Can you please help me to create this?

FoxxMD commented 9 years ago

@teijo

To make it more practical, there should be something along the lines of edit decorator, to allow creating case specific form to edit the match data.

That's exactly what I plan on doing!

I'm not sure if indicating what has changed should be the library's concern

I disagree. While I do agree that returning the new state of the entire data structure is appropriate I think you would be remiss not to consider that the structure is made up of smaller components which users may be more interested in. Individual matches, and even seats in those matches, are smaller building blocks where data changes actually matter.

(like what if user could edit two fields at a time, you'd need the complete new state after finishing the edit).

I see your point. However in a real life scenario (where this library is being used at an actual event) matches take place linearly and we can expect data changes to be made in the same fashion. Having a callback for those linear changes to the data would make the library much more useful IMO as the user can then act on that changed data in some external way. My situation is only one example, I could imagine many more scenarios where having access to this information would be greatly beneficial.

This is all moot though since both functions can be provided :) I am going to see if I can condition the data in such a way that it only passes back on those linear changes rather than in an example like you described where multiple data points change as that would make the most sense...

@eeyago

I've written a terrible hack of a library called Scala-Brackets that I am using in an app to do that. It's no where near usable for anyone other than myself but you can get an idea of how I am structuring things to figure out how to implement it in whatever language you are using.

The short version is that I have written the individual structures of a bracket (bracket, match, seat, and participant) into shell structures. I then include properties on each structure to help determine how logic flows for that type of bracket. Then in the bracket structure I implement generic methods from the BaseTournament trait that would be useful for all types of brackets -- like advanceMatch(), addParticipant(), and setScore(). The idea is to keep the data structures agnostic and then implement all the logic to progress that type of bracket (elimination, round-robin, swiss, etc.) from the top down. I am plan taking advantage of jquery-group soon!

I'm using a serialization library to store the in-memory data structures in mongoDB. I can then quickly retrieve them and reconstruct, manipulate, and serialize back to the DB.

You can see how I output to jquery-bracket here.

I haven't actually implemented a way to reconstruct data from jquery-bracket yet :) I'm cheating a bit which is why I wanted the changed data hook -- passing back the whole structure would be inefficient in data usage when I can simply reconstruct from the server-side DB and then manipulate the one match using the data from the callback.

Shoutout to @cadwallion as I drew a lot of inspiration from bracket_tree for this library. Unfortunately his library assumes elimination-style tournament as the data is structured like a binary search tree. If that's all you need though his solution may be more useful!

teijo commented 9 years ago

Having a callback for those linear changes to the data would make the library much more useful IMO as the user can then act on that changed data in some external way.

Very likely. These kinds of design questions get best answered when there is a good real life use case that needs to be implemented. Unfortunately since this has been more of a toy project for me, there haven't been too many such use cases :)