diff options
64 files changed, 1530 insertions, 342 deletions
@@ -8,6 +8,12 @@ gem 'rails', '4.0.2' # bcrypt is used for password digesting gem 'bcrypt-ruby', '3.1.2' +gem 'httparty' + +gem 'simple_captcha2', require: 'simple_captcha' + +#gem 'rmagick' + group :development, :test do # Use sqlite3 as the database for Active Record gem 'sqlite3' @@ -41,6 +47,9 @@ gem 'turbolinks' # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder gem 'jbuilder', '~> 1.2' +# Asynchronously handle longer or delayed tasks +gem 'delayed_job' + group :doc do # bundle exec rake doc:rails generates the API under doc/api. gem 'sdoc', require: false diff --git a/Gemfile.lock b/Gemfile.lock index 7899a93..51e29e7 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -26,7 +26,7 @@ GEM thread_safe (~> 0.1) tzinfo (~> 0.3.37) arel (4.0.2) - atomic (1.1.15) + atomic (1.1.16) bcrypt-ruby (3.1.2) bootstrap-sass (3.1.1.0) sass (~> 3.2) @@ -38,9 +38,14 @@ GEM coffee-script-source execjs coffee-script-source (1.7.0) + delayed_job (4.0.0) + activesupport (>= 3.0, < 4.1) erubis (2.7.0) execjs (2.0.2) hike (1.2.3) + httparty (0.13.0) + json (~> 1.8) + multi_xml (>= 0.5.2) i18n (0.6.9) jbuilder (1.5.3) activesupport (>= 3.0.0) @@ -55,7 +60,8 @@ GEM treetop (~> 1.4.8) mime-types (1.25.1) minitest (4.7.5) - multi_json (1.8.4) + multi_json (1.9.2) + multi_xml (0.5.5) polyglot (0.3.4) rack (1.5.2) rack-test (0.6.2) @@ -73,18 +79,21 @@ GEM activesupport (= 4.0.2) rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) - rake (10.1.1) + rake (10.2.2) rdoc (4.1.1) json (~> 1.4) ref (1.0.5) - sass (3.2.14) - sass-rails (4.0.1) + sass (3.2.18) + sass-rails (4.0.2) railties (>= 4.0.0, < 5.0) - sass (>= 3.1.10) + sass (~> 3.2.0) + sprockets (~> 2.8, <= 2.11.0) sprockets-rails (~> 2.0.0) sdoc (0.4.0) json (~> 1.8) rdoc (~> 4.0, < 5.0) + simple_captcha2 (0.2.1) + rails (>= 3.1) sprockets (2.11.0) hike (~> 1.2) multi_json (~> 1.0) @@ -98,8 +107,8 @@ GEM therubyracer (0.12.1) libv8 (~> 3.16.14.0) ref - thor (0.18.1) - thread_safe (0.2.0) + thor (0.19.1) + thread_safe (0.3.1) atomic (>= 1.1.7, < 2) tilt (1.4.1) treetop (1.4.15) @@ -107,8 +116,8 @@ GEM polyglot (>= 0.3.1) turbolinks (2.2.1) coffee-rails - tzinfo (0.3.38) - uglifier (2.4.0) + tzinfo (0.3.39) + uglifier (2.5.0) execjs (>= 0.3.0) json (>= 1.8.0) @@ -119,11 +128,14 @@ DEPENDENCIES bcrypt-ruby (= 3.1.2) bootstrap-sass coffee-rails (~> 4.0.0) + delayed_job + httparty jbuilder (~> 1.2) jquery-rails rails (= 4.0.2) sass-rails (~> 4.0.0) sdoc + simple_captcha2 sqlite3 therubyracer turbolinks diff --git a/app/assets/javascripts/ajax.js b/app/assets/javascripts/ajax.js new file mode 100644 index 0000000..040c100 --- /dev/null +++ b/app/assets/javascripts/ajax.js @@ -0,0 +1,19 @@ +function populate() { + //populate optionArray + //make a form element + var e = document.getElementById("tournament_id"); + var gameType = e.options[e.selectedIndex].text; + if (gameType != "Select a Game Type") { + alert(gameType + " was Selected!"); + //populate optionArray via AJAX + //select * from tournament_settings where gametype = GameType + for(var option in optionArray){ + //identify the number of + ; + } + }; + +//$.ajax(url: "/selected").done (html) -> $("#ajax-form").append html + +} + diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css index 3192ec8..dd7588a 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.css @@ -11,3 +11,20 @@ *= require_self *= require_tree . */ + +#query{ + background-color: white; + border: 2px solid #ED9C28; + border-radius: 5px; + color: #FFF; + font-weight: bold; + height: 30px; +} +.navbar{ + background-color: white; +} + +footer { + clear: both; + margin-top: 10px; +}
\ No newline at end of file diff --git a/app/assets/stylesheets/custom.css.scss b/app/assets/stylesheets/custom.css.scss new file mode 100644 index 0000000..1a9b09a --- /dev/null +++ b/app/assets/stylesheets/custom.css.scss @@ -0,0 +1,53 @@ +@import "bootstrap"; + +header > nav { + @extend .navbar; + @extend .navbar-inverse; + color: white; + + #log-buttons { + margin-top: 8px; + form { display: inline; } + } + form.search { + @extend .navbar-form; + @extend .navbar-right; + input[type="submit"] { + @extend .btn-warning; margin-top: -3px; margin-right: 8px; + } + } +} + +a, input[type="submit"] { + @extend .btn; + &.user { @extend .btn-info; } + &.signup { @extend .btn-success; } + &.signin { @extend .btn-info; } + &.signout { @extend .btn-danger; } +} + +p.errors { + background-color: #FCF8C7; + color: red; + border-radius: 7px; + padding: 10px; +} + +#errorExplanation { + h2 { + font-size: 1em; + color: red; + font-weight: bold; + } + li { + font-size: 1em; + color: red; + font-style: italic; + } + +} + +#notice { + text-align: center; + font-weight: bold; +}
\ No newline at end of file diff --git a/app/assets/stylesheets/scaffolds.css.scss b/app/assets/stylesheets/scaffolds.css.scss index 6ec6a8f..6700fef 100644 --- a/app/assets/stylesheets/scaffolds.css.scss +++ b/app/assets/stylesheets/scaffolds.css.scss @@ -1,3 +1,5 @@ +@import "bootstrap"; + body { background-color: #fff; color: #333; @@ -6,6 +8,10 @@ body { line-height: 18px; } +h1, h2, h3, h4, h5{ + color: #0f0f0f; +} + p, ol, ul, td { font-family: verdana, arial, helvetica, sans-serif; font-size: 13px; @@ -21,11 +27,11 @@ pre { a { color: #000; &:visited { - color: #666; + color: #000; } &:hover { - color: #fff; - background-color: #000; + color: green; + text-decoration: none; } } @@ -40,8 +46,9 @@ div { } .field_with_errors { - padding: 2px; - background-color: red; + padding: 1px; + background-color: #FF4C4C; + box-shadow: 0px 0px 5px red; display: table; } @@ -67,3 +74,26 @@ div { list-style: square; } } + +.navbar-brand { + @extend .no-dec; + a{ + color: white; + &:hover, &:active, &:focus { + color: white; + font-weight: normal; + text-decoration: none; + } + } +} + +footer { + text-align: center; + border-top: solid 1px #999999; + padding-top: 18px; + margin-top: 18px; +} + +button, input[type="submit"] { + @extend .btn; +} diff --git a/app/assets/stylesheets/static.css.scss b/app/assets/stylesheets/static.css.scss index 5a803c8..d73e77d 100644 --- a/app/assets/stylesheets/static.css.scss +++ b/app/assets/stylesheets/static.css.scss @@ -1,3 +1,12 @@ // Place all the styles related to the static controller here. // They will automatically be included in application.css. // You can use Sass (SCSS) here: http://sass-lang.com/ + +.jumbotron { + background-color: #FFF; + + p { + line-height: 1.5em; + } + +} diff --git a/app/assets/stylesheets/tournaments.css.scss b/app/assets/stylesheets/tournaments.css.scss index e372b90..2074783 100644 --- a/app/assets/stylesheets/tournaments.css.scss +++ b/app/assets/stylesheets/tournaments.css.scss @@ -1,3 +1,83 @@ // Place all the styles related to the tournaments controller here. // They will automatically be included in application.css. // You can use Sass (SCSS) here: http://sass-lang.com/ + +p.default-field { + display: inline; +} + +span.default-explanation { + color: gray; + font-style: italic; +} + +#players-needed { + text-align: center; + font-style: italic; +} + +#tournament-side-params { + background: none repeat scroll 0 0 #ADD8E6; + border-radius: 5px; + float: right; + font-size: 7px; + padding: 10px; + + p { + font-size: 10px; + margin-bottom: 5px; + } + +} + +#tournament-users{ + + li { + color: green; + } + + .black { + color: black; + } +} + + +/* Style of a tournament listing div */ +div.tournament-listing { + margin-top: 10px; + border-radius: 5px; + box-shadow: 2px 2px 4px #B8B8B8; + border: 2px solid #AAAAAA; + min-height: 100px; + padding: 4px; + + /* AKA the listing title */ + h3 { + margin-top: 0px; + color: #F0AD4E; + font-weight: bold; + } + + h3:hover { + color: #D09D3E; + } + + /* host of the tournament */ + .host { + font-weight: bold; + } + + .col-md-8 { + padding: 0; + a { + padding: 5px 0 0 0; + } + } +} + +div.leave-buttons { + margin-top: 50px; + form { + display: inline; + } +}
\ No newline at end of file diff --git a/app/controllers/alerts_controller.rb b/app/controllers/alerts_controller.rb index a3cb8f9..333022a 100644 --- a/app/controllers/alerts_controller.rb +++ b/app/controllers/alerts_controller.rb @@ -1,6 +1,4 @@ class AlertsController < ApplicationController - before_action :set_alert, only: [:show, :edit, :update, :destroy] - # GET /alerts # GET /alerts.json def index @@ -62,11 +60,16 @@ class AlertsController < ApplicationController end private + # Use callbacks to share common setup or constraints between actions. def set_alert @alert = Alert.find(params[:id]) end + def is_owner?(object) + object.author == current_user + end + # Never trust parameters from the scary internet, only allow the white list through. def alert_params params.require(:alert).permit(:author_id, :message) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 27ef6a7..d5752aa 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,5 +1,55 @@ class ApplicationController < ActionController::Base + before_action :set_object, only: [:show] + before_action :check_create, only: [:new, :create] + before_action :check_edit, only: [:edit, :update] + before_action :check_delete, only: [:destroy] + # Prevent CSRF attacks by raising an exception. # For APIs, you may want to use :null_session instead. protect_from_forgery with: :exception + + #include sessionhelper for the session controller and view + include SessionsHelper + + include SimpleCaptcha::ControllerHelpers + + def check_permission(verb, object=nil) + unless current_user.can?((verb.to_s+"_"+noun).to_sym) or (!object.nil? and is_owner?(object)) + respond_to do |format| + format.html do + if object.nil? + redirect_to send(noun.pluralize+"_url"), notice: "You don't have permission to #{verb} #{noun.pluralize}." + else + redirect_to object, notice: "You don't have permission to #{verb} this #{noun}." + end + end + format.json { render json: "Permission denied", status: :forbidden } + end + end + end + + def noun + @noun ||= self.class.name.underscore.sub(/_controller$/, '').singularize + end + + def set_object + object = send("set_"+noun) + end + + def check_create + check_permission(:create) + end + def check_edit + object = send("set_"+noun) + check_permission(:edit, object) + end + def check_delete + object = send("set_"+noun) + check_permission(:edit, object) + end + + # Override this + def is_owner?(object) + return false + end end diff --git a/app/controllers/games_controller.rb b/app/controllers/games_controller.rb index e9620b4..f18a5ad 100644 --- a/app/controllers/games_controller.rb +++ b/app/controllers/games_controller.rb @@ -1,6 +1,4 @@ class GamesController < ApplicationController - before_action :set_game, only: [:show, :edit, :update, :destroy] - # GET /games # GET /games.json def index diff --git a/app/controllers/matches_controller.rb b/app/controllers/matches_controller.rb index 32108d9..31fc9ad 100644 --- a/app/controllers/matches_controller.rb +++ b/app/controllers/matches_controller.rb @@ -1,70 +1,130 @@ class MatchesController < ApplicationController - before_action :set_match, only: [:show, :edit, :update, :destroy] + before_action :set_tournament, only: [:index] # GET /matches # GET /matches.json - def index - @matches = Match.all - end + require 'httparty' + require 'json' - # GET /matches/1 - # GET /matches/1.json - def show - end + def index + @matches = @tournament.matches + end - # GET /matches/new - def new - @match = Match.new - end + def get_riot_info + if signed_in? - # GET /matches/1/edit - def edit - end + #current user information + response = HTTParty.get("https://prod.api.pvp.net/api/lol/na/v1.3/summoner/by-name/#{current_user.user_name}?api_key=ad539f86-22fd-474d-9279-79a7a296ac38") - # POST /matches - # POST /matches.json - def create - @match = Match.new(match_params) + id = response["#{current_user.user_name.downcase}"]['id'] - respond_to do |format| - if @match.save - format.html { redirect_to @match, notice: 'Match was successfully created.' } - format.json { render action: 'show', status: :created, location: @match } - else - format.html { render action: 'new' } - format.json { render json: @match.errors, status: :unprocessable_entity } - end - end - end + #recent game information + recent = HTTParty.get("https://prod.api.pvp.net/api/lol/na/v1.3/game/by-summoner/#{response["#{current_user.user_name.downcase}"]['id']}/recent?api_key=ad539f86-22fd-474d-9279-79a7a296ac38") + + game_id = recent["games"][0]["gameId"] + + #remote_user_id = 6651654651354 + #remove_user_name = TeslasMind + #How to Add + #how do I access + + #members of most recent game id's + player1 = recent["games"][0]["fellowPlayers"][0]["summonerId"] + player2 = recent["games"][0]["fellowPlayers"][1]["summonerId"] + player3 = recent["games"][0]["fellowPlayers"][2]["summonerId"] + player4 = recent["games"][0]["fellowPlayers"][3]["summonerId"] + player5 = recent["games"][0]["fellowPlayers"][4]["summonerId"] + player6 = recent["games"][0]["fellowPlayers"][5]["summonerId"] + player7 = recent["games"][0]["fellowPlayers"][6]["summonerId"] + player8 = recent["games"][0]["fellowPlayers"][7]["summonerId"] + player9 = recent["games"][0]["fellowPlayers"][8]["summonerId"] + + players_by_id = [player1, player2, player3, player4, player5, player6, player7, player8, player9] + + #collect summoner names + memb1 = HTTParty.get("https://prod.api.pvp.net/api/lol/na/v1.3/summoner/#{player1}/name?api_key=ad539f86-22fd-474d-9279-79a7a296ac38") + memb1 = memb1["#{player1}"] + sleep(1); + + memb2 = HTTParty.get("https://prod.api.pvp.net/api/lol/na/v1.3/summoner/#{player2}/name?api_key=ad539f86-22fd-474d-9279-79a7a296ac38") + memb2 = memb2["#{player2}"] + sleep(1); + + memb3 = HTTParty.get("https://prod.api.pvp.net/api/lol/na/v1.3/summoner/#{player3}/name?api_key=ad539f86-22fd-474d-9279-79a7a296ac38") + memb3 = memb3["#{player3}"] + sleep(1); + + memb4 = HTTParty.get("https://prod.api.pvp.net/api/lol/na/v1.3/summoner/#{player4}/name?api_key=ad539f86-22fd-474d-9279-79a7a296ac38") + memb4 = memb4["#{player4}"] + sleep(1); - # PATCH/PUT /matches/1 - # PATCH/PUT /matches/1.json - def update - respond_to do |format| - if @match.update(match_params) - format.html { redirect_to @match, notice: 'Match was successfully updated.' } - format.json { head :no_content } + memb5 = HTTParty.get("https://prod.api.pvp.net/api/lol/na/v1.3/summoner/#{player5}/name?api_key=ad539f86-22fd-474d-9279-79a7a296ac38") + memb5 = memb5["#{player5}"] + sleep(1); + + memb6 = HTTParty.get("https://prod.api.pvp.net/api/lol/na/v1.3/summoner/#{player6}/name?api_key=ad539f86-22fd-474d-9279-79a7a296ac38") + memb6 = memb6["#{player6}"] + sleep(1); + + memb7 = HTTParty.get("https://prod.api.pvp.net/api/lol/na/v1.3/summoner/#{player7}/name?api_key=ad539f86-22fd-474d-9279-79a7a296ac38") + memb7 = memb7["#{player7}"] + sleep(1); + + memb8 = HTTParty.get("https://prod.api.pvp.net/api/lol/na/v1.3/summoner/#{player8}/name?api_key=ad539f86-22fd-474d-9279-79a7a296ac38") + memb8 = memb8["#{player8}"] + sleep(1); + + memb9 = HTTParty.get("https://prod.api.pvp.net/api/lol/na/v1.3/summoner/#{player9}/name?api_key=ad539f86-22fd-474d-9279-79a7a296ac38") + memb9 = memb9["#{player9}"] + sleep(1); + + memb10 = HTTParty.get("https://prod.api.pvp.net/api/lol/na/v1.3/summoner/#{id}/name?api_key=ad539f86-22fd-474d-9279-79a7a296ac38") + memb10 = memb10["#{id}"] + + players = ["#{memb1}", "#{memb2}", "#{memb3}", "#{memb4}", "#{memb5}", "#{memb6}", "#{memb7}", "#{memb8}", "#{memb9}", "#{memb10}"] + + sleep(5); + + blue = Hash.new + purple = Hash.new + + for i in 0..8 + current_player = players_by_id[i] + place = players[i] + info = HTTParty.get("https://prod.api.pvp.net/api/lol/na/v1.3/game/by-summoner/#{current_player}/recent?api_key=ad539f86-22fd-474d-9279-79a7a296ac38") + + if 100 == info["games"][0]["stats"]["team"] + blue.merge!("#{place}" => info["games"][0]["stats"]) + else + purple.merge!("#{place}" => info["games"][0]["stats"]) + end + sleep(1) + end + + if 100 == recent["games"][0]["stats"]["team"] + blue.merge!("#{players[9]}" => recent["games"][0]["stats"]) else - format.html { render action: 'edit' } - format.json { render json: @match.errors, status: :unprocessable_entity } + purple.merge!("#{players[9]}" => recent["games"][0]["stats"]) end - end - end - # DELETE /matches/1 - # DELETE /matches/1.json - def destroy - @match.destroy - respond_to do |format| - format.html { redirect_to matches_url } - format.json { head :no_content } - end + @purp = purple + @blue = blue + + end #end if + end #end def + # GET /matches/1 + # GET /matches/1.json + def show end private # Use callbacks to share common setup or constraints between actions. def set_match - @match = Match.find(params[:id]) + set_tournament + @match = @tournament.matches.find(params[:id]); + end + def set_tournament + @tournament = Tournament.find(params[:tournament_id]) end # Never trust parameters from the scary internet, only allow the white list through. diff --git a/app/controllers/pms_controller.rb b/app/controllers/pms_controller.rb index b62a6ef..af112d1 100644 --- a/app/controllers/pms_controller.rb +++ b/app/controllers/pms_controller.rb @@ -1,6 +1,4 @@ class PmsController < ApplicationController - before_action :set_pm, only: [:show, :edit, :update, :destroy] - # GET /pms # GET /pms.json def index diff --git a/app/controllers/servers_controller.rb b/app/controllers/servers_controller.rb index 43999c4..3f11075 100644 --- a/app/controllers/servers_controller.rb +++ b/app/controllers/servers_controller.rb @@ -1,5 +1,4 @@ class ServersController < ApplicationController - before_action :set_server, only: [:show, :edit, :update, :destroy] # GET /servers # GET /servers.json @@ -67,6 +66,15 @@ class ServersController < ApplicationController @server = Server.find(params[:id]) end + def check_perms + unless (signed_in? and current_user.in_group?(:admin)) + respond_to do |format| + format.html { render action: 'permission_denied', status: :forbidden } + format.json { render json: "Permission denied", status: :forbidden } + end + end + end + # Never trust parameters from the scary internet, only allow the white list through. def server_params params[:server] diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index b035ea0..a0390ad 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -1,52 +1,27 @@ class SessionsController < ApplicationController - before_action :set_session, only: [:show, :edit, :update, :destroy] - - # GET /sessions - # GET /sessions.json - def index - @sessions = Session.all - end - - # GET /sessions/1 - # GET /sessions/1.json - def show - end # GET /sessions/new def new - @session = Session.new - end - - # GET /sessions/1/edit - def edit + @user = User.new + #@session = Session.new end # POST /sessions # POST /sessions.json def create - @session = Session.new(session_params) + # find the user... + @user = User.find_by_email(params[:session][:username_or_email]) || User.find_by_user_name(params[:session][:username_or_email]) + #@session = Session.new(@user) + # ... and create a new session respond_to do |format| - if @session.save - format.html { redirect_to @session, notice: 'Session was successfully created.' } - format.json { render action: 'show', status: :created, location: @session } + if @user && @user.authenticate(params[:session][:password]) + sign_in @user + format.html { redirect_to root_path } + #format.json { #TODO } else format.html { render action: 'new' } - format.json { render json: @session.errors, status: :unprocessable_entity } - end - end - end - - # PATCH/PUT /sessions/1 - # PATCH/PUT /sessions/1.json - def update - respond_to do |format| - if @session.update(session_params) - format.html { redirect_to @session, notice: 'Session was successfully updated.' } - format.json { head :no_content } - else - format.html { render action: 'edit' } - format.json { render json: @session.errors, status: :unprocessable_entity } + format.json { render json: @user.errors, status: :unprocessable_entity } end end end @@ -54,9 +29,10 @@ class SessionsController < ApplicationController # DELETE /sessions/1 # DELETE /sessions/1.json def destroy - @session.destroy + #@session.destroy + sign_out respond_to do |format| - format.html { redirect_to sessions_url } + format.html { redirect_to root_path } format.json { head :no_content } end end @@ -64,11 +40,16 @@ class SessionsController < ApplicationController private # Use callbacks to share common setup or constraints between actions. def set_session - @session = Session.find(params[:id]) + @token = Session.hash_token(cookies[:remember_token]) + @session = Session.find_by(token: @token) end # Never trust parameters from the scary internet, only allow the white list through. def session_params - params.require(:session).permit(:user_id, :token) + params.require(:session).permit(:session_email, :session_user_name, :session_password) + end + + def is_owner?(object) + object.user == current_user end end diff --git a/app/controllers/static_controller.rb b/app/controllers/static_controller.rb index c6df11e..038cc19 100644 --- a/app/controllers/static_controller.rb +++ b/app/controllers/static_controller.rb @@ -1,2 +1,7 @@ class StaticController < ApplicationController + def homepage + end + + def test + end end diff --git a/app/controllers/teams_controller.rb b/app/controllers/teams_controller.rb index 05e7a12..57ae256 100644 --- a/app/controllers/teams_controller.rb +++ b/app/controllers/teams_controller.rb @@ -1,5 +1,4 @@ class TeamsController < ApplicationController - before_action :set_team, only: [:show, :edit, :update, :destroy] # GET /teams # GET /teams.json @@ -71,4 +70,8 @@ class TeamsController < ApplicationController def team_params params.require(:team).permit(:match_id) end + + def is_owner?(object) + object.users.include?(current_user) + end end diff --git a/app/controllers/tournaments_controller.rb b/app/controllers/tournaments_controller.rb index e43976c..d1a926b 100644 --- a/app/controllers/tournaments_controller.rb +++ b/app/controllers/tournaments_controller.rb @@ -1,5 +1,4 @@ class TournamentsController < ApplicationController - before_action :set_tournament, only: [:show, :edit, :update, :destroy] # GET /tournaments # GET /tournaments.json @@ -10,24 +9,42 @@ class TournamentsController < ApplicationController # GET /tournaments/1 # GET /tournaments/1.json def show + respond_to do |format| + format.html { + case @tournament.status + when 0 + render action: 'show' + when 1..2 + redirect_to "/tournaments/" + @tournament.id.to_s + "/matches" #tournament_matches_page(@tournament) + end + } + format.json { + data = JSON.parse(@tournament.to_json) + data["players"] = @tournament.players; + render :json => data.to_json + } + end end # GET /tournaments/new def new - @tournament = Tournament.new + @games = Game.all + @tournament = Tournament.new(game: Game.find_by_id(params[:game])) end # GET /tournaments/1/edit def edit + check_permission(:edit, @tournament) end # POST /tournaments # POST /tournaments.json def create @tournament = Tournament.new(tournament_params) - + @tournament.status = 0 respond_to do |format| if @tournament.save + @tournament.hosts.push(current_user) format.html { redirect_to @tournament, notice: 'Tournament was successfully created.' } format.json { render action: 'show', status: :created, location: @tournament } else @@ -40,12 +57,52 @@ class TournamentsController < ApplicationController # PATCH/PUT /tournaments/1 # PATCH/PUT /tournaments/1.json def update - respond_to do |format| - if @tournament.update(tournament_params) - format.html { redirect_to @tournament, notice: 'Tournament was successfully updated.' } - format.json { head :no_content } - else - format.html { render action: 'edit' } + case params[:update_action] + when nil + check_permission(:edit, @tournament) + respond_to do |format| + if @tournament.update(tournament_params) + format.html { redirect_to @tournament, notice: 'Tournament was successfully updated.' } + format.json { head :no_content } + else + format.html { render action: 'edit' } + format.json { render json: @tournament.errors, status: :unprocessable_entity } + end + end + when "join" + check_permission(:join) + respond_to do |format| + if @tournament.join(current_user) + format.html { redirect_to @tournament, notice: 'You have joined this tournament.' } + format.json { head :no_content } + end + format.html { render action: 'permission_denied', status: :forbidden } + format.json { render json: "Permission denied", status: :forbidden } + end + when "leave" + respond_to do |format| + if @tournament.leave(current_user) + format.html { redirect_to tournaments_url, notice: 'You have left the tournament.' } + format.json { head :no_content } + end + format.html {redirect_to @tournament, notice: 'You were\'t a part of this tournament.' } + format.json { render json: "Permission denied", status: :forbidden } + end + when "start" + check_permission(:edit, @tournament) + @tournament.status = 1 + @tournament.save + respond_to do |format| + if @tournament.setup + format.html { redirect_to @tournament, notice: 'You have joined this tournament.' } + format.json { head :no_content } + end + format.html { render action: 'permission_denied', status: :forbidden } + format.json { render json: "Permission denied", status: :forbidden } + end + else + respond_to do |format| + format.html { redirect_to @tournament, notice: "Invalid action", status: :unprocessable_entity } format.json { render json: @tournament.errors, status: :unprocessable_entity } end end @@ -69,6 +126,14 @@ class TournamentsController < ApplicationController # Never trust parameters from the scary internet, only allow the white list through. def tournament_params - params.require(:tournament).permit(:name, :game_id, :status, :min_players_per_team, :max_players_per_team, :min_teams_per_match, :max_teams_per_match, :set_rounds, :randomized_teams) + params.require(:tournament).permit(:game, :name, :game_id, :status, :min_players_per_team, :max_players_per_team, :min_teams_per_match, :max_teams_per_match, :set_rounds, :randomized_teams) + end + + def is_owner?(object) + object.hosts.include?(current_user) + end + + # Turn of check_edit, since our #update is flexible + def check_edit end end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 58bf4c6..bcb45aa 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -1,7 +1,7 @@ class UsersController < ApplicationController - before_action :set_user, only: [:show, :edit, :update, :destroy] # GET /users + # GET /users.json def index @users = User.all @@ -24,15 +24,17 @@ class UsersController < ApplicationController # POST /users # POST /users.json def create - @user = User.new(user_params) - - respond_to do |format| - if @user.save - format.html { redirect_to @user, notice: 'User was successfully created.' } - format.json { render action: 'show', status: :created, location: @user } - else - format.html { render action: 'new' } - format.json { render json: @user.errors, status: :unprocessable_entity } + if simple_captcha_valid? + @user = User.new(user_params) + respond_to do |format| + if @user.save + sign_in @user + format.html { redirect_to root_path, notice: 'User was successfully created.' } + format.json { render action: 'show', status: :created, location: @user } + else + format.html { render action: 'new', status: :unprocessable_entity } + format.json { render json: @user.errors, status: :unprocessable_entity } + end end end end @@ -67,8 +69,12 @@ class UsersController < ApplicationController @user = User.find(params[:id]) end + def is_owner?(object) + object == current_user + end + # Never trust parameters from the scary internet, only allow the white list through. def user_params - params.require(:user).permit(:name, :email, :user_name) + params.require(:user).permit(:name, :email, :user_name, :password, :password_confirmation) end end diff --git a/app/helpers/sessions_helper.rb b/app/helpers/sessions_helper.rb index 309f8b2..ac62cdc 100644 --- a/app/helpers/sessions_helper.rb +++ b/app/helpers/sessions_helper.rb @@ -1,2 +1,67 @@ +require 'user' + module SessionsHelper + def sign_in(user) + @session = Session.new(user: user) + raw_token = @session.create_token + @session.save # FIXME: error handling + + @token = Session.hash_token(raw_token) + cookies.permanent[:remember_token] = { value: raw_token, expires: 20.minutes.from_now.utc } + + #set the current user to be the given user + @current_user = user + end + + # sets the @current_user instance virable to the user corresponding + # to the remember token, but only if @current_user is undefined + # since the remember token is hashed, we need to hash the cookie + # to find match the remember token + def current_user + @token ||= Session.hash_token(cookies[:remember_token]) + @session ||= Session.find_by(token: @token) + @current_user ||= (@session.nil? ? NilUser.new : @session.user) + end + + # checks if someone is currently signed in + def signed_in? + !current_user.nil? + end + + def sign_out + if signed_in? + @session.destroy + end + @current_user = NilUser.new + cookies.delete(:remember_token) + end + + # This is for anyone that cares about how long a user is signed + # in: + # + # Currently I have a user to be signed in forever unless they + # log out (cookies.permanent....). + # + # If you want to change that, change line 7 to this: + # + # cookies[:remember_token] = { value: remember_token, + # expires: 20.years.from_now.utc } + # + # which will expire the cookie in 20 years from its date of + # creation. + # + # Oddly enough, this line above is equivalent to the: + # + # cookies.permanent + # + # This is just a short cut for this line since most people + # create permanent cookies these days. + # + # Other times are: + # + # 10.weeks.from_now + # + # 5.days.ago + # + # etc... end diff --git a/app/models/alert.rb b/app/models/alert.rb index 0516355..9876711 100644 --- a/app/models/alert.rb +++ b/app/models/alert.rb @@ -1,3 +1,3 @@ class Alert < ActiveRecord::Base - belongs_to :author + belongs_to :author, class_name: "User" end diff --git a/app/models/match.rb b/app/models/match.rb index fe68d31..c596ced 100644 --- a/app/models/match.rb +++ b/app/models/match.rb @@ -1,4 +1,11 @@ class Match < ActiveRecord::Base belongs_to :tournament - belongs_to :winner + + has_and_belongs_to_many :teams + + belongs_to :winner, class_name: "Team" + + def setup() + + end end diff --git a/app/models/session.rb b/app/models/session.rb index a5fd26e..f5e642b 100644 --- a/app/models/session.rb +++ b/app/models/session.rb @@ -1,3 +1,42 @@ class Session < ActiveRecord::Base belongs_to :user + + ## + # Create a random remember token for the user. This will be + # changed every time the user creates a new session. + # + # If you want this value, hang on to it; the raw value is + # discarded afterward. + # + # By changing the cookie every new session, any hijacked sessions + # (where the attacker steals a cookie to sign in as a certain + # user) will expire the next time the user signs back in. + # + # The random string is of length 16 composed of A-Z, a-z, 0-9 + # This is the browser's cookie value. + def create_token() + t = SecureRandom.urlsafe_base64 + self.token = Session.hash_token(t) + t + end + + ## + # Encrypt the remember token. + # This is the encrypted version of the cookie stored on + # the database. + # + # The reasoning for storing a hashed token is so that even if + # the database is compromised, the attacker won't be able to use + # the remember tokens to sign in. + def Session.hash_token(token) + # SHA-1 (Secure Hash Algorithm) is a US engineered hash + # function that produces a 20 byte hash value which typically + # forms a hexadecimal number 40 digits long. + # The reason I am not using the Bcrypt algorithm is because + # SHA-1 is much faster and I will be calling this on + # every page a user accesses. + # + # https://en.wikipedia.org/wiki/SHA-1 + Digest::SHA1.hexdigest(token.to_s) + end end diff --git a/app/models/team.rb b/app/models/team.rb index 8d89f51..7aae7c2 100644 --- a/app/models/team.rb +++ b/app/models/team.rb @@ -1,3 +1,5 @@ class Team < ActiveRecord::Base belongs_to :match + has_and_belongs_to_many :matches + has_and_belongs_to_many :users end diff --git a/app/models/tournament.rb b/app/models/tournament.rb index dcdb8d5..22711b1 100644 --- a/app/models/tournament.rb +++ b/app/models/tournament.rb @@ -1,3 +1,46 @@ class Tournament < ActiveRecord::Base belongs_to :game + has_many :matches + has_and_belongs_to_many :players, class_name: "User", association_foreign_key: "player_id", join_table: "players_tournaments" + has_and_belongs_to_many :hosts, class_name: "User", association_foreign_key: "host_id", join_table: "hosts_tournaments" + + def open? + return true + end + + def joinable_by?(user) + return (open? and user.can?(:join_tournament) and !players.include?(user)) + end + + def join(user) + unless joinable_by?(user) + return false + end + players.push(user) + end + + def leave(user) + if players.include?(user) && status == 0 + players.delete(user) + end + end + + def setup() + num_teams = (self.players.count/self.min_players_per_team).floor + num_matches = num_teams - 1 + for i in 1..num_matches + self.matches.create(name: "Match #{i}", status: 0) + end + match_num = 0 + team_num = 0 + self.players.each_slice(min_players_per_team) do |players| + self.matches[match_num].teams.push(Team.create(users: players)) + if (team_num != 0 and team_num % max_teams_per_match == 0) + match_num += 1 + team_num = 0 + else + team_num += 1 + end + end + end end diff --git a/app/models/user.rb b/app/models/user.rb index 4a57cf0..d1698bd 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,2 +1,109 @@ class User < ActiveRecord::Base + has_and_belongs_to_many :tournaments_played, class_name: "Tournament", foreign_key: "player_id", join_table: "players_tournaments" + has_and_belongs_to_many :tournaments_hosted, class_name: "Tournament", foreign_key: "host_id", join_table: "hosts_tournaments" + has_and_belongs_to_many :teams + has_many :sessions + + apply_simple_captcha + + before_save { self.email = email.downcase } + before_save { self.user_name = user_name } + + def after_initialize + self.permissions = 0 + end + + def can?(action) + case action + when :create_tournament + when :edit_tournament + when :join_tournament + when :delete_tournament + + when :create_game + when :edit_game + when :delete_game + + when :create_user + return false + when :edit_user + when :delete_user + + when :create_alert + when :edit_alert + when :delete_alert + + when :create_pm + when :edit_pm + when :delete_pm + + when :create_session + return false + when :delete_session + + else + return false + end + end + + ## + # VAILD_EMAIL is the regex used to validate a user given email. + VALID_EMAIL_REG = /\A\S+@\S+\.\S+\z/i + + ## + # VALID_USER_NAME checks to make sure a user's user_name + # is in the proper format. + VALID_USER_NAME_REG = /\A[a-zA-Z0-9\-]+\z/ + + ## + # The following lines put a user account through a series of + # validations in order to make sure all of their information + # is in the proper format. + # + # validates :symbol_to_be_validated + # + # - presence: determines whether or not a symbol is filled or not + # - length: ensures there is a length limit on the symbol + # - format: checks the format of given information to ensure + # validity + validates(:name, presence: true, length: { maximum: 50 }) + validates(:email, presence: true, format: {with: + VALID_EMAIL_REG}, + uniqueness: { case_sensitive: false }) + validates(:user_name, presence: true, length:{maximum: 50}, + format: {with: VALID_USER_NAME_REG }, + uniqueness: {case_sensitive: false }) + + ## + # Instead of adding password and password_confirmation + # attributes, requiring the presence of a password, + # requiring that pw and pw_com match, and add an authenticate + # method to compare an encrypted password to the + # password_digest to authenticate users, I can just add + # has_secure_password which does all of this for me. + has_secure_password + + validates :password, length: { minimum: 6 } +end +class NilUser + def nil? + return true + end + def can?(action) + case action + when :create_user + return true + when :create_session + return true + else + return false + end + end + def method_missing(name, *args) + # Throw an error if User doesn't have this method + super unless User.new.respond_to?(name) + # User has this method -- return a blank value + # 'false' if the method ends with '?'; 'nil' otherwise. + name.ends_with?('?') ? false : nil + end end diff --git a/app/views/alerts/show.html.erb b/app/views/alerts/show.html.erb index eeab7f7..5dda2c9 100644 --- a/app/views/alerts/show.html.erb +++ b/app/views/alerts/show.html.erb @@ -1,5 +1,3 @@ -<p id="notice"><%= notice %></p> - <p> <strong>Author:</strong> <%= @alert.author %> diff --git a/app/views/application/permission_denied.html.erb b/app/views/application/permission_denied.html.erb new file mode 100644 index 0000000..1ef883c --- /dev/null +++ b/app/views/application/permission_denied.html.erb @@ -0,0 +1 @@ +<h1>Permission denied</h1> diff --git a/app/views/common/_error_messages.html.erb b/app/views/common/_error_messages.html.erb new file mode 100644 index 0000000..731f62c --- /dev/null +++ b/app/views/common/_error_messages.html.erb @@ -0,0 +1,11 @@ +<%# http://railscasts.com/episodes/211-validations-in-rails-3 %> +<% if target.errors.any? %> +<div id="errorExplanation"> + <h2><%= pluralize(target.errors.count, "error") %> prohibited this form from being submitted:</h2> + <ul> + <% target.errors.full_messages.each do |msg| %> + <li><%= msg %></li> + <% end %> + </ul> +</div> +<% end %> diff --git a/app/views/games/index.html.erb b/app/views/games/index.html.erb index 27c5860..79acd1e 100644 --- a/app/views/games/index.html.erb +++ b/app/views/games/index.html.erb @@ -1,6 +1,6 @@ <h1>Listing games</h1> -<table> +<table class="table table-hover"> <thead> <tr> <th>Name</th> @@ -36,4 +36,6 @@ <br> -<%= link_to 'New Game', new_game_path %> + +<%= link_to 'New Game', new_game_path, {:class => "btn btn-warning"} %> + diff --git a/app/views/games/show.html.erb b/app/views/games/show.html.erb index 1f1a154..39d4a97 100644 --- a/app/views/games/show.html.erb +++ b/app/views/games/show.html.erb @@ -1,5 +1,3 @@ -<p id="notice"><%= notice %></p> - <p> <strong>Name:</strong> <%= @game.name %> diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index cefd1be..976ee85 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -5,10 +5,36 @@ <%= stylesheet_link_tag "application", media: "all", "data-turbolinks-track" => true %> <%= javascript_include_tag "application", "data-turbolinks-track" => true %> <%= csrf_meta_tags %> + <%= yield :head %> </head> <body> +<header><nav> + <div class="navbar-brand"><%= link_to('Leaguer', root_path) %></div> + <div> + <%= form_tag("/search", method: "get", :class => "search") do %> + <%= text_field_tag(:query, nil, :placeholder => "Search") %> + <%= submit_tag("Go") %> + <% end %> + </div> -<%= yield %> + <div id="log-buttons"> + <% if signed_in? %> + <%= link_to current_user.user_name, current_user, :class => "user" %> + <%= link_to "Sign out", session_path("current"), method: "delete", :class => "signout" %> + <% else %> + <%= link_to "Log in", new_session_path, :class => "signin" %> + <%= link_to "Sign up", new_user_path, :class => "signup" %> + <% end %> + </div> +</nav></header> +<% if notice %><div id="notice"><p><%= notice %></p></div><% end %> + +<div class="container"><%= yield %></div> + +<footer> + <p>Leaguer © 2014, Tomer Kimia, Andrew Murrell, Luke Shumaker, Nathaniel Foy, Davis Webb, and Guntas Grewal</p> +</footer> +<%= debug(params) if Rails.env.development? %> </body> </html> diff --git a/app/views/matches/_form.html.erb b/app/views/matches/_form.html.erb index 9fe255c..e2ec73b 100644 --- a/app/views/matches/_form.html.erb +++ b/app/views/matches/_form.html.erb @@ -1,16 +1,5 @@ -<%= form_for(@match) do |f| %> - <% if @match.errors.any? %> - <div id="error_explanation"> - <h2><%= pluralize(@match.errors.count, "error") %> prohibited this match from being saved:</h2> - - <ul> - <% @match.errors.full_messages.each do |msg| %> - <li><%= msg %></li> - <% end %> - </ul> - </div> - <% end %> - +<%= form_for([@tournament, @tournament.matches.build]) do |f| %> + <div class="field"> <%= f.label :status %><br> <%= f.number_field :status %> diff --git a/app/views/matches/index.html.erb b/app/views/matches/index.html.erb index 8d699f9..d4ddb0e 100644 --- a/app/views/matches/index.html.erb +++ b/app/views/matches/index.html.erb @@ -1,30 +1,23 @@ -<h1>Listing matches</h1> +<h1><%= @tournament.name %> - Matches</h1> -<table> + +<table class="table"> <thead> <tr> <th>Status</th> - <th>Tournament</th> <th>Name</th> <th>Winner</th> - <th>Remote</th> - <th></th> - <th></th> <th></th> </tr> </thead> - <tbody> - <% @matches.each do |match| %> + <tbody class="table-hover"> + <% @tournament.matches.each do |match| %> <tr> <td><%= match.status %></td> - <td><%= match.tournament %></td> + <td><%= match.id%></td> <td><%= match.name %></td> - <td><%= match.winner %></td> - <td><%= match.remote_id %></td> - <td><%= link_to 'Show', match %></td> - <td><%= link_to 'Edit', edit_match_path(match) %></td> - <td><%= link_to 'Destroy', match, method: :delete, data: { confirm: 'Are you sure?' } %></td> + <td><%= link_to "Show", tournament_match_path(@tournament, match) %> </tr> <% end %> </tbody> @@ -32,4 +25,17 @@ <br> -<%= link_to 'New Match', new_match_path %> + +<SVG version="1.1" + baseProfile="full" + width="<%= 300 * @matches.count / 2 + 50 %>" height="<%= 200 * @matches.count + 50 %>" + xmlns="http://www.w3.org/2000/svg"> + + <% (1..@matches.count).each do |i| %> + <g class="svg-match"> + <rect rx="10" + + </g> + <% end %> + +</SVG> diff --git a/app/views/matches/new.html.erb b/app/views/matches/new.html.erb index bd4c78c..74e7e3a 100644 --- a/app/views/matches/new.html.erb +++ b/app/views/matches/new.html.erb @@ -1,5 +1,3 @@ <h1>New match</h1> <%= render 'form' %> - -<%= link_to 'Back', matches_path %> diff --git a/app/views/matches/show.html.erb b/app/views/matches/show.html.erb index 1ee7f1d..4973dc3 100644 --- a/app/views/matches/show.html.erb +++ b/app/views/matches/show.html.erb @@ -1,5 +1,3 @@ -<p id="notice"><%= notice %></p> - <p> <strong>Status:</strong> <%= @match.status %> @@ -7,23 +5,49 @@ <p> <strong>Tournament:</strong> - <%= @match.tournament %> + <%= @match.tournament.id %> </p> <p> <strong>Name:</strong> <%= @match.name %> </p> +<!-- + Match Status 0 => Pairings Stage + Match Status 1 => Match Active + Match Status 2 => Match Finished (Peer Review Starts) + Match Status 3 => Match Completed (Scores Completed OR Results Page) -<p> - <strong>Winner:</strong> - <%= @match.winner %> -</p> + Four views:- (status is Match status) + A. Pairings, when status is 0 for either Host or Player Or when status is 1 for player + B. A page the host will see if status is 1 OR 2 + C. The Peer review page that the players will see if status is 2. + D. The page everyone will see when status is 3. -<p> - <strong>Remote:</strong> - <%= @match.remote_id %> -</p> + Note:- The change of status from 1 to 2 is coming from League Data Pull (RIOT API) + +--> +<!-- + This is what the HOST will see when the Match Status is NOT 3 +--> +<% if (@tournament.hosts.include?(current_user) and @match.winner.nil?) %> + <%= form_for([@tournament, @match], method: "put") do |f| %> + <ul> + <% @match.teams.each do |team| %> + <li><label><%= f.radio_button(:winner, team.id) %> + <%= team.users.collect{|u| u.user_name}.join(", ") %></label></li> + <% end %> + </ul> + <%= f.submit("Select winner") %> + <% end %> +<% end %> + + + +<% unless @match.winner.nil? %> + <p> + <strong>Winner:</strong> + <%= @match.winner.users.collect{|u| u.user_name}.join(", ") %> + </p> +<% end %> -<%= link_to 'Edit', edit_match_path(@match) %> | -<%= link_to 'Back', matches_path %> diff --git a/app/views/pms/show.html.erb b/app/views/pms/show.html.erb index 5ee483f..2a03716 100644 --- a/app/views/pms/show.html.erb +++ b/app/views/pms/show.html.erb @@ -1,5 +1,3 @@ -<p id="notice"><%= notice %></p> - <p> <strong>Author:</strong> <%= @pm.author %> diff --git a/app/views/servers/show.html.erb b/app/views/servers/show.html.erb index 039cd8f..67f7647 100644 --- a/app/views/servers/show.html.erb +++ b/app/views/servers/show.html.erb @@ -1,4 +1,2 @@ -<p id="notice"><%= notice %></p> - <%= link_to 'Edit', edit_server_path(@server) %> | <%= link_to 'Back', servers_path %> diff --git a/app/views/sessions/_form.html.erb b/app/views/sessions/_form.html.erb deleted file mode 100644 index 90ad0ad..0000000 --- a/app/views/sessions/_form.html.erb +++ /dev/null @@ -1,25 +0,0 @@ -<%= form_for(@session) do |f| %> - <% if @session.errors.any? %> - <div id="error_explanation"> - <h2><%= pluralize(@session.errors.count, "error") %> prohibited this session from being saved:</h2> - - <ul> - <% @session.errors.full_messages.each do |msg| %> - <li><%= msg %></li> - <% end %> - </ul> - </div> - <% end %> - - <div class="field"> - <%= f.label :user_id %><br> - <%= f.text_field :user_id %> - </div> - <div class="field"> - <%= f.label :token %><br> - <%= f.text_field :token %> - </div> - <div class="actions"> - <%= f.submit %> - </div> -<% end %> diff --git a/app/views/sessions/edit.html.erb b/app/views/sessions/edit.html.erb deleted file mode 100644 index bbd8407..0000000 --- a/app/views/sessions/edit.html.erb +++ /dev/null @@ -1,6 +0,0 @@ -<h1>Editing session</h1> - -<%= render 'form' %> - -<%= link_to 'Show', @session %> | -<%= link_to 'Back', sessions_path %> diff --git a/app/views/sessions/index.html.erb b/app/views/sessions/index.html.erb deleted file mode 100644 index 43a7e1f..0000000 --- a/app/views/sessions/index.html.erb +++ /dev/null @@ -1,29 +0,0 @@ -<h1>Listing sessions</h1> - -<table> - <thead> - <tr> - <th>User</th> - <th>Token</th> - <th></th> - <th></th> - <th></th> - </tr> - </thead> - - <tbody> - <% @sessions.each do |session| %> - <tr> - <td><%= session.user %></td> - <td><%= session.token %></td> - <td><%= link_to 'Show', session %></td> - <td><%= link_to 'Edit', edit_session_path(session) %></td> - <td><%= link_to 'Destroy', session, method: :delete, data: { confirm: 'Are you sure?' } %></td> - </tr> - <% end %> - </tbody> -</table> - -<br> - -<%= link_to 'New Session', new_session_path %> diff --git a/app/views/sessions/index.json.jbuilder b/app/views/sessions/index.json.jbuilder deleted file mode 100644 index 5205ede..0000000 --- a/app/views/sessions/index.json.jbuilder +++ /dev/null @@ -1,4 +0,0 @@ -json.array!(@sessions) do |session| - json.extract! session, :id, :user_id, :token - json.url session_url(session, format: :json) -end diff --git a/app/views/sessions/new.html.erb b/app/views/sessions/new.html.erb index 55c9eca..b4acf77 100644 --- a/app/views/sessions/new.html.erb +++ b/app/views/sessions/new.html.erb @@ -1,5 +1,23 @@ -<h1>New session</h1> +<h1>Sign in</h1> +<% if @user.nil? %> + <p class="errors"> The email/username or password is incorrect. Verify that CAPS LOCK is not on, and then retype the current email/username and password. </p> +<% end %> -<%= render 'form' %> + <div class="span6 offset3"> + <%= form_for(:session, url: sessions_path) do |f| %> + <p> + <%= f.label(:username_or_email, "Username/Email") %><br/> + <%= f.text_field :username_or_email%> + </p> + <p> + <%= f.label :password %><br/> + <%= f.password_field :password %> + </p> + <p> + <%= f.submit "Sign in", class: "btn btn-large btn-primary" %> + </p> + <% end %> + + <p>New user? <%= link_to("Sign up now!", new_user_path) %></p> + </div> -<%= link_to 'Back', sessions_path %> diff --git a/app/views/sessions/show.html.erb b/app/views/sessions/show.html.erb deleted file mode 100644 index 230e6bd..0000000 --- a/app/views/sessions/show.html.erb +++ /dev/null @@ -1,14 +0,0 @@ -<p id="notice"><%= notice %></p> - -<p> - <strong>User:</strong> - <%= @session.user %> -</p> - -<p> - <strong>Token:</strong> - <%= @session.token %> -</p> - -<%= link_to 'Edit', edit_session_path(@session) %> | -<%= link_to 'Back', sessions_path %> diff --git a/app/views/sessions/show.json.jbuilder b/app/views/sessions/show.json.jbuilder deleted file mode 100644 index c9efd3b..0000000 --- a/app/views/sessions/show.json.jbuilder +++ /dev/null @@ -1 +0,0 @@ -json.extract! @session, :id, :user_id, :token, :created_at, :updated_at diff --git a/app/views/static/homepage.html.erb b/app/views/static/homepage.html.erb new file mode 100644 index 0000000..3ca8176 --- /dev/null +++ b/app/views/static/homepage.html.erb @@ -0,0 +1,18 @@ +<div role="main" class="container theme-showcase"> + + <div class="jumbotron"> + <h1>Welcome to Leaguer</h1> + <p>This is a tournment management system designed to be used for any team sport. Our peer review system ensures that the best players move on to the next round! Try creating a new tournament and having people sign up for it. </p> + <p id="jumbo-buttons"> + <% if !signed_in? %> + <%= link_to 'Log In', new_session_path, :class => "btn btn-warning btn-lg", :role => "button" %> + <%= link_to 'Sign Up', new_user_path, :class => "btn btn-warning btn-lg", :role => "button" %> + <% else %> + <%= link_to 'Start a Tournament', new_tournament_path, :class => "btn btn-warning btn-lg", :role => "button" %> + <% end %> + <%= link_to 'See Ongoing Tournaments', tournaments_path, :class => "btn btn-warning btn-lg", :role => "button" %> + + </p> + </div> + + </div> diff --git a/app/views/tournaments/_selected.html.erb b/app/views/tournaments/_selected.html.erb new file mode 100644 index 0000000..8a704b5 --- /dev/null +++ b/app/views/tournaments/_selected.html.erb @@ -0,0 +1,21 @@ +<%= form_for(@tournament) do |f| %> + <%= render "common/error_messages", :target => @tournament %> + <%= f.hidden_field(:game_id) %> + + <% @chosen = Game.find_by(params[:game]) %> + <% @tournament.attributes.each do |name, value| %> + <% if (name == "id") or (name =~ /.*_at$/) or (name == "game_id") or (name == "status") or (name == "set_rounds") %> + <% next %> + <% end %> + <p> + <%= f.label name %><br> + <% if !@chosen.attributes[name].nil? %> + <%= f.text_field(name, :value => @chosen.attributes[name] ) %> + <% else %> + <%= f.text_field name %> + <% end %> + </p> + <% end %> + + <%= f.submit %> +<% end %> diff --git a/app/views/tournaments/index.html.erb b/app/views/tournaments/index.html.erb index f8f21e7..e174de7 100644 --- a/app/views/tournaments/index.html.erb +++ b/app/views/tournaments/index.html.erb @@ -1,43 +1,52 @@ -<h1>Listing tournaments</h1> +<h1>Listing Tournaments</h1> -<table> - <thead> - <tr> - <th>Name</th> - <th>Game</th> - <th>Status</th> - <th>Min players per team</th> - <th>Max players per team</th> - <th>Min teams per match</th> - <th>Max teams per match</th> - <th>Set rounds</th> - <th>Randomized teams</th> - <th></th> - <th></th> - <th></th> - </tr> - </thead> +<% if @tournaments.length > 0 %> + <div id="tournament-list"> + + <%# Each tournament has a div for its listing %> + <% @tournaments.each do |t| %> + <div class="row tournament-listing"> + <div class="col-md-2 "><%= image_tag 'http://www.gravatar.com/avatar/' + Digest::MD5.hexdigest(t.hosts.first.email) + '?s=100&d=mm' %></div> + - <tbody> - <% @tournaments.each do |tournament| %> - <tr> - <td><%= tournament.name %></td> - <td><%= tournament.game %></td> - <td><%= tournament.status %></td> - <td><%= tournament.min_players_per_team %></td> - <td><%= tournament.max_players_per_team %></td> - <td><%= tournament.min_teams_per_match %></td> - <td><%= tournament.max_teams_per_match %></td> - <td><%= tournament.set_rounds %></td> - <td><%= tournament.randomized_teams %></td> - <td><%= link_to 'Show', tournament %></td> - <td><%= link_to 'Edit', edit_tournament_path(tournament) %></td> - <td><%= link_to 'Destroy', tournament, method: :delete, data: { confirm: 'Are you sure?' } %></td> - </tr> + <div class="col-md-8"> + <%# "header" %> + <%= link_to(t) do %> + <h3><%= t.name %></h3> + <% end %> + + <div class="row"> + <div class="col-md-4 host"> + Hosted by: <%= t.hosts.first.name %> + </div> + <div class="col-md-8 things"> + <p> Players per team </p> + <p> two </p> + </div> + </div> + + </div> + + + <div class="col-md-2"> + <%# If this guy is logged in AND not in the tournament %> + <% if signed_in? && !t.players.include?(current_user) %> + <%= form_tag(tournament_path(t), method: "put") do %> + <input type="hidden" name="update_action" value="join"> + <%= submit_tag("Join") %> + <% end %> + <% else %> + <p> You've signed up for this tournament! </p> + <% end %> + </div> + + </div> <% end %> - </tbody> -</table> + </div> +<% else %> + <p class="no-entries"> No tournaments going on right now... Why not start your own? </p> +<% end %> <br> -<%= link_to 'New Tournament', new_tournament_path %> +<%= link_to 'New Tournament', new_tournament_path, :class => "btn btn-warning btn-lg" %> diff --git a/app/views/tournaments/join.html.erb b/app/views/tournaments/join.html.erb new file mode 100644 index 0000000..1d38d68 --- /dev/null +++ b/app/views/tournaments/join.html.erb @@ -0,0 +1,2 @@ + <%= @user.name %> + diff --git a/app/views/tournaments/new.html.erb b/app/views/tournaments/new.html.erb index 2a60539..8c74068 100644 --- a/app/views/tournaments/new.html.erb +++ b/app/views/tournaments/new.html.erb @@ -1,5 +1,16 @@ -<h1>New tournament</h1> +<h1>New Tournament</h1> -<%= render 'form' %> +<%= form_tag(new_tournament_path, method: "get") do %> + <%= select_tag('game', + options_from_collection_for_select(@games, 'id', 'name', @tournament.game.nil? || @tournament.game.id), + :prompt => "Select a Game Type") %> + <%= submit_tag("Select", :class => "btn btn-success btn-xs") %> +<% end %> + +<div id='ajax-form'> + <% if not @tournament.game.nil? %> + <%= render 'selected' %> + <% end %> +</div> <%= link_to 'Back', tournaments_path %> diff --git a/app/views/tournaments/show.html.erb b/app/views/tournaments/show.html.erb index 3cb6179..b654804 100644 --- a/app/views/tournaments/show.html.erb +++ b/app/views/tournaments/show.html.erb @@ -1,18 +1,23 @@ -<p id="notice"><%= notice %></p> - -<p> - <strong>Name:</strong> +<h2 id="tournament-name"> <%= @tournament.name %> -</p> +</h2> -<p> - <strong>Game:</strong> - <%= @tournament.game %> -</p> +<div class="progress"> + <%= tag("div", {:id => "prog-bar", :class => "progress-bar progress-bar-warning", :style => "width: " +(@tournament.players.count * 100 / (@tournament.min_players_per_team * @tournament.min_teams_per_match)).to_s + "%", "aria-valuemax" => "100", "aria-valuemin" => "0", "aria-valuenow" => (@tournament.players.count * 100 / (@tournament.min_players_per_team * @tournament.min_teams_per_match)).to_s, "role" => "progressbar"}) %> + <span class="sr-only">60% Complete (warning)</span> + </div> +</div> +<p id="players-needed"><%= pluralize(@tournament.players.count, "player has", "players have") %> signed up. <%= @tournament.min_players_per_team * @tournament.min_teams_per_match %> needed. </p> + +<span id="tournament-side-params"> <p> <strong>Status:</strong> - <%= @tournament.status %> + <% if @tournament.status == 0 %> + Waiting for players... + <% else %> + Started + <% end %> </p> <p> @@ -35,15 +40,89 @@ <%= @tournament.max_teams_per_match %> </p> -<p> - <strong>Set rounds:</strong> - <%= @tournament.set_rounds %> -</p> <p> <strong>Randomized teams:</strong> <%= @tournament.randomized_teams %> </p> -<%= link_to 'Edit', edit_tournament_path(@tournament) %> | -<%= link_to 'Back', tournaments_path %> +</span> + +<div > +<%# Show all players in the tournament %> +<% if @tournament.players.length > 0 %> +<h3> Players Here: </h3> + <ul id="tournament-users"> + <% @tournament.players.each do |p| %> + <li><span class="black"> <%= p.user_name %> </span> </li> + <% end %> + </ul> + <% else %> + <h3 div="players-needed">Hmmm.... nobody's here yet! You and your friends should join the tournament.</h3> +<% end %> + +<div class="leave-buttons"> +<%# If user can join, and user hasn't joined already, show the join tournment tag %> +<% if @tournament.joinable_by?(current_user) && !@tournament.players.include?(current_user) %> + <%= form_tag(tournament_path(@tournament), method: "put") do %> + <input type="hidden" name="update_action" value="join"> + <%= submit_tag("Join Tournamnet") %> + <% end %> + +<% elsif @tournament.players.include?(current_user) %> + <%= form_tag(tournament_path(@tournament), method: "put") do %> + <input type="hidden" name="update_action" value="leave"> + <%= submit_tag("Leave Tournamnet") %> + <% end %> +<% end %> + +<%# If user is the host, let them start the tournment %> +<% if @tournament.hosts.include?(current_user) %> + + <%= form_tag(tournament_path(@tournament), method: "put") do %> + <input type="hidden" name="update_action" value="start"> + <% if @tournament.players.count >= @tournament.min_players_per_team * @tournament.min_teams_per_match %> + <%= submit_tag("Start Tournament") %> + <% else %> + <%= submit_tag("Start Tournament", disabled: true) %> + <% end %> + <br /> + <%= link_to 'Edit', edit_tournament_path(@tournament) %> | + <%= link_to 'Back', tournaments_path %> | + <%= link_to 'Cancel Tournament', @tournament, method: :delete, data: { confirm: 'Are you sure?' } %> + <% end %> +</div> + +<%end %> +</div> + +<script> +function donehandle( tournament ) { + if ( console && console.log ) { + var here = tournament["players"].length; + var needed = (tournament["min_teams_per_match"] * tournament["min_players_per_team"]); + var pct_complete = here / needed; + $("#prog-bar").width( (pct_complete * 100) +"%"); + $("#players-needed").text(here + " " + (here==1?"player has":"players have") + " signed up. " + needed + " players needed. "); + players = ""; + + //creates the present user list + for (var i = 0; i < tournament["players"].length; i++) { + players = players+"<li><span class=\"black\">"+tournament["players"][i]["user_name"]+"</span></li>" + } + + //updates the user list + $("#tournament-users").html(players); + + //if there are enough players to start, enable the button, else disable it. + $("input[value=\"Start Tournament\"]").prop('disabled', (pct_complete >= 1)? false : true); + + if (tournament["status"] == 1) + window.location.reload(true); + } + setTimeout(function(){$.ajax({url: "<%= url_for @tournament %>.json"}).done(donehandle)}, 2000); +} + +$.ajax({url: "<%= url_for @tournament %>.json"}) + .done(donehandle); +</script> diff --git a/app/views/users/_form.html.erb b/app/views/users/_form.html.erb index 4d28738..ae63f06 100644 --- a/app/views/users/_form.html.erb +++ b/app/views/users/_form.html.erb @@ -23,6 +23,14 @@ <%= f.label :user_name %><br> <%= f.text_field :user_name %> </div> + <p> + <%= f.label(:password, "New Password (or use old)") %><br> + <%= f.password_field :password %> + </p> + <div> + <%= f.label(:password_confirmation, "Confirm Password") %><br> + <%= f.password_field :password_confirmation %> + </div> <div class="actions"> <%= f.submit %> </div> diff --git a/app/views/users/already_signed_in.html.erb b/app/views/users/already_signed_in.html.erb new file mode 100644 index 0000000..04b4248 --- /dev/null +++ b/app/views/users/already_signed_in.html.erb @@ -0,0 +1 @@ +<h1>You are currently signed in</h1> diff --git a/app/views/users/edit.html.erb b/app/views/users/edit.html.erb index 99bd4cc..52f32a2 100644 --- a/app/views/users/edit.html.erb +++ b/app/views/users/edit.html.erb @@ -3,4 +3,4 @@ <%= render 'form' %> <%= link_to 'Show', @user %> | -<%= link_to 'Back', users_path %> +<%= link_to 'Users', users_path %> diff --git a/app/views/users/index.html.erb b/app/views/users/index.html.erb index 3692112..89e369a 100644 --- a/app/views/users/index.html.erb +++ b/app/views/users/index.html.erb @@ -1,8 +1,9 @@ <h1>Listing users</h1> -<table> +<table class="table table-hover"> <thead> <tr> + <th>Username</th> <th>Name</th> <th>Email</th> <th>User name</th> @@ -15,8 +16,9 @@ <tbody> <% @users.each do |user| %> <tr> + <td><%= link_to("#{user.user_name}", user, nil) %></td> <td><%= user.name %></td> - <td><%= user.email %></td> + <td> ******* </td> <td><%= user.user_name %></td> <td><%= link_to 'Show', user %></td> <td><%= link_to 'Edit', edit_user_path(user) %></td> diff --git a/app/views/users/new.html.erb b/app/views/users/new.html.erb index efc0404..5e369ac 100644 --- a/app/views/users/new.html.erb +++ b/app/views/users/new.html.erb @@ -1,5 +1,34 @@ -<h1>New user</h1> +<h1> Sign Up </h1> -<%= render 'form' %> +<%= form_for @user do |f| %> + <%= render "common/error_messages", :target => @user %> + <p> + <%= f.label :name %><br> + <%= f.text_field :name %> + </p> + <p> + <%= f.label :email %><br> + <%= f.text_field :email %> + </p> + <p> + <%= f.label :user_name %><br> + <%= f.text_field :user_name %> + </p> + <p> + <%= f.label :password %><br> + <%= f.password_field :password %> + </p> + <p> + <%= f.label(:password_confirmation, "Confirm Password") %><br> + <%= f.password_field :password_confirmation %> + </p> + <p> + <%= show_simple_captcha %> + + <%= f.submit("Be a Leaguer", :class => "signup") %> + </p> +<% end %> + + +<%= link_to 'Already Have an Account? Log in', new_session_path, :class => "signin" %> -<%= link_to 'Back', users_path %> diff --git a/app/views/users/show.html.erb b/app/views/users/show.html.erb index 9455a3c..7bda009 100644 --- a/app/views/users/show.html.erb +++ b/app/views/users/show.html.erb @@ -1,4 +1,6 @@ -<p id="notice"><%= notice %></p> +<p> + <%= image_tag 'http://www.gravatar.com/avatar/' + Digest::MD5.hexdigest(@user.email) + '?s=100&d=mm' %> +</p> <p> <strong>Name:</strong> @@ -16,4 +18,4 @@ </p> <%= link_to 'Edit', edit_user_path(@user) %> | -<%= link_to 'Back', users_path %> +<%= link_to 'Users', users_path %> diff --git a/config/locales/en.yml b/config/locales/en.yml index 0653957..9b7f013 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -21,3 +21,11 @@ en: hello: "Hello world" + + simple_captcha: + placeholder: "Enter the image value" + label: "Enter the code in the box:" + + message: + default: "Secret Code did not match with the Image" + user: "The secret Image and code were different" diff --git a/config/routes.rb b/config/routes.rb index 97e82f8..3a23a68 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,21 +1,25 @@ Leaguer::Application.routes.draw do - resources :sessions + resources :sessions, only: [:new, :create, :destroy] resources :users resources :games - resources :tournaments - resources :pms resources :alerts - resources :teams + resource :server, only: [:show, :edit, :update] - resources :matches + #match 'simple_captcha/:id', :to => 'simple_captcha#show', :as => :simple_captcha + + resources :teams + resources :tournaments do + resources :matches, only: [:index, :show] + end - resources :servers + root to: 'static#homepage' + get '/testsvg', to: 'static#test' # The priority is based upon order of creation: first created -> highest priority. # See how all your routes lay out with "rake routes". diff --git a/db/seeds.rb b/db/seeds.rb index 4edb1e8..22f1595 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -5,3 +5,15 @@ # # cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }]) # Mayor.create(name: 'Emanuel', city: cities.first) +# +Game.create(name: "League of Legends",min_players_per_team: 5, max_players_per_team: 5, min_teams_per_match: 2, max_teams_per_match: 2, set_rounds: nil, randomized_teams: true) +Game.create(name: "Chess", min_players_per_team: 1, max_players_per_team: 1, min_teams_per_match: 2, max_teams_per_match: 2, set_rounds: nil, randomized_teams: true) +Game.create(name: "Hearthstone", min_players_per_team: 1, max_players_per_team: 1, min_teams_per_match: 2, max_teams_per_match: 2, set_rounds: 1, randomized_teams: false) + + +GameSetting.create(game_id: 0, name: "Map", default: "Summoners Rift,Twisted Treeline,Crystal Scar,Haunted Abyss", discription: "Select a map to play on.", type_opt: "Select", display_order: 1) +GameSetting.create(game_id: 0, name: "Pick Type", default: "Blind Pick,Draft", discription: "Select a pick type.", type_opt: "Select", display_order: 2) + +#Game_setting.create(game_id: , type: , name: , default: , discription: , type_opt: , display_order: , created_at: , updated_at: ) + +#User.create(id: 1, name: "Andrew", email: "amurrel@example.com", user_name: "ImFromNASA", created_at: "2014-03-07 22:48:53", updated_at: "2014-03-07 22:48:53", password_digest: "$2a$10$W0FwPRDzdQp8arolCHGt5ezXqkOiTLNsXI5GKGtu9qr3...", remember_token: "1583722b02663e024533296171cad79f29037ebc", groups: 0) diff --git a/doc/Sprint1-Retrospective.md b/doc/Sprint1-Retrospective.md new file mode 100644 index 0000000..7bffde7 --- /dev/null +++ b/doc/Sprint1-Retrospective.md @@ -0,0 +1,220 @@ +--- +title: "Team 6 - Project Leaguer: Sprint 1 Retrospective" +author: [ Nathaniel Foy, Guntas Grewal, Tomer Kimia, Andrew Murrell, Luke Shumaker, Davis Webb ] +--- + +# User Stories + +1) As an administrator, I would like to install and boot my own server. + - Alternately: As a developer, I would like a demo/testing server, + with a basic Rails setup. +2) As a host/player, I would like to register and have an account. + - For this task, we will be creating the user registration and log + in capabilities for Leaguer. +3) As a host, I would like to start a tournament. + - For this task, we will be creating a base tournament system for a + host to run. +4) As a host/player, I would like to enter scores for players. + - For sprint own, the scores will be entered by hand. +5) As an administrator, I want to specify how users become hosts. +6) As a user I would like to see the progress of the tournament in my + browser. +7) As a user, I would like a presentable homepage. + - For this task, we will be creating a Leaguer homepage and ensure that it + is pleasing to the eye and easy to navigate. + +# Tasks + +The "size" is using the modified Fibonacci scale. A '1' is expected +to take less than an hour. A '3' is expected to take 3-6 hours. A +'5' should take the better part of a day or two. An 8 should take +several days. + ++---------------------------------------------------------+------+--------+----+ +| Tasks Implemented and Working | Size | Person | US | ++=========================================================+======+========+====+ +| [Learn Rails, set up Scaffolding for all Models, Views, | 8 | All | 1 | +| Controllers](#learn-rails) | | | | ++---------------------------------------------------------+------+--------+----+ +| [Deploy rails on Luke's server](#deploy-rails) | 3 | Luke | 1 | ++---------------------------------------------------------+------+--------+----+ +| [Create log-in system back-end (verification, cookies, | 5 | Davis | 2 | +| and redirection)](#login-backend) | | | | ++---------------------------------------------------------+------+--------+----+ +| [Create log-in system UI](#login-ui) | 2 | Tomer | 2 | ++---------------------------------------------------------+------+--------+----+ +| [Create Tournament Settings Page](#tourney-settings) | 3 | Guntas | 3 | ++---------------------------------------------------------+------+--------+----+ +| [Implement Tournament Registration and Tournament | 2 | Andrew | 3 | +| Controller](#tourney-registration) | | | | ++---------------------------------------------------------+------+--------+----+ +| [Implement match controller](#match-controller) | 3 | Dav+And| 4 | ++---------------------------------------------------------+------+--------+----+ +| [Implement permissions system over the users | 3 | Luke | 5 | +| system](#permissions) | | | | ++---------------------------------------------------------+------+--------+----+ +| [Create View Tournament Page](#tourney-view) | 5 | All | 6 | ++---------------------------------------------------------+------+--------+----+ +| [Create Presentable Homepage](#homepage) | 5 | Guntas | 7 | ++---------------------------------------------------------+------+--------+----+ + + ++---------------------------------------------------------+------+--------+----+ +| Tasks Implemented and Not Working Well | Size | Person | US | ++=========================================================+======+========+====+ +| [Design and implement match score models](#match-score) | 3 | Foy | 4 | ++---------------------------------------------------------+------+--------+----+ +| [Create Admin-level Server Management Page](#srv-man) | 2 | Luke | 5 | ++---------------------------------------------------------+------+--------+----+ + + ++---------------------------------------------------------+------+--------+----+ +| Tasks Not Implemented | Size | Person | US | ++=========================================================+======+========+====+ +| [Design/Code Scoring/Pairing Algorithms and | 5 | Foy | 3 | +| Procedures](#score-algo) | | | | ++---------------------------------------------------------+------+--------+----+ +| [Observe Foy Design/Code Scoring/Pairing | 2 | Dav+Foy| 3 | +| Algorithms](#score-algo) | | | | ++---------------------------------------------------------+------+--------+----+ +| [Create a Player-level Data Entry Page/Method for | 3 | Tomer | 4 | +| Results](#data-entry) | | | | ++---------------------------------------------------------+------+--------+----+ + +# Implemented and working + +## Learn Rails {#learn-rails} + +Learning Rails has been a growing experience for the majority of the +team. Some of us coming from no significant experience to being able +to put together a relatively functional product in only three weeks +has been an impressive journey. + +## Deploy Rails {#deploy-rails} + +The entire team became familiar with deploying Rails in our rather +diverse working environments and successfully deployed a server +instance located at demo.projectleaguer.net as well as on our local boxes. + +## Login (back-end) {#login-backend} + +Our login back-end successfully logs users in and our and can handle +user registrations and first-come-first-serve uniqueness validation. + +## Login (UI) {#login-ui} + +Our login user interface successfully differentiates between logged in +and logged out users as well as between users of different +designations (although for the demo some of the hooks were not in +place, this has been fixed). + +## Tournament settings {#tourney-settings} + +Tournament settings were implemented at a basic level, instituting those +items which are similar to all tournaments, regardless of type, orginating +from the game model. + +## Tournament registration {#tourney-registration} + +Tournament registration and the tournament contoller were completed which +allowed users to join and participate in basic tournaments of several types. +The tournament controller handled a variety of tournament related tasks, +including creating and updating tournaments and validating tournament related +operations. + +## Match controller {#match-controller} + +The Match Controller creates the separate matches for a specific tournament. +When a tournament is started, it begins with an initial match that contains +no players. Currently, a player must join a match by entering the specific +tournament (by clicking the 'show' button on the tournament), +then they must enter the match (again by clicking the 'show' button but this +time on the match they desire to participate in) and then finally clicking +the 'join' button. This updates the match with the user as a participant in +the matc and then finally clicking the 'join' button. This updates the match +with the user as a participant in the match. A match can also be destroyed +by clicking the 'delete' button on the no longer desired match on the page. + +## Permissions system {#permissions} + +The permissions system is implemented, easy to use, and works well. +In some places, it appears to be broken (overly-permisive), but this +is because the relevant page doesn't hook into the permission system. +This needs to be fixed with unit tests. + +## Tournament view {#tourney-view} +The view page for tournaments contains a table that lists all on going +tournaments for all types of games. It also lists other game attribute like +Players per team, Teams per match, whether or not teams were randomized +and also has links to Show, Edit, Destroy, Join a particular tournament. +A link to create a tournament is also provided. Each of the links correspond +to view pages which are html.erb pages that provide styles and functionality +of each of the tournament setting features. Show, Edit, Destroy, all have +views of their own to perform each of the above actions. + +## Homepage {#homepage} +The homepage is mainly controlled by the views that are associated with each +model and controller. The main view for the homepage is the one found in the +layouts called application.html.erb, this view is responsible for display of +all the headings, navigation bars, forms and containers. This view page +contains mostly links to other view pages and yields whatever the other view +pages have to offer. The Homepage redirects to Login, Signup, See Ongoing +Tournaments and shows the view for those models. + + +# Implemented but not working well + +## Match score models {#match-score} + +This only functioned properly for noting which team would win a match. We want +more information to be included, such as individual player scores. We also +only had it working where the tournament host would decide who won. + +## Server management {#srv-man} + +The server management software interface is implemented, and working +fine. The other modules use it. However, what we didn't implement is +an actual *page* to edit these settings. We had this task in the +iteration because other items depended on it. Though we did not +implement the full story, we implemented the core reason that we +wanted it. + +# Not implemented + +## Scoring Algorithms {#score-algo} + +Scoring algorithms was not implemented because we did not have time for +implementing player statistics in the first sprint. There were some +preliminary approaches, but the task lost priority and was abandoned. + +## Data entry {#data-entry} + +It was decided to not be a priority for sprint one due to time constraints. +Also, we want to implement data entry for League of Legends through +Riot Games (TM)'s API for grabbing match data. + +# How to improve + +Peer reviews and testing were our biggest pitfalls. + + +1. All testing was just manual, in-browser testing, rather than unit + tests. We really need write unit tests this iteration, as we had + breakages where we said "this is exactly why we need unit + testing." However, that happened late enough in the iteration that + we didn't have time to do anything about it. + +2. That leads us into time management. Our commit activity plotted + against time has humps each weak, each growing a little. That is, + we started slow, and ended with a lot of work. This wasn't exactly + poor planing, but we had a poor idea of how much time things would + take. We plan to fix this by front-loading this iteration instead + of back-loading it. + +3. We had the approach of "show everyone everything" with peer + reviews, as we anticipated that this would be nescessary for + everyone learning Rails. However, in effect it meant that + sometimes information was spread very thin, or because things were + being done "in the open", we didn't ever explicitly review them. + We plan on fixing this next iteration by committing to do very + specific peer reviews with just a couple members of the team. diff --git a/doc/Sprint2.md b/doc/Sprint2.md new file mode 100644 index 0000000..a823600 --- /dev/null +++ b/doc/Sprint2.md @@ -0,0 +1,103 @@ +--- +title: "Team 6 - Project Leaguer: Sprint 2" +author: [ Nathaniel Foy, Guntas Grewal, Tomer Kimia, Andrew Murrell, Luke Shumaker, Davis Webb ] +--- + +# User Stories + +1) As an admin, I would like hosts/players, to have only the options + their group entitles them to. + +2) As an admin, I would like anti-spam measures for registration. + +3) As a player I would like to review my peers and have our + scores reflect these reviews. + +4) As a host I would like to have both game-type specific settings and + tournament specific preferences available when creating a new + tournament. + - These settings and preferences were moved to sprint 2 from sprint 1 + because we did not have the API functionality for any specific game yet. + +5) As a host/player/spectator I would like to have Riot Games League + of Legends API integration for match and player statistics and results for + League of Legends tournaments. + +6) As a host/player, I would like my pages to actively update without + refreshing my current page. + - For this task, we will implement an Active Status Update system with AJAX. + +7) As a host/player, I would like to see an interactive tournament lobby page + that displays tournament and match information. + - This will be accomplished with dynamic graphs, trees, and status bars. + +8) As a host/player, I would like the Leaguer application to be more intuitive + and easy to use. + +9) As a user, I would like past tournament and player information to be + persistent and search-able. + - A working search utility should be implemented that will find specific + players or tournaments and return their pages. + +10) As a user, I would like to see Player Profile pages. + - For this task, we will be creating profile pages for registered users that + have player-specific information such as tournament history and activity. + +# Tasks + +The "size" is using the modified Fibonacci scale. A '1' is expected +to take less than an hour. A '3' is expected to take 3-6 hours. A +'5' should take the better part of a day or two. An 8 should take +several days. + ++---------------------------------------------------------+------+------------+----+ +| Task Description | Size | Person\* | US | ++=========================================================+======+============+====+ +| Define Specific Unit Tests for Security | 3 | All | 1 | ++---------------------------------------------------------+------+------------+----+ +| Implement Anti-spam measures | 2 | Davis | 2 | ++---------------------------------------------------------+------+------------+----+ +| Email Verification Option | 5 | Luke | 2 | ++---------------------------------------------------------+------+------------+----+ +| Implement Teammate Rating System (peer review view) | 5 | Guntas | 3 | ++---------------------------------------------------------+------+------------+----+ +| Design/Code Scoring/Pairing Algorithms and Procedures | 5 | D+F+A | 3 | ++---------------------------------------------------------+------+------------+----+ +| Implement game-type specific and tournament | 8 | L+A+G | 4 | +| specific settings and preferences | | | | ++---------------------------------------------------------+------+------------+----+ +| Retrieve data from Riot Games (TM) API | 3 | Foy | 5 | ++---------------------------------------------------------+------+------------+----+ +| Parse Riot data and attach to scoring subsystem | 5 | Davis | 5 | ++---------------------------------------------------------+------+------------+----+ +| Teach Andrew and Tomer AJAX | 2 | Luke | 6 | ++---------------------------------------------------------+------+------------+----+ +| Make pages auto-update with AJAX | 5 | T+A | 6 | ++---------------------------------------------------------+------+------------+----+ +| Setting up a Tournament View for matches and tree | 5 | Tomer | 7 | ++---------------------------------------------------------+------+------------+----+ +| Increase Usability | 3 | All-L | 8 | ++---------------------------------------------------------+------+------------+----+ +| Project Leaguer Logo | spike| D+G | 8 | ++---------------------------------------------------------+------+------------+----+ +| Develop comprehensive data storage for s&p&other | 5 | L+A | 9 | ++---------------------------------------------------------+------+------------+----+ +| Create Player Profile Pages | 2 | Tomer | 10 | ++---------------------------------------------------------+------+------------+----+ +| Gravitar Integration | 2 | Foy | 10 | ++---------------------------------------------------------+------+------------+----+ +| Test it | 1 | All-L | all| ++---------------------------------------------------------+------+------------+----+ +| Peer review | 1 | All | all| ++---------------------------------------------------------+------+------------+----+ + +Total Size of Iteration: 55 + + D = Davis = 10 + A = Andrew = 10 + F = Nathaniel = 10 + G = Guntas = 10 + L = Luke = 11 + T = Tomer = 10 + +\* `+` means those members work together, `-` means exclude following members @@ -1,5 +1,4 @@ #!/bin/bash -generate.sh -nohup bundle exec rails server & +bundle exec rails server --daemon @@ -0,0 +1,3 @@ +#!/bin/bash + +kill $(<tmp/pids/server.pid) |