62 Commits

Author SHA1 Message Date
gballan 25d394627d Version bump to v0.1.1
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2025-07-28 11:01:35 -04:00
gballan 85dfdb163a Fixed rubocop warnings
ci/woodpecker/push/woodpecker Pipeline was successful
2025-07-28 10:57:40 -04:00
gballan b593ef7593 Updating to ruby:3.4 in CI/CD
ci/woodpecker/push/woodpecker Pipeline was successful
2025-07-28 10:54:53 -04:00
gballan 26698082f4 Removed backup copy of the reports.coffee script
ci/woodpecker/push/woodpecker Pipeline failed
2025-07-28 10:49:09 -04:00
gballan 42a0b95015 Fixed the chart generation page with the new ChartJS version and DB schema
ci/woodpecker/push/woodpecker Pipeline failed
2025-07-28 10:48:49 -04:00
gballan 5cc3b8f824 Added a better way of setting/reading the app version; fixed some of the flexbox sizing
ci/woodpecker/push/woodpecker Pipeline failed
2025-07-28 10:23:51 -04:00
gballan d59c75281e Updating puma.rb to allow changing the rack environment dynamically
ci/woodpecker/push/woodpecker Pipeline failed
2025-07-28 10:17:45 -04:00
gballan 55e4f397f8 Fixed the test edit view
ci/woodpecker/push/woodpecker Pipeline failed
2025-07-26 11:37:43 -04:00
gballan 49d1276031 Fixed puma config for new Puma versions 2025-07-26 11:32:32 -04:00
gballan bc70fb8dd0 Added config/puma.rb for production deployment
ci/woodpecker/push/woodpecker Pipeline failed
2025-07-26 11:16:07 -04:00
gballan 0231ebad2d Made some more style changes
ci/woodpecker/push/woodpecker Pipeline failed
2025-07-26 01:08:35 -04:00
gballan 541b5236f0 Finished transition to Bootstrap 5
ci/woodpecker/push/woodpecker Pipeline failed
2025-07-26 00:55:52 -04:00
Gregory Ballantine 6215cecb53 Cleaned up app version output
ci/woodpecker/push/woodpecker Pipeline failed
2025-07-25 17:48:21 -04:00
Gregory Ballantine 886f566ae2 Started work to move the CSS framework to Bootstrap 5
ci/woodpecker/push/woodpecker Pipeline failed
ci/woodpecker/tag/woodpecker Pipeline failed
2025-07-25 17:44:25 -04:00
Gregory Ballantine fdd350e16f Updated gulpfile to be a little bit less repetitive
ci/woodpecker/push/woodpecker Pipeline failed
2025-07-25 13:41:51 -04:00
gballan bc4cb181c3 Added some styles
ci/woodpecker/push/woodpecker Pipeline failed
2025-07-23 23:54:12 -04:00
gballan 822f49bcc2 fixed display of tests on the dashboard 2025-07-23 23:26:22 -04:00
gballan aec77628f7 Small updates
ci/woodpecker/push/woodpecker Pipeline failed
2025-07-23 23:25:00 -04:00
gballan ecb696372d Added dockerfile to build assets via Gulp; updated docker scripts 2025-07-23 23:24:49 -04:00
gballan 31b8404be1 Swapping out Grunt for Gulp 2025-06-22 19:30:17 -04:00
gballan 8cd47d7e70 Added API routes to provide some information for JS; added CoffeeScript to grab benchmark results for a test 2025-06-19 00:38:36 -04:00
gballan d17b66920a Fixed typo in watch task for CoffeeScript files 2025-06-18 16:44:10 -04:00
gballan 47cb580393 Started adding test model/routes/views; removed a bit of the old paradigm with tying results directly to hardware 2025-06-10 17:35:52 -04:00
gballan 883019b181 Fixing server:dev Rake command to actuualy reload the app 2025-06-09 23:27:48 -04:00
gballan 2cc80e239b Added TTY flag to docker-run.bat 2025-05-30 09:38:20 -04:00
gballan ed085e4b40 Added abilities to view and edit benchmark info 2025-05-30 01:45:49 -04:00
gballan cf9235cb18 Added ability to edit hardware 2025-05-30 01:30:36 -04:00
gballan 28b6cc66bb Updating dependencies to newer versions; Updating to Ruby 3.4 2025-05-30 01:20:43 -04:00
gballan e3a334546f Transitioned to using SASS and CoffeeScript for assets; using Grunt.js for compiling
ci/woodpecker/push/woodpecker Pipeline was successful
2024-06-12 22:19:46 -04:00
gballan d899ffe58a Adding bundle config to project
ci/woodpecker/push/woodpecker Pipeline was successful
2024-06-12 19:58:08 -04:00
gballan e6853a52d7 Fixed some alignment issues on the benchmark add page
ci/woodpecker/push/woodpecker Pipeline was successful
2024-02-26 15:53:13 -05:00
gballan 6d26eea419 Added shell scripts for managing the docker container
ci/woodpecker/push/woodpecker Pipeline was successful
2024-02-26 00:10:42 -05:00
gballan 778bd25ef2 Fixed awful FirstHashElementIndentation setting
ci/woodpecker/push/woodpecker Pipeline was successful
2024-02-09 15:45:16 -05:00
gballan 6aca95d832 Added rudimentary chart generation for benchmarks
ci/woodpecker/push/woodpecker Pipeline was successful
2024-02-09 15:39:49 -05:00
gballan 357bb69257 Fixed linter errors
ci/woodpecker/push/woodpecker Pipeline was successful
2024-02-09 13:59:44 -05:00
gballan 1e0cd49cf8 Added ability to add benchmark results from hardware page
ci/woodpecker/push/woodpecker Pipeline was successful
2024-02-09 13:57:01 -05:00
gballan 5628c69d66 Fix style linting
ci/woodpecker/push/woodpecker Pipeline was successful
2024-02-09 10:05:10 -05:00
gballan e6333b855f Added form to hardware view page for adding test results
ci/woodpecker/push/woodpecker Pipeline was successful
2024-02-09 10:03:27 -05:00
gballan f7979cd005 Removed more references to test
ci/woodpecker/push/woodpecker Pipeline was successful
2024-02-09 08:42:49 -05:00
gballan ef2e0a12a5 Removed Test object to simplify database schema; updated docker scripts to run database migrations before starting the server if the database doesn't exist
ci/woodpecker/push/woodpecker Pipeline was successful
2024-02-09 08:40:34 -05:00
gballan c83d517437 Updating Gem versions
ci/woodpecker/push/woodpecker Pipeline was successful
2024-02-09 07:19:30 -05:00
gballan 8dee8b1f40 Added docker support for development environment
ci/woodpecker/push/woodpecker Pipeline was successful
2024-02-09 07:17:14 -05:00
gballan 175ce24b27 Started work to redo the UI for database changes
ci/woodpecker/push/woodpecker Pipeline was successful
2023-12-21 01:29:59 -05:00
gballan 516f125ea7 Fixed a lot of linter warnings
ci/woodpecker/push/woodpecker Pipeline was successful
2023-12-11 13:08:46 -05:00
gballan 34741691ed Fixed Rubocop all
ci/woodpecker/push/woodpecker Pipeline was successful
2023-12-08 15:21:11 -05:00
Gregory Ballantine fa9245d7ec Added rubocop for style checking; added woodpecker CI config
ci/woodpecker/manual/woodpecker Pipeline was successful
2023-12-08 12:17:13 -05:00
gballan 1a363a5862 Improved the db:migrate function in the Rakefile to utilize the Sequel API for running migrations, and to allow downgrading to a specific migration easily 2023-12-07 13:04:02 -05:00
gballan b819f4f1cd Reworked database schema from scratch 2023-12-07 12:59:32 -05:00
gballan 394a8ef91f Modified the Rakefile to make the rerun command a lot better 2023-11-29 22:01:44 -05:00
gballan c940a248d7 [Issue #5] - Reworked app to better organize results with their corresponding tests 2023-09-21 20:41:25 -06:00
gballan 269587e6c9 Updated README 2023-09-20 23:04:05 -06:00
gballan 3c9e6eab6b Removed unnecessary score formatting from result add 2023-09-08 07:15:54 -06:00
gballan a1a00d9fe5 Added functionality for better tracking of the minimum and maximum fps 2023-09-08 00:36:40 -06:00
gballan 7e38154bc1 Fixed the table on the dashboard 2023-09-03 09:26:09 -04:00
gballan f0115847b8 Fixed a couple of things missing from result displays 2023-09-03 09:02:31 -04:00
gballan b8bec03357 Removed the setup rake task to run bundler and replaced it with a shell script 2023-07-06 11:49:48 -04:00
gballan 7655b75410 Added functionality to add a benchmark result 2023-07-05 23:39:08 -04:00
gballan 691e2e9b1e Added module for helper functions; added helper for formatting date consistently 2023-07-05 22:27:07 -04:00
gballan 291db231d5 Added functionality to add new hardware and benchmarks to tie to results (this may be moved to an admin panel later) 2023-07-05 22:08:37 -04:00
gballan 6c1c8bca0a Added Sequel ORM for database handling; added navbar 2023-07-05 18:04:31 -04:00
gballan 6f680e72e5 Added some more scaffolding to the project to get going; added Foundation css to start with styles 2023-07-03 23:02:25 -04:00
gballan b376bf3a79 Added started adding some Sinatra app stuff 2023-07-02 23:42:24 -04:00
67 changed files with 5011 additions and 3 deletions
+2
View File
@@ -0,0 +1,2 @@
---
BUNDLE_PATH: "vendor/bundle"
+9
View File
@@ -56,3 +56,12 @@ build-iPhoneSimulator/
# Used by RuboCop. Remote config files pulled in from inherit_from directive.
# .rubocop-https?--*
# Ignore local data
data/
# Node modules
node_modules/
# Compiled assets
public/css/
public/js/
+23
View File
@@ -0,0 +1,23 @@
require: rubocop-sequel
AllCops:
NewCops: enable
Layout/EmptyLinesAroundClassBody:
EnforcedStyle: 'empty_lines_except_namespace'
Layout/EmptyLinesAroundModuleBody:
EnforcedStyle: 'empty_lines_except_namespace'
Layout/FirstHashElementIndentation:
EnforcedStyle: 'consistent'
Metrics/ClassLength:
Max: 150
Style/ClassVars:
Enabled: false
Style/GlobalVars:
Enabled: false
Style/MethodCallWithoutArgsParentheses:
Enabled: false
Style/MethodCallWithArgsParentheses:
Enabled: true
Style/RedundantReturn:
Enabled: false
+19
View File
@@ -0,0 +1,19 @@
pipeline:
style:
image: ruby:3.4
commands:
- 'gem install rake'
- 'bundle config set --local path "vendor/bundle"'
- 'bundle install'
- 'rake test:rubocop'
gitea_release:
image: plugins/gitea-release
settings:
api_key:
from_secret: gitea_api_key
base_url: https://git.metaunix.net
title: "${CI_COMMIT_TAG}"
when:
event: tag
+16
View File
@@ -0,0 +1,16 @@
FROM ruby:3.4
RUN gem install bundler
WORKDIR /usr/src/game-data
COPY Gemfile Gemfile.l*ck ./
RUN bundle check || bundle install
RUN gem install rake
COPY . ./
ENTRYPOINT ["bash", "entrypoints/dev.sh"]
+13
View File
@@ -0,0 +1,13 @@
# Node.js runtime
FROM node:24
WORKDIR /usr/src/game-data/
COPY package.* /usr/src/game-data/
RUN npm install
VOLUME /usr/src/game-data/node_modules/
# Run the app
CMD [ "npm", "run", "gulp" ]
+19
View File
@@ -0,0 +1,19 @@
source 'https://rubygems.org'
gem 'sinatra', '~> 3.2'
gem 'sinatra-contrib', '~> 3.2'
gem 'puma', '~> 6.6'
gem 'sequel', '~> 5.92'
gem 'sqlite3', '~> 2.6'
gem 'logger'
group :development, :test do
gem 'rerun'
gem 'wdm', '>= 0.1.0' if Gem.win_platform?
# rubocop and extensions for code style
gem 'rubocop'
gem 'rubocop-sequel'
end
+118
View File
@@ -0,0 +1,118 @@
GEM
remote: https://rubygems.org/
specs:
ast (2.4.3)
base64 (0.3.0)
bigdecimal (3.2.2)
ffi (1.17.2-aarch64-linux-gnu)
ffi (1.17.2-aarch64-linux-musl)
ffi (1.17.2-arm-linux-gnu)
ffi (1.17.2-arm-linux-musl)
ffi (1.17.2-arm64-darwin)
ffi (1.17.2-x86-linux-gnu)
ffi (1.17.2-x86-linux-musl)
ffi (1.17.2-x86_64-darwin)
ffi (1.17.2-x86_64-linux-gnu)
ffi (1.17.2-x86_64-linux-musl)
json (2.12.2)
language_server-protocol (3.17.0.5)
lint_roller (1.1.0)
listen (3.9.0)
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
logger (1.7.0)
multi_json (1.15.0)
mustermann (3.0.3)
ruby2_keywords (~> 0.0.1)
nio4r (2.7.4)
parallel (1.27.0)
parser (3.3.8.0)
ast (~> 2.4.1)
racc
prism (1.4.0)
puma (6.6.0)
nio4r (~> 2.0)
racc (1.8.1)
rack (2.2.17)
rack-protection (3.2.0)
base64 (>= 0.1.0)
rack (~> 2.2, >= 2.2.4)
rainbow (3.1.1)
rb-fsevent (0.11.2)
rb-inotify (0.11.1)
ffi (~> 1.0)
regexp_parser (2.10.0)
rerun (0.14.0)
listen (~> 3.0)
rubocop (1.76.1)
json (~> 2.3)
language_server-protocol (~> 3.17.0.2)
lint_roller (~> 1.1.0)
parallel (~> 1.10)
parser (>= 3.3.0.2)
rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 2.9.3, < 3.0)
rubocop-ast (>= 1.45.0, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 2.4.0, < 4.0)
rubocop-ast (1.45.1)
parser (>= 3.3.7.2)
prism (~> 1.4)
rubocop-sequel (0.4.1)
lint_roller (~> 1.1)
rubocop (>= 1.72.1, < 2)
ruby-progressbar (1.13.0)
ruby2_keywords (0.0.5)
sequel (5.93.0)
bigdecimal
sinatra (3.2.0)
mustermann (~> 3.0)
rack (~> 2.2, >= 2.2.4)
rack-protection (= 3.2.0)
tilt (~> 2.0)
sinatra-contrib (3.2.0)
multi_json (>= 0.0.2)
mustermann (~> 3.0)
rack-protection (= 3.2.0)
sinatra (= 3.2.0)
tilt (~> 2.0)
sqlite3 (2.7.0-aarch64-linux-gnu)
sqlite3 (2.7.0-aarch64-linux-musl)
sqlite3 (2.7.0-arm-linux-gnu)
sqlite3 (2.7.0-arm-linux-musl)
sqlite3 (2.7.0-arm64-darwin)
sqlite3 (2.7.0-x86-linux-gnu)
sqlite3 (2.7.0-x86-linux-musl)
sqlite3 (2.7.0-x86_64-darwin)
sqlite3 (2.7.0-x86_64-linux-gnu)
sqlite3 (2.7.0-x86_64-linux-musl)
tilt (2.6.0)
unicode-display_width (3.1.4)
unicode-emoji (~> 4.0, >= 4.0.4)
unicode-emoji (4.0.4)
PLATFORMS
aarch64-linux-gnu
aarch64-linux-musl
arm-linux-gnu
arm-linux-musl
arm64-darwin
x86-linux-gnu
x86-linux-musl
x86_64-darwin
x86_64-linux-gnu
x86_64-linux-musl
DEPENDENCIES
logger
puma (~> 6.6)
rerun
rubocop
rubocop-sequel
sequel (~> 5.92)
sinatra (~> 3.2)
sinatra-contrib (~> 3.2)
sqlite3 (~> 2.6)
BUNDLED WITH
2.6.9
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) <year> <owner>
Copyright (c) 2023 Bit Goblin
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+33 -2
View File
@@ -1,3 +1,34 @@
# game-data
# Game Data
Web-based tool to store and organize PC hardware gaming benchmarks
Web-based tool to store and organize PC hardware gaming benchmarks.
## Project Goals
The goals of this project are to:
* Record benchmarking results from multiple devices - e.g. log from a laptop or a phone.
* Group results into tests - it's good practice to run a benchmark multiple times for accuracy.
* Create comparisons of hardware tests to compare performance.
* Generate graphs of hardware comparisons for usage in videos and articles.
## Requirements
Game Data runs on Ruby, and takes advantage of Bundler to manage code dependencies and Rake to run various tasks for maintaining the app. You can install them globally like so:
Debian/Ubuntu: `apt install -y ruby ruby-bundler rake`
## Development
Install dependencies via bundler:
`bundle install`
Perform database migrations:
`rake db:migrate`
Run the server in development with auto-reloading:
`rake server:dev`
If everything is running successfully you can open your browser and go to https://localhost:9292.
+31
View File
@@ -0,0 +1,31 @@
require 'bundler/setup'
namespace :db do
desc 'Run migrations'
task :migrate, [:version] do |t, args|
require "sequel/core"
Sequel.extension :migration
version = args[:version].to_i if args[:version]
Sequel.connect('sqlite://data/gamedata.db') do |db|
Sequel::Migrator.run(db, "db/migrations", target: version)
end
end
end
namespace :server do
task :start do
ENV['APP_ENV'] = 'production'
system("puma")
end
task :dev do
system('rerun --no-notify --dir="src/" puma')
end
end
namespace :test do
task :rubocop do
system("rubocop src/")
end
end
+3
View File
@@ -0,0 +1,3 @@
$ ->
# run foundation scripts
console.log('Ready.')
+113
View File
@@ -0,0 +1,113 @@
$ ->
chartInstance = null
$('#reports-download').on 'click', (e) ->
e.preventDefault()
canvas = $('#benchmark-chart')[0]
a = document.createElement 'a'
a.href = canvas.toDataURL 'image/png'
a.download = 'chart.png'
a.click()
$('#reports-button').on 'click', (e) ->
e.preventDefault()
$('#reports-download').attr('disabled', true)
chartInstance.destroy() if chartInstance
benchmarkId = $('#report-benchmarks').val()
testIds = $('#report-tests').val()
benchmarkSearchParams = new URLSearchParams
benchmark_id: benchmarkId
benchmarkRes = await fetch("/api/v1/benchmark/details?#{benchmarkSearchParams}")
benchmarkData = await benchmarkRes.json()
data =
labels: []
datasets: []
switch benchmarkData.scoring
when 'pts'
data.datasets.push({
label: 'Average Score'
data: []
})
when 'fps'
data.datasets.push({
label: 'Average FPS'
data: []
})
data.datasets.push({
label: 'Minimum FPS'
data: []
})
when 'ms'
data.datasets.push({
label: 'Average Frame Time'
data: []
})
data.datasets.push({
label: 'Minimum Frame Time'
data: []
})
for testId in testIds
try
testSearchParams = new URLSearchParams
test_id: testId
testRes = await fetch("/api/v1/test/details?#{testSearchParams}")
testData = await testRes.json()
resultSearchParams = new URLSearchParams
test_id: testId
benchmark_id: 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 if result.min_score
max_total += result.max_score if result.max_score
data.labels.push(testData.name)
data.datasets[0].data.push(avg_total / resultData.length)
console.log(data.datasets[0].data)
switch benchmarkData.scoring
when 'fps', 'ms'
data.datasets[1].data.push(min_total / resultData.length)
catch error
console.error 'An error occurred while fetching benchmark results.', error
ctx = $('#benchmark-chart')[0].getContext('2d')
options =
indexAxis: 'y'
plugins:
title:
display: true
text: benchmarkData.name
font:
size: '24'
datalabels:
anchor: 'end'
align: 'left'
color: 'black'
font:
weight: 'bold'
formatter: (value) -> value
scales:
y:
beginAtZero: true
chartInstance = new Chart ctx,
type: 'bar'
data: data
options: options
plugins: [ChartDataLabels]
$('#reports-download').attr('disabled', false)
$('#benchmark-chart').removeClass('disabled')
+49
View File
@@ -0,0 +1,49 @@
testId = $('#results-table').data('test-id')
fetchTestBenchmarkResults = (testId, benchmarkId) ->
try
benchmarkSearchParams = new URLSearchParams
benchmark_id: benchmarkId
benchmarkRes = await fetch("/api/v1/benchmark/details?#{benchmarkSearchParams}")
benchmarkData = await benchmarkRes.json()
resultSearchParams = new URLSearchParams
test_id: testId
benchmark_id: 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
tableRow = $("#results-table tr[data-benchmark-id=#{benchmarkId}]")
tableRow.append('<td><a href="/benchmark/' + benchmarkData.id + '">' + benchmarkData.name + '</a></td>')
tableRow.append('<td>' + benchmarkData.scoring + '</td>')
tableRow.append('<td>' + resultData.length + '</td>')
if resultData.length != 0
tableRow.append('<td>' + (avg_total / resultData.length) + '</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>')
else
tableRow.append('<td>N/a</td>')
tableRow.append('<td>N/a</td>')
catch error
console.error 'An error occurred while fetching benchmark results.', error
$('#results-table tbody tr').each((index, tr) ->
benchmarkId = $(tr).data('benchmark-id')
console.log("Fetching results for benchmark id: " + benchmarkId)
fetchTestBenchmarkResults(testId, benchmarkId)
)
+21
View File
@@ -0,0 +1,21 @@
@use "sass:color"
//$primary-color: #3399ff
$primary-color: cornflowerblue
$primary-color-highlight: color.adjust($primary-color, $lightness: -10%)
html
width: 100%
height: 100%
body
background: rgb(240, 235, 248)
table
border: 1px solid #666
#wrapper
background: white
padding: 1.5rem 2rem
border: 1px solid #bbb
border-radius: 8px
+2
View File
@@ -0,0 +1,2 @@
docker build -t game-data -f Dockerfile.dev .
docker build -t game-data-gulp -f Dockerfile.gulp .
+5
View File
@@ -0,0 +1,5 @@
#!/bin/sh
docker build -t game-data -f Dockerfile.dev .
docker build -t game-data-gulp -f Dockerfile.gulp .
+1
View File
@@ -0,0 +1 @@
docker exec game-data rake %*
+3
View File
@@ -0,0 +1,3 @@
#!/bin/sh
docker exec game-data rake "$@"
+2
View File
@@ -0,0 +1,2 @@
docker run --rm -d -t -v "%cd%:/usr/src/game-data" -p 9292:9292 --name game-data game-data
docker run --rm -d -t -v "%cd%:/usr/src/game-data" --name game-data-gulp game-data-gulp npm run gulp watch
+5
View File
@@ -0,0 +1,5 @@
#!/bin/sh
docker run --rm -d -t -v "$(pwd):/usr/src/game-data" -p 9292:9292 --name game-data game-data
docker run --rm -d -t -v "$(pwd):/usr/src/game-data" --name game-data-gulp game-data-gulp npm run gulp watch
+6
View File
@@ -0,0 +1,6 @@
#!/bin/sh
# Configure bundler to use local vendor path
bundle config set --local path 'vendor/bundle'
# Install gems
bundle install
+7
View File
@@ -0,0 +1,7 @@
# Load application config
require_relative 'src/config.rb'
$conf = Config.new(File.join(__dir__, 'config/defaults.yaml'))
root = ::File.dirname(__FILE__)
require ::File.join( root, 'src', 'server' )
run GameData.new
+6
View File
@@ -0,0 +1,6 @@
database:
adapter: 'sqlite'
database: 'data/gamedata.db'
testing:
minimum_results_required: 3
+9
View File
@@ -0,0 +1,9 @@
app_dir = File.expand_path('..', __dir__)
directory app_dir
environment ENV.fetch('RACK_ENV', 'development')
bind 'tcp://0.0.0.0:9292'
workers 2
threads 1, 5
View File
+28
View File
@@ -0,0 +1,28 @@
Sequel.migration do
up do
create_table(:hardware) do
primary_key :id
String :name, null: false
String :type, null: false
String :description, text: true
DateTime :created_at, default: Sequel::CURRENT_TIMESTAMP
DateTime :updated_at, default: Sequel::CURRENT_TIMESTAMP
end
create_table(:benchmarks) do
primary_key :id
String :name, null: false
String :description, text: true
String :scoring, null: false
DateTime :created_at, default: Sequel::CURRENT_TIMESTAMP
DateTime :updated_at, default: Sequel::CURRENT_TIMESTAMP
end
end
down do
drop_table(:hardware)
drop_table(:benchmarks)
end
end
+21
View File
@@ -0,0 +1,21 @@
Sequel.migration do
up do
# create tests table
create_table(:results) do
primary_key :id
foreign_key :hardware_id, :hardware
foreign_key :benchmark_id, :benchmarks
Float :avg_score, null: false
Float :min_score
Float :max_score
DateTime :created_at, default: Sequel::CURRENT_TIMESTAMP
DateTime :updated_at, default: Sequel::CURRENT_TIMESTAMP
end
end
down do
drop_table(:results)
end
end
+39
View File
@@ -0,0 +1,39 @@
Sequel.migration do
up do
# create tests table
create_table(:tests) do
primary_key :id
foreign_key :hardware_id, :hardware
String :name, null: false
String :description
DateTime :created_at, default: Sequel::CURRENT_TIMESTAMP
DateTime :updated_at, default: Sequel::CURRENT_TIMESTAMP
end
# create many-to-many table between tests and benchmarks
create_table(:benchmarks_tests) do
primary_key :id
foreign_key :benchmark_id, :benchmarks
foreign_key :test_id, :tests
DateTime :created_at, default: Sequel::CURRENT_TIMESTAMP
DateTime :updated_at, default: Sequel::CURRENT_TIMESTAMP
end
# modify results table to integrate with the new tests table
alter_table(:results) do
drop_foreign_key :hardware_id
add_foreign_key :test_id, :tests
end
end
down do
alter_table(:results) do
drop_foreign_key :test_id
add_foreign_key :hardware_id, :hardware
end
drop_table(:benchmarks_tests)
drop_table(:tests)
end
end
+7
View File
@@ -0,0 +1,7 @@
#!/bin/bash
if [ ! -f ./data/gamedata.db ]; then
rake db:migrate
fi
rake server:dev
+50
View File
@@ -0,0 +1,50 @@
const gulp = require('gulp');
const sass = require('gulp-sass')(require('sass'));
const coffee = require('gulp-coffee');
const sourcemaps = require('gulp-sourcemaps');
const plumber = require('gulp-plumber');
// Compile .sass files to compressed CSS
function compileSass() {
return gulp.src('assets/styles/**/*.sass')
.pipe(plumber())
.pipe(sass({ outputStyle: 'compressed' }).on('error', sass.logError))
.pipe(gulp.dest('public/css'));
}
// Compile .coffee files to JavaScript with source maps
function compileCoffee() {
return gulp.src('assets/scripts/**/*.coffee', { sourcemaps: true })
.pipe(plumber())
.pipe(coffee({ bare: true }))
.pipe(gulp.dest('public/js', { sourcemaps: '.' }));
}
// Watch files for changes
function watchFiles(cb) {
gulp.watch('assets/styles/**/*.sass', compileSass);
gulp.watch('assets/scripts/**/*.coffee', compileCoffee);
cb();
}
// Chain all asset builds together
function buildAssets() {
return gulp.parallel(compileSass, compileCoffee);
}
// Perform initial build then watch
function buildAndWatch() {
return gulp.series(
buildAssets(),
watchFiles
);
}
// one-time build
exports.build = buildAssets();
// start builds with file watching
exports.watch = buildAndWatch();
// Define default task (defaults to watch)
exports.default = exports.watch;
+3159
View File
File diff suppressed because it is too large Load Diff
+28
View File
@@ -0,0 +1,28 @@
{
"name": "game-data",
"description": "PC hardware benchmarking data logger",
"version": "0.1.0",
"main": "src/server.rb",
"scripts": {
"gulp": "gulp"
},
"repository": {
"type": "git",
"url": "https://git.metaunix.net/BitGoblin/game-data"
},
"keywords": [
"pc",
"hardware",
"benchmarking"
],
"author": "Gregory Ballantine <gballantine@bitgoblin.tech>",
"license": "BSD-2-Clause",
"devDependencies": {
"gulp": "^5.0.1",
"gulp-coffee": "^3.0.3",
"gulp-plumber": "^1.2.1",
"gulp-sass": "^6.0.1",
"gulp-sourcemaps": "^3.0.0",
"sass": "^1.89.2"
}
}
+7
View File
@@ -0,0 +1,7 @@
# frozen_string_literal: true
module AppInfo
VERSION = '0.1.1'
end
+28
View File
@@ -0,0 +1,28 @@
# frozen_string_literal: true
require 'yaml'
# Config - loads and manages the app's configuration
class Config
DEFAULT_CONFIG = 'config/defaults.yaml'
def initialize(config_path)
@data = YAML.load_file(DEFAULT_CONFIG)
# merge in user-defined configuration if it exists
@data.merge!(YAML.load_file(config_path)) if File.exist?(config_path)
end
def get(key)
bits = key.split('.')
value = @data
bits.each do |bit|
value = value[bit]
end
return value
end
end
+29
View File
@@ -0,0 +1,29 @@
# frozen_string_literal: true
# Helpers - view helper functions
module Helpers
def ruby_version
return RUBY_VERSION
end
def app_version
return AppInfo::VERSION
end
def date_format(date)
dt = date.to_datetime
return dt.strftime('%B %d, %Y @ %I:%M:%S %p %Z')
end
def average_array(arr, decimals = 1)
sum = 0
arr.each do |i|
sum += i
end
return (sum / arr.length).round(decimals)
end
end
+9
View File
@@ -0,0 +1,9 @@
# frozen_string_literal: true
# Benchmark - database model for PC benchmarks
class Benchmark < Sequel::Model
many_to_many :tests
one_to_many :results
end
+20
View File
@@ -0,0 +1,20 @@
# frozen_string_literal: true
# Hardware - database model for PC hardware
class Hardware < Sequel::Model(:hardware)
one_to_many :tests
def bench_results
br = {}
results.each do |r|
br[r.benchmark.name.to_s] = [] unless br.key?(r.benchmark.name)
br[r.benchmark.name.to_s].push(r.avg_score)
end
return br
end
end
+6
View File
@@ -0,0 +1,6 @@
# frozen_string_literal: true
require_relative 'hardware'
require_relative 'benchmark'
require_relative 'result'
require_relative 'test'
+13
View File
@@ -0,0 +1,13 @@
# frozen_string_literal: true
# Result - database model for benchmark results
class Result < Sequel::Model
many_to_one :test
many_to_one :benchmark
def formatted_score
return @avg_score
end
end
+10
View File
@@ -0,0 +1,10 @@
# frozen_string_literal: true
# Test - database model for hardware tests
class Test < Sequel::Model
one_to_many :result
many_to_one :hardware
many_to_many :benchmarks
end
+31
View File
@@ -0,0 +1,31 @@
# frozen_string_literal: true
# /api/v1 routes
class GameData < Sinatra::Base
get '/api/v1/benchmark/details' do
benchmark_id = params[:benchmark_id]
benchmark = Benchmark.where(id: benchmark_id).first()
json benchmark.values()
end
get '/api/v1/result/list' do
test_id = params[:test_id]
benchmark_id = params[:benchmark_id]
results = Result.where(test_id: test_id, benchmark_id: benchmark_id).all()
json results.map(&:values)
end
get '/api/v1/test/details' do
test_id = params[:test_id]
tst = Test.where(id: test_id).first()
json tst.values()
end
end
+58
View File
@@ -0,0 +1,58 @@
# frozen_string_literal: true
# /benchmark routes
class GameData < Sinatra::Base
get '/benchmark' do
benchmarks = Benchmark.reverse(:updated_at).limit(10).all()
erb :'benchmark/index', locals: {
title: 'List of Benchmarks',
benchmarks: benchmarks
}
end
get '/benchmark/add' do
erb :'benchmark/add', locals: {
title: 'Add Benchmark'
}
end
post '/benchmark/add' do
benchmark = Benchmark.create(
name: params[:benchmark_name],
scoring: params[:benchmark_scoring],
description: params[:benchmark_description]
)
redirect "/benchmark/#{benchmark.id}"
end
get '/benchmark/:benchmark_id' do
benchmark = Benchmark.where(id: params[:benchmark_id]).first()
erb :'benchmark/view', locals: {
title: benchmark.name,
benchmark: benchmark
}
end
get '/benchmark/:benchmark_id/edit' do
benchmark = Benchmark.where(id: params[:benchmark_id]).first()
erb :'benchmark/edit', locals: {
title: "Editing: #{benchmark.name}",
benchmark: benchmark
}
end
post '/benchmark/:benchmark_id/edit' do
benchmark = Benchmark.where(id: params[:benchmark_id]).first()
benchmark.update(
name: params[:benchmark_name],
scoring: params[:benchmark_scoring],
description: params[:benchmark_description]
)
redirect "/benchmark/#{benchmark.id}"
end
end
+58
View File
@@ -0,0 +1,58 @@
# frozen_string_literal: true
# /hardware routes
class GameData < Sinatra::Base
get '/hardware' do
hardware = Hardware.reverse(:updated_at).limit(10).all()
erb :'hardware/index', locals: {
title: 'List of Hardware',
hardware: hardware
}
end
get '/hardware/add' do
erb :'hardware/add', locals: {
title: 'Add Hardware'
}
end
post '/hardware/add' do
hardware = Hardware.create(
name: params[:hardware_name],
type: params[:hardware_type]
)
redirect "/hardware/#{hardware.id}"
end
get '/hardware/:hardware_id' do
benchmarks = Benchmark.order(:name).all()
hardware = Hardware.where(id: params[:hardware_id]).first()
erb :'hardware/view', locals: {
title: hardware.name,
hardware: hardware,
benchmarks: benchmarks
}
end
get '/hardware/:hardware_id/edit' do
hardware = Hardware.where(id: params[:hardware_id]).first()
erb :'hardware/edit', locals: {
title: "Editing: #{hardware.name}",
hardware: hardware
}
end
post '/hardware/:hardware_id/edit' do
hardware = Hardware.where(id: params[:hardware_id]).first()
hardware.update(
name: params[:hardware_name],
type: params[:hardware_type]
)
redirect "/hardware/#{hardware.id}"
end
end
+15
View File
@@ -0,0 +1,15 @@
# frozen_string_literal: true
# / (top-level) routes
class GameData < Sinatra::Base
get '/' do
tests = Test.reverse(:updated_at).limit(10).all()
erb :'index/index', locals: {
title: 'Dashboard',
tests: tests
}
end
end
+10
View File
@@ -0,0 +1,10 @@
# 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'
+46
View File
@@ -0,0 +1,46 @@
# frozen_string_literal: true
# /reports routes
class GameData < Sinatra::Base
get '/report' do
benchmarks = Benchmark.order(:name).all()
tests = Test.order(:name).all()
erb :'reports/index', locals: {
title: 'Generate Reports',
tests: tests,
benchmarks: benchmarks
}
end
post '/report' do
report_type = params[:type]
report_choice = params[:choice]
report_compare = params[:compare]
if report_type == 'benchmark'
choice = Benchmark.where(id: report_choice).first().name
names = []
avg_results = []
min_results = []
report_compare.each do |c|
tst = Test.where(id: c).first()
names.push(tst.name)
res = Result.where(benchmark_id: report_choice, test_id: c).first()
avg_results.push(res.avg_score)
min_results.push(res.min_score)
end
json({
choice: choice,
names: names,
avg_results: avg_results,
min_results: min_results
})
end
end
end
+29
View File
@@ -0,0 +1,29 @@
# frozen_string_literal: true
# /result routes
class GameData < Sinatra::Base
post '/result/add' do
result_minimum = params[:result_minimum] if params.key?(:result_minimum)
result_maximum = params[:result_maximum] if params.key?(:result_maximum)
Result.create(
test_id: params[:result_test],
benchmark_id: params[:result_benchmark],
avg_score: params[:result_average],
min_score: result_minimum,
max_score: result_maximum
)
if params.key?(:result_referrer)
if params[:result_referrer] == 'test'
redirect "/test/#{params[:result_test]}"
elsif params[:result_referrer] == 'benchmark'
redirect "/benchmark/#{params[:result_benchmark]}"
end
else
redirect '/result'
end
end
end
+74
View File
@@ -0,0 +1,74 @@
# frozen_string_literal: true
# /test routes
class GameData < Sinatra::Base
get '/test' do
tests = Test.reverse(:updated_at).limit(10).all()
erb :'test/index', locals: {
title: 'List of Tests',
tests: tests
}
end
get '/test/add' do
hardware = Hardware.order(:name).all()
benchmarks = Benchmark.order(:name).all()
erb :'test/add', locals: {
title: 'Add Test',
hardware: hardware,
benchmarks: benchmarks
}
end
post '/test/add' do
tst = Test.create(
name: params[:test_name],
hardware_id: params[:test_hardware],
description: params[:test_description]
)
# create an array of the selected benchmarks
benchmarks = Array(params[:test_benchmarks])
# associate the benchmarks to the test
benchmarks.each do |b|
tst.add_benchmark(b)
end
redirect "/test/#{tst.id}"
end
get '/test/:test_id' do
tst = Test.where(id: params[:test_id]).first()
erb :'test/view', locals: {
title: tst.name,
test: tst
}
end
get '/test/:test_id/edit' do
tst = Test.where(id: params[:test_id]).first()
hardware = Hardware.order(:name).all()
benchmarks = Benchmark.order(:name).all()
erb :'test/edit', locals: {
title: "Editing: #{tst.name}",
test: tst,
hardware: hardware,
benchmarks: benchmarks
}
end
post '/test/:test_id/edit' do
tst = Test.where(id: params[:test_id]).first()
tst.update(
name: params[:test_name],
type: params[:test_type]
)
redirect "/test/#{tst.id}"
end
end
Executable
+36
View File
@@ -0,0 +1,36 @@
# 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')
# Register view helpers
require_relative 'helpers'
helpers Helpers
# Set up our view engine
set :views, File.join(settings.root, '/../views')
end
# Load routes
require_relative 'routes/init'
# Load models
require_relative 'models/init'
+40
View File
@@ -0,0 +1,40 @@
<div class="row mb-4">
<div class="col-12">
<h1>Add new benchmark</h1>
</div>
</div>
<div class="row">
<form class="col-12" action="/benchmark/add" method="post">
<div class="row mb-3">
<div class="col-9">
<label for="benchmark_name">Benchmark name</label>
<input id="benchmark_name" class="form-control" type="text" name="benchmark_name" placeholder="Example benchmark">
</div>
<div class="col-3">
<label for="benchmark_scoring">Scoring type</label>
<select id="benchmark_scoring" class="form-select" name="benchmark_scoring">
<option value="fps">Frames per Second (fps)</option>
<option value="ms">Frame Time (ms)</option>
<option value="pts">Total Points</option>
</select>
</div>
</div>
<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>
</div>
</div>
<div class="row">
<div class="col-12">
<input class="btn btn-primary w-100" type="submit" value="Create Benchmark">
</div>
</div>
</form>
</div>
+40
View File
@@ -0,0 +1,40 @@
<div class="row mb-4">
<div class="col-12">
<h1>Editing: <%= benchmark.name %></h1>
</div>
</div>
<div class="row">
<form class="cell small-12" action="/benchmark/<%= benchmark.id %>/edit" method="post">
<div class="row mb-3">
<div class="col-9">
<label for="benchmark_name">Benchmark name</label>
<input id="benchmark_name" class="form-control" type="text" name="benchmark_name" placeholder="Example benchmark" value="<%= benchmark.name %>">
</div>
<div class="col-3">
<label for="benchmark_scoring">Scoring type</label>
<select id="benchmark_scoring" class="form-select" name="benchmark_scoring">
<option value="fps" <% if benchmark.scoring == 'fps' %>selected<% end %>>Frames per Second (fps)</option>
<option value="ms" <% if benchmark.scoring == 'ms' %>selected<% end %>>Frame Time (ms)</option>
<option value="pts" <% if benchmark.scoring == 'pts' %>selected<% end %>>Total Points</option>
</select>
</div>
</div>
<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>
</div>
</div>
<div class="row">
<div class="col-12">
<input class="btn btn-primary w-100" type="submit" value="Submit Changes">
</div>
</div>
</form>
</div>
+40
View File
@@ -0,0 +1,40 @@
<div class="row mb-3">
<div class="col-12">
<h1>List of benchmarks</h1>
</div>
<div class="col-12">
<p>
<a href="/benchmark/add">Add new benchmark</a>
</p>
</div>
</div>
<div class="row">
<% if benchmarks.length > 0 %>
<div class="col-12">
<table class="table table-hover table-responsive">
<thead class="table-light">
<tr>
<th>Benchmark name</th>
<th>Scoring type</th>
<th>Notes</th>
</tr>
</thead>
<tbody>
<% benchmarks.each do |b| %>
<tr>
<td><a href="/benchmark/<%= b.id %>"><%= b.name %></a></td>
<td><%= b.scoring %></td>
<td><%= b.description %></td>
</tr>
<% end %>
</tbody>
</table>
</div>
<% else %>
<div class="col-12">
<p>I'm sorry, there doesn't appear to be any benchmarks added yet. Check again later!</p>
</div>
<% end %>
</div>
+49
View File
@@ -0,0 +1,49 @@
<div class="row">
<div class="col-12">
<h1><%= benchmark.name %></h1>
</div>
<div class="col-12">
<p><a href="/benchmark/<%= benchmark.id %>/edit">Edit</a></p>
</div>
<div class="col-12">
Benchmark scoring type: <%= benchmark.scoring %>
</div>
<div class="col-12">
Description:
<p><%= benchmark.description %></p>
</div>
</div>
<hr>
<div class="row">
<div class="col-12">
<h3 class="mb-3">Tests using this benchmark:</h3>
<% if benchmark.tests.length > 0 %>
<table class="table table-hover table-responsive">
<thead class="table-light">
<tr>
<th>Test title</th>
<th>Benchmarks</th>
<th>Last updated</th>
</tr>
</thead>
<tbody>
<% benchmark.tests.each do |t| %>
<tr>
<td><a href="/test/<%= t.id %>"><%= t.name %></a></td>
<td><%= t.benchmarks.length %></td>
<td><%= t.updated_at %></td>
</tr>
<% end %>
</tbody>
</table>
<% else %>
<p>There are no tests associated with this benchmark.</p>
<% end %>
</div>
</div>
+32
View File
@@ -0,0 +1,32 @@
<div class="row mb-3">
<div class="col-12">
<h1>Add new hardware</h1>
</div>
</div>
<div class="row mb-3">
<form class="col-12" action="/hardware/add" method="post">
<div class="row mb-3">
<div class="col-12 col-md-9">
<label for="hardware_name">Hardware name</label>
<input id="hardware_name" class="form-control" type="text" name="hardware_name" placeholder="Example hardware">
</div>
<div class="col-12 col-md-3">
<label for="hardware_type">Type</label>
<select id="hardware_type" class="form-select" name="hardware_type">
<option value="gpu">Graphics card</option>
<option value="cpu">Processor</option>
</select>
</div>
</div>
<div class="row">
<div class="col-12">
<input class="btn btn-primary w-100" type="submit" value="Create Hardware">
</div>
</div>
</form>
</div>
+32
View File
@@ -0,0 +1,32 @@
<div class="row mb-3">
<div class="col-12">
<h1>Add new hardware</h1>
</div>
</div>
<div class="row mb-3">
<form class="col-12" action="/hardware/<%= hardware.id %>/edit" method="post">
<div class="row mb-3">
<div class="col-12 col-md-9">
<label for="hardware_name">Hardware name</label>
<input id="hardware_name" class="form-control" type="text" name="hardware_name" placeholder="Example hardware" value="<%= hardware.name %>">
</div>
<div class="col-12 col-md-3">
<label for="hardware_type">Type</label>
<select id="hardware_type" class="form-select" name="hardware_type">
<option value="gpu" <% if hardware.type == 'gpu' %>selected<% end %>>Graphics card</option>
<option value="cpu" <% if hardware.type == 'cpu' %>selected<% end %>>Processor</option>
</select>
</div>
</div>
<div class="row">
<div class="col-12">
<input class="btn btn-primary w-100" type="submit" value="Submit Changes">
</div>
</div>
</form>
</div>
+42
View File
@@ -0,0 +1,42 @@
<div class="row mb-3">
<div class="col-12">
<h1>List of hardware</h1>
</div>
<div class="col-12">
<p>
<a href="/hardware/add">Add new hardware</a>
</p>
</div>
</div>
<div class="row mb-3">
<% if hardware.length > 0 %>
<div class="col-12">
<table class="table table-hover table-responsive">
<thead class="table-light">
<tr>
<th>Hardware name</th>
<th>Type</th>
<th>Date added</th>
<th>Date modified</th>
</tr>
</thead>
<tbody>
<% hardware.each do |h| %>
<tr>
<td><a href="/hardware/<%= h.id %>"><%= h.name %></a></td>
<td><%= h.type %></td>
<td><%= date_format(h.created_at) %></td>
<td><%= date_format(h.updated_at) %></td>
</tr>
<% end %>
</tbody>
</table>
</div>
<% else %>
<div class="col-12">
<p>I'm sorry, there doesn't appear to be any hardware added yet. Check again later!</p>
</div>
<% end %>
</div>
+44
View File
@@ -0,0 +1,44 @@
<div class="row">
<div class="col-12">
<h1><%= hardware.name %></h1>
</div>
<div class="col-12">
<p><a href="/hardware/<%= hardware.id %>/edit">Edit</a></p>
</div>
<div class="col-12">
Hardware type: <%= hardware.type %>
</div>
</div>
<hr>
<div class="row">
<div class="col-12">
<h3 class="mb-3">Tests using this benchmark:</h3>
<% if hardware.tests.length > 0 %>
<table class="table table-hover table-responsive">
<thead class="table-light">
<tr>
<th>Test title</th>
<th>Benchmarks</th>
<th>Last updated</th>
</tr>
</thead>
<tbody>
<% hardware.tests.each do |t| %>
<tr>
<td><a href="/test/<%= t.id %>"><%= t.name %></a></td>
<td><%= t.benchmarks.length %></td>
<td><%= t.updated_at %></td>
</tr>
<% end %>
</tbody>
</table>
<% else %>
<p>There are no tests associated with this benchmark.</p>
<% end %>
</div>
</div>
+32
View File
@@ -0,0 +1,32 @@
<div class="row">
<% if tests.length > 0 %>
<div class="12-col mb-3">
<h2>Latest benchmark results:</h2>
</div>
<div class="12-col">
<table class="table table-hover table-responsive">
<thead class="table-light">
<tr>
<th>Test name</th>
<th># Benchmarks</th>
<th>Last Updated</th>
</tr>
</thead>
<tbody>
<% tests.each do |t| %>
<tr>
<td><a href="/test/<%= t.id %>"><%= t.name %></a></td>
<td><%= t.benchmarks.length %></td>
<td><%= t.updated_at %></td>
</tr>
<% end %>
</tbody>
</table>
</div>
<% else %>
<div class="12-col">
<p>I'm sorry, there don't appear to be any benchmark results logged yet. Check again later!</p>
</div>
<% end %>
</div>
+28
View File
@@ -0,0 +1,28 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title><%= title %> | Game Data</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.7/css/bootstrap.min.css">
<link rel="stylesheet" href="/css/rimmington.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js" charset="utf-8"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.7/js/bootstrap.min.js" charset="utf-8"></script>
<script src="/js/edgeville.js" charset="utf-8"></script>
</head>
<body class="d-flex flex-column min-vh-100">
<!-- main navigation -->
<%= erb :'partials/navbar', :locals => locals %>
<!-- main content -->
<main class="flex-grow-1 py-4">
<div id="wrapper" class="container mb-4">
<%= yield %>
</div>
</main>
<!-- site footer -->
<%= erb :'partials/footer', :locals => locals %>
</body>
</html>
+10
View File
@@ -0,0 +1,10 @@
<footer id="main-footer" class="bg-light border-top py-3 text-center">
<div class="container">
<div class="row">
<div class="col-12">
<p>Game Data version v<%= app_version() %></p>
<p class="mb-0">Running Ruby version v<%= ruby_version() %></p>
</div>
</div>
</div>
</footer>
+29
View File
@@ -0,0 +1,29 @@
<div id="main-nav" class="navbar navbar-expand-md bg-dark border-bottom border-body mb-3" data-bs-theme="dark">
<div class="container-fluid">
<a class="navbar-brand mb-0 h1" href="#">Game Data</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-md-0">
<li class="nav-item">
<a class="nav-link" href="/">Dashboard</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/benchmark">Benchmarks</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/hardware">Hardware</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/test">Test</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/report">Reports</a>
</li>
</ul>
</div>
</div>
</div>
+43
View File
@@ -0,0 +1,43 @@
<div class="row">
<form class="col-12" action="/reports" method="post">
<div class="row mb-3">
<div class="col-12 col-md-6">
<select id="report-type" class="form-select" name="report_type" disabled>
<option value="benchmark">Benchmark</option>
<option value="test">Test</option>
</select>
</div>
<div class="col-12 col-md-6">
<select id="report-benchmarks" class="form-select" name="report_choice">
<% benchmarks.each do |b| %>
<option value="<%= b.id %>"><%= b.name %></option>
<% end %>
</select>
</div>
</div>
<div class="col-12 mb-3">
<select id="report-tests" class="col-12 form-select" name="report_compare[]" multiple>
<% tests.each do |t| %>
<option value="<%= t.id %>"><%= t.name %></option>
<% end %>
</select>
</div>
<div class="col-12 mb-3">
<button id="reports-button" class="btn btn-primary">Generate</button>
<button id="reports-download" class="btn btn-primary" disabled>Download</button>
</div>
<div class="col-12">
<canvas id="benchmark-chart" width="100%" height="25"></canvas>
</div>
</form>
</div>
<!-- load the chart.js library -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.1/chart.umd.min.js" charset="utf-8"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/chartjs-plugin-datalabels/2.2.0/chartjs-plugin-datalabels.min.js" charset="utf-8"></script>
<!-- load chart functionality -->
<script src="/js/reports.js" charset="utf-8"></script>
+49
View File
@@ -0,0 +1,49 @@
<div class="row mb-3">
<div class="col-12">
<h1>Add new test</h1>
</div>
</div>
<div class="row">
<form class="col-12" action="/test/add" method="post">
<div class="row mb-0 mb-md-3">
<div class="col-12 col-md-6 mb-3 mb-md-0">
<label for="test_name">Test name</label>
<input id="test_name" class="form-control" type="text" name="test_name" placeholder="My hardware test (01/99)">
</div>
<div class="col-12 col-md-6 mb-3 mb-md-0">
<label for="test_hardware">Hardware to test</label>
<select id="test_hardware" class="form-select" name="test_hardware">
<% for h in hardware %>
<option value="<%= h.id %>"><%= h.name %></option>
<% end %>
</select>
</div>
</div>
<div class="row mb-0 mb-md-3">
<div class="col-12 col-md-4 mb-3 mb-md-0">
<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>
<% end %>
</select>
</div>
<div class="col-12 col-md-8 mb-3 mb-md-0">
<label for="test_description">Test description</label>
<textarea id="test_description" class="form-control" name="test_description" placeholder="This is my test for a hardware..."></textarea>
</div>
</div>
<div class="row">
<div class="col-12">
<input class="btn btn-primary w-100" type="submit" value="Create Test">
</div>
</div>
</form>
</div>
+49
View File
@@ -0,0 +1,49 @@
<div class="row mb-3">
<div class="col-12">
<h1>Editing: <%= test.name %></h1>
</div>
</div>
<div class="row">
<form class="col-12" action="/test/<%= test.id %>/edit" method="post">
<div class="row mb-0 mb-md-3">
<div class="col-12 col-md-6 mb-3 mb-md-0">
<label for="test_name">Test name</label>
<input id="test_name" class="form-control" type="text" name="test_name" placeholder="My hardware test (01/99)" value="<%= test.name %>">
</div>
<div class="col-12 col-md-6 mb-3 mb-md-0">
<label for="test_hardware">Hardware to test</label>
<select id="test_hardware" class="form-select" name="test_hardware">
<% for h in hardware %>
<option value="<%= h.id %>" <% if h.id == test.hardware.id %>selected<% end %>><%= h.name %></option>
<% end %>
</select>
</div>
</div>
<div class="row mb-0 mb-md-3">
<div class="col-12 col-md-4 mb-3 mb-md-0">
<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>
<% end %>
</select>
</div>
<div class="col-12 col-md-8 mb-3 mb-md-0">
<label for="test_description">Test description</label>
<textarea id="test_description" class="form-control" name="test_description" placeholder="This is my test for a hardware..."><%= test.description %></textarea>
</div>
</div>
<div class="row">
<div class="col-12">
<input class="btn btn-primary w-100" type="submit" value="Create Test">
</div>
</div>
</form>
</div>
+42
View File
@@ -0,0 +1,42 @@
<div class="row mb-3">
<div class="col-12">
<h1>List of tests</h1>
</div>
<div class="col-12">
<p>
<a href="/test/add">Add new test</a>
</p>
</div>
</div>
<div class="row mb-3">
<% if tests.length > 0 %>
<div class="col-12">
<table class="table table-hover table-responsive">
<thead class="table-light">
<tr>
<th>Test name</th>
<th># of benchmarks</th>
<th>Date added</th>
<th>Date modified</th>
</tr>
</thead>
<tbody>
<% tests.each do |t| %>
<tr>
<td><a href="/test/<%= t.id %>"><%= t.name %></a></td>
<td><%= t.benchmarks.length %></td>
<td><%= date_format(t.created_at) %></td>
<td><%= date_format(t.updated_at) %></td>
</tr>
<% end %>
</tbody>
</table>
</div>
<% else %>
<div class="col-12">
<p>I'm sorry, there doesn't appear to be any tests added yet. Check again later!</p>
</div>
<% end %>
</div>
+83
View File
@@ -0,0 +1,83 @@
<div class="row">
<div class="col-12">
<h1><%= test.name %></h1>
</div>
<div class="col-12">
<p><a href="/test/<%= test.id %>/edit">Edit</a></p>
</div>
<div class="col-12">
<p>Hardware tested: <a href="/hardware/<%= test.hardware.id %>"><%= test.hardware.name %></a></p>
</div>
<div class="col-12">
<p><%= test.description %></p>
</div>
</div>
<hr>
<div class="row">
<div class="col-12">
<form action="/result/add" method="post">
<input type="hidden" name="result_test" value="<%= test.id %>">
<input type="hidden" name="result_referrer" value="test">
<div class="row">
<div class="col-12 col-md-5 mb-3 mb-md-0">
<label for="result_benchmark">Add benchmark result:</label>
<select class="form-select" id="result_benchmark" name="result_benchmark">
<% test.benchmarks.each do |b| %>
<option value="<%= b.id %>"><%= b.name %></option>
<% end %>
</select>
</div>
<div class="col-4 col-md-2">
<label for="result_average">Average score:</label>
<input id="result_average" class="form-control" type="text" name="result_average" value="">
</div>
<div class="col-4 col-md-2">
<label for="result_minimum">Minimum score:</label>
<input id="result_minimum" class="form-control" type="text" name="result_minimum" value="">
</div>
<div class="col-4 col-md-2 mb-3 mb-md-0">
<label for="result_maximum">Maximum score:</label>
<input id="result_maximum" class="form-control" type="text" name="result_maximum" value="">
</div>
<div class="col-12 col-md-1 d-flex align-items-stretch mb-3 mb-md-0">
<input class="btn btn-primary w-100" type="submit" value="Submit">
</div>
</div>
</form>
<hr>
<h4>Benchmark results for this test:</h4>
<table id="results-table" class="table table-hover table-responsive" data-test-id="<%= test.id %>">
<thead class="table-light">
<tr>
<th>Benchmark name</th>
<th>Scoring type</th>
<th># results</th>
<th>Average</th>
<th>Minimum</th>
<th>Maximum</th>
</tr>
</thead>
<tbody>
<% test.benchmarks.each do |benchmark| %>
<tr data-benchmark-id="<%= benchmark.id %>"></tr>
<% end %>
</tbody>
</table>
</div>
</div>
<!-- load test data fetching functionality -->
<script src="/js/test.js" charset="utf-8"></script>