8 Commits

Author SHA1 Message Date
Gregory Ballantine
e1f5bd3950 Updating to Sinatra 4.1
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2025-08-12 14:27:22 -04:00
Gregory Ballantine
1f0c481105 Refactored app to more explicitly require gems/modules that are used per-file
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2025-08-12 14:12:32 -04:00
Gregory Ballantine
dd8e419e52 Switched over to a modular Sinatra app layout
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2025-08-12 13:54:25 -04:00
Gregory Ballantine
c74ca114d8 Fixed a logic error with removing benchmarks from a test; cleaned up some linter errors
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2025-08-12 12:19:25 -04:00
0a1037e79a Modified the front-end to display averaged results up to two decimals
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2025-08-11 23:39:01 -04:00
Gregory Ballantine
bc5ae4962f Fixed the placeholder for benchmark add/edit pages
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2025-07-31 14:07:58 -04:00
Gregory Ballantine
ec2bf45a6e Fixed the test edit page
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2025-07-31 14:06:08 -04:00
57163b10e4 The report test selection resets when you change the benchmark
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2025-07-30 23:29:53 -04:00
22 changed files with 159 additions and 89 deletions

View File

@@ -1,7 +1,7 @@
source 'https://rubygems.org'
gem 'sinatra', '~> 3.2'
gem 'sinatra-contrib', '~> 3.2'
gem 'sinatra', '~> 4.1'
gem 'sinatra-contrib', '~> 4.1'
gem 'puma', '~> 6.6'
gem 'sequel', '~> 5.92'

View File

@@ -33,10 +33,14 @@ GEM
puma (6.6.0)
nio4r (~> 2.0)
racc (1.8.1)
rack (2.2.17)
rack-protection (3.2.0)
rack (3.2.0)
rack-protection (4.1.1)
base64 (>= 0.1.0)
rack (~> 2.2, >= 2.2.4)
logger (>= 1.6.0)
rack (>= 3.0.0, < 4)
rack-session (2.1.1)
base64 (>= 0.1.0)
rack (>= 3.0.0)
rainbow (3.1.1)
rb-fsevent (0.11.2)
rb-inotify (0.11.1)
@@ -65,16 +69,18 @@ GEM
ruby2_keywords (0.0.5)
sequel (5.93.0)
bigdecimal
sinatra (3.2.0)
sinatra (4.1.1)
logger (>= 1.6.0)
mustermann (~> 3.0)
rack (~> 2.2, >= 2.2.4)
rack-protection (= 3.2.0)
rack (>= 3.0.0, < 4)
rack-protection (= 4.1.1)
rack-session (>= 2.0.0, < 3)
tilt (~> 2.0)
sinatra-contrib (3.2.0)
sinatra-contrib (4.1.1)
multi_json (>= 0.0.2)
mustermann (~> 3.0)
rack-protection (= 3.2.0)
sinatra (= 3.2.0)
rack-protection (= 4.1.1)
sinatra (= 4.1.1)
tilt (~> 2.0)
sqlite3 (2.7.0-aarch64-linux-gnu)
sqlite3 (2.7.0-aarch64-linux-musl)
@@ -110,8 +116,8 @@ DEPENDENCIES
rubocop
rubocop-sequel
sequel (~> 5.92)
sinatra (~> 3.2)
sinatra-contrib (~> 3.2)
sinatra (~> 4.1)
sinatra-contrib (~> 4.1)
sqlite3 (~> 2.6)
BUNDLED WITH

View File

@@ -1,3 +1,21 @@
$ ->
# run foundation scripts
console.log('Ready.')
averageResults = (results, decimals = 2) ->
avgScore = 0
minScore = Infinity
maxScore = -Infinity
factor = (10 ^ decimals)
for result in results
avgScore += result.avg_score
minScore = Math.min(minScore, result.min_score)
maxScore = Math.max(maxScore, result.max_score)
return {
avgScore: Math.round((avgScore / results.length) * factor) / factor,
minScore: minScore,
maxScore: maxScore,
}

View File

@@ -1,6 +1,9 @@
$ ->
chartInstance = null
$('#report-benchmarks').on 'change', (e) ->
$('#report-tests option').prop('selected', false)
$('#reports-download').on 'click', (e) ->
e.preventDefault()
canvas = $('#benchmark-chart')[0]
@@ -64,21 +67,14 @@ $ ->
resultRes = await fetch("/api/v1/result/list?#{resultSearchParams}")
resultData = await resultRes.json()
avg_total = 0
min_total = 0
max_total = 0
for result in resultData
avg_total += result.avg_score
min_total += result.min_score if result.min_score
max_total += result.max_score if result.max_score
resultAverage = averageResults(resultData)
data.labels.push(testData.name)
data.datasets[0].data.push(avg_total / resultData.length)
data.datasets[0].data.push(resultAverage.avgScore)
console.log(data.datasets[0].data)
switch benchmarkData.scoring
when 'fps', 'ms'
data.datasets[1].data.push(min_total / resultData.length)
data.datasets[1].data.push(resultAverage.minScore)
catch error
console.error 'An error occurred while fetching benchmark results.', error

View File

@@ -13,14 +13,7 @@ fetchTestBenchmarkResults = (testId, benchmarkId) ->
resultRes = await fetch("/api/v1/result/list?#{resultSearchParams}")
resultData = await resultRes.json()
avg_total = 0
min_total = 0
max_total = 0
for result in resultData
avg_total += result.avg_score
min_total += result.min_score
max_total += result.max_score
resultAverage = averageResults(resultData)
tableRow = $("#results-table tr[data-benchmark-id=#{benchmarkId}]")
@@ -29,16 +22,18 @@ fetchTestBenchmarkResults = (testId, benchmarkId) ->
tableRow.append('<td>' + resultData.length + '</td>')
if resultData.length != 0
tableRow.append('<td>' + (avg_total / resultData.length) + '</td>')
tableRow.append('<td>' + resultAverage.avgScore + '</td>')
if benchmarkData.scoring == 'fps'
tableRow.append('<td>' + resultAverage.minScore + '</td>')
tableRow.append('<td>' + resultAverage.maxScore + '</td>')
else
tableRow.append('<td>N/a</td>')
if min_total != 0
tableRow.append('<td>' + (min_total / resultData.length) + '</td>')
tableRow.append('<td>' + (max_total / resultData.length) + '</td>')
tableRow.append('<td>N/a</td>')
else
tableRow.append('<td>N/a</td>')
tableRow.append('<td>N/a</td>')
tableRow.append('<td>N/a</td>')
catch error
console.error 'An error occurred while fetching benchmark results.', error

View File

@@ -3,5 +3,5 @@ require_relative 'src/config.rb'
$conf = Config.new(File.join(__dir__, 'config/defaults.yaml'))
root = ::File.dirname(__FILE__)
require ::File.join( root, 'src', 'server' )
require ::File.join( root, 'src', 'app' )
run GameData.new

41
src/app.rb Normal file
View File

@@ -0,0 +1,41 @@
# frozen_string_literal: true
require 'sinatra/base'
require 'sequel'
require 'sqlite3'
# Load the Sequel timestamps plugin
Sequel::Model.plugin(:timestamps)
# Initialize Sequel gem for database actions
DB = Sequel.connect(adapter: $conf.get('database.adapter'), database: $conf.get('database.database'))
# Load in routes (must happen after Sequel is loaded!)
require_relative 'routes/api1'
require_relative 'routes/benchmark'
require_relative 'routes/hardware'
require_relative 'routes/index'
require_relative 'routes/reports'
require_relative 'routes/result'
require_relative 'routes/test'
# GameData - main app that gets launched
# - inherits from Sinatra::Base to instantiate the server
# - sets up some base app configuration
# - registers route classes with the base app
class GameData < Sinatra::Base
enable :sessions
# Set up static file serving
enable :static
set :public_folder, File.join(__dir__, '/../public')
use IndexRoutes
use HardwareRoutes
use BenchmarkRoutes
use TestRoutes
use ResultRoutes
use ReportsRoutes
use APIv1Routes
end

View File

@@ -1,5 +1,7 @@
# frozen_string_literal: true
require_relative 'appinfo'
# Helpers - view helper functions
module Helpers

View File

@@ -1,6 +0,0 @@
# frozen_string_literal: true
require_relative 'hardware'
require_relative 'benchmark'
require_relative 'result'
require_relative 'test'

View File

@@ -7,4 +7,8 @@ class Test < Sequel::Model
many_to_one :hardware
many_to_many :benchmarks
def benchmark?(benchmark_id)
return benchmarks_dataset.where(Sequel[:benchmarks][:id] => benchmark_id).any?
end
end

View File

@@ -1,7 +1,14 @@
# frozen_string_literal: true
require 'sinatra/json'
require_relative '../server'
require_relative '../models/benchmark'
require_relative '../models/test'
require_relative '../models/result'
# /api/v1 routes
class GameData < Sinatra::Base
class APIv1Routes < Server
get '/api/v1/benchmark/details' do
benchmark_id = params[:benchmark_id]

View File

@@ -1,7 +1,10 @@
# frozen_string_literal: true
require_relative '../server'
require_relative '../models/benchmark'
# /benchmark routes
class GameData < Sinatra::Base
class BenchmarkRoutes < Server
get '/benchmark' do
benchmarks = Benchmark.reverse(:updated_at).limit(10).all()

View File

@@ -1,7 +1,11 @@
# frozen_string_literal: true
require_relative '../server'
require_relative '../models/hardware'
require_relative '../models/benchmark'
# /hardware routes
class GameData < Sinatra::Base
class HardwareRoutes < Server
get '/hardware' do
hardware = Hardware.reverse(:updated_at).limit(10).all()

View File

@@ -1,7 +1,10 @@
# frozen_string_literal: true
require_relative '../server'
require_relative '../models/test'
# / (top-level) routes
class GameData < Sinatra::Base
class IndexRoutes < Server
get '/' do
tests = Test.reverse(:updated_at).limit(10).all()

View File

@@ -1,10 +0,0 @@
# frozen_string_literal: true
require_relative 'index'
require_relative 'hardware'
require_relative 'benchmark'
require_relative 'reports'
require_relative 'result'
require_relative 'test'
require_relative 'api1'

View File

@@ -1,7 +1,14 @@
# frozen_string_literal: true
require 'sinatra/json'
require_relative '../server'
require_relative '../models/benchmark'
require_relative '../models/result'
require_relative '../models/test'
# /reports routes
class GameData < Sinatra::Base
class ReportsRoutes < Server
get '/report' do
benchmarks = Benchmark.order(:name).all()

View File

@@ -1,7 +1,10 @@
# frozen_string_literal: true
require_relative '../server'
require_relative '../models/result'
# /result routes
class GameData < Sinatra::Base
class ResultRoutes < Server
post '/result/add' do
result_minimum = params[:result_minimum] if params.key?(:result_minimum)

View File

@@ -1,7 +1,12 @@
# frozen_string_literal: true
require_relative '../server'
require_relative '../models/benchmark'
require_relative '../models/hardware'
require_relative '../models/test'
# /test routes
class GameData < Sinatra::Base
class TestRoutes < Server
get '/test' do
tests = Test.reverse(:updated_at).limit(10).all()
@@ -65,9 +70,22 @@ class GameData < Sinatra::Base
tst.update(
name: params[:test_name],
type: params[:test_type]
hardware_id: params[:test_hardware],
description: params[:test_description]
)
selected_benchmarks = Array(params[:test_benchmarks])
# remove benchmarks no longer associated with the test
tst.benchmarks.dup.each do |b|
tst.remove_benchmark(b.id) unless selected_benchmarks.include?(b.id)
end
# associate the benchmarks to the test
selected_benchmarks.each do |b|
tst.add_benchmark(b) unless tst.benchmark?(b)
end
redirect "/test/#{tst.id}"
end

View File

@@ -1,25 +1,9 @@
# frozen_string_literal: true
require 'sinatra/base'
require 'sinatra/json'
require 'sequel'
require 'sqlite3'
require_relative 'appinfo'
# Load the Sequel timestamps plugin
Sequel::Model.plugin(:timestamps)
# Initialize Sequel gem for database actions
DB = Sequel.connect(adapter: $conf.get('database.adapter'), database: $conf.get('database.database'))
# Base app
class GameData < Sinatra::Base
enable :sessions
# Set up static file serving
enable :static
set :public_folder, File.join(__dir__, '/../public')
# Server - base modular Sinatra app class
class Server < Sinatra::Base
# Register view helpers
require_relative 'helpers'
@@ -29,8 +13,3 @@ class GameData < Sinatra::Base
set :views, File.join(settings.root, '/../views')
end
# Load routes
require_relative 'routes/init'
# Load models
require_relative 'models/init'

View File

@@ -26,7 +26,7 @@
<div class="row mb-3">
<div class="col-12">
<label for="benchmark_description">Benchmark description</label>
<textarea id="benchmark_description" class="form-control" name="benchmark_description">Enter a description/notes here.</textarea>
<textarea id="benchmark_description" class="form-control" name="benchmark_description" placeholder="Enter a description/notes here."></textarea>
</div>
</div>

View File

@@ -26,7 +26,7 @@
<div class="row mb-3">
<div class="col-12">
<label for="benchmark_description">Benchmark description</label>
<textarea id="benchmark_description" class="form-control" name="benchmark_description"><%= benchmark.description %></textarea>
<textarea id="benchmark_description" class="form-control" name="benchmark_description" placeholder="Enter a description/notes here."><%= benchmark.description %></textarea>
</div>
</div>

View File

@@ -28,7 +28,7 @@
<label for="test_benchmarks">Benchmarks</label>
<select id="test_benchmarks" class="form-select" name="test_benchmarks[]" multiple>
<% for b in benchmarks %>
<option value="<%= b.id %>"><%= b.name %></option>
<option value="<%= b.id %>" <% if test.benchmark?(b.id) %>selected<% end %>><%= b.name %></option>
<% end %>
</select>
</div>
@@ -41,7 +41,7 @@
<div class="row">
<div class="col-12">
<input class="btn btn-primary w-100" type="submit" value="Create Test">
<input class="btn btn-primary w-100" type="submit" value="Submit Changes">
</div>
</div>
</form>