parent
510d00f312
commit
5908ae9a25
@ -1,7 +1,7 @@ |
||||
source 'https://rubygems.org' |
||||
|
||||
gem 'oj' |
||||
gem 'amazing_print' |
||||
gem 'httparty' |
||||
gem 'parallel' |
||||
gem 'sqlite3' |
||||
gem 'rubyzip' |
||||
|
@ -1,30 +1,18 @@ |
||||
.DEFAULT_GOAL := all
|
||||
ROOT_DIR := $(dir $(realpath $(firstword $(MAKEFILE_LIST))))
|
||||
|
||||
%.zevtc: %.evtc |
||||
zip -9 "$@" "$<"
|
||||
|
||||
%.html: %.zevtc |
||||
mono GW2EI/GuildWars2EliteInsights.exe -p -c gw2ei.conf "$<"
|
||||
|
||||
LOGS := $(shell find arcdps.cbtlogs -name '*.evtc' | sed 's/ /\\ /g' | sort)
|
||||
ARCHIVES := $(patsubst %.evtc,%.zevtc,$(LOGS))
|
||||
REPORTS := $(patsubst %.evtc,%.html,$(LOGS))
|
||||
all: $(REPORTS) |
||||
|
||||
html: |
||||
mono GW2EI/GuildWars2EliteInsights.exe -p -c gw2ei.conf $(ARCHIVES)
|
||||
# sed -i 's/,-\.,/,0,/g' html/*.html
|
||||
mv html/*_{LGolem,StdGolem}_*.html html/golem
|
||||
mv html/*_{arkk,arts,ensol,mama,siax,skorv}_*.html html/fractals
|
||||
mv html/*_{boneskin,fraenir,icebrood,supkodbros,woj}_*.html html/attack
|
||||
mv html/*.html html/raids
|
||||
.PHONY: html |
||||
process: |
||||
./process
|
||||
.PHONY: process |
||||
|
||||
rsync: |
||||
rsync --progress -ahxvAHX --delete html/ kamino:/srv/www/fr.imirhil/www/gw2/
|
||||
|
||||
all: |
||||
gw2-post-exit
|
||||
$(MAKE) process
|
||||
$(MAKE) rsync
|
||||
|
||||
html/static.html: static.html.erb |
||||
erb "$<" > "$@"
|
||||
static: html/static.html |
||||
|
||||
$(MAKE) rsync
|
||||
|
@ -0,0 +1,124 @@ |
||||
ME = 'aeris.5846' |
||||
LANG = :fr |
||||
|
||||
IDS = { |
||||
amam: 17021, siaxx: 17028, ensolyss: 16948, |
||||
|
||||
skorvald: 17632, artsariiv: 17949, arkk: 17759, |
||||
|
||||
ai: 23254, elemental_ai: 23254, dark_ai: 23254, |
||||
|
||||
vale_guardian: 15438, gorseval: 15429, sabetha: 15375, |
||||
|
||||
slothasor: 16123, trio: 16088, matthias: 16115, |
||||
|
||||
keep_construct: 16235, twisted_castle: 16247, xera: 16246, |
||||
|
||||
cairn: 17194, mursaat: 17172, samarog: 17188, deimos: 17154, |
||||
|
||||
soulless_horror: 19767, dhuum: 19450, |
||||
|
||||
conjured_amalgamate: 43974, |
||||
|
||||
adina: 22006, sabir: 21964, |
||||
|
||||
icebrood: 22154, kodans: 22481, fraenir: 22492, boneskinner: 22521, |
||||
whisper_jormag: 22711, freezie: 21333, |
||||
|
||||
standard_golem: 16199, medium_golem: 19645, large_golem: 19676 |
||||
}.freeze |
||||
|
||||
BOSSES = { |
||||
fractals: { |
||||
'98cm': %i[amam siaxx ensolyss], |
||||
'99cm': %i[skorvald artsariiv arkk], |
||||
'100cm': %i[elemental_ai dark_ai] |
||||
}, |
||||
raids: { |
||||
spirit_vale: %i[vale_guardian gorseval sabetha], |
||||
salvation_pass: %i[slothasor trio matthias], |
||||
stronghold_faithfull: %i[keep_construct twisted_castle xera], |
||||
bastion_penitent: %i[cairn mursaat samarog deimos], |
||||
hall_chains: %i[soulless_horror river_souls statues_grenth dhuum], |
||||
mythwright_gambit: %i[conjured_amalgamate twin_largos qadim], |
||||
key_ahdashim: %i[adina sabir qadim_peerless] |
||||
}, |
||||
strikes: %i[icebrood kodans fraenir boneskinner whisper_jormag forging_steel cold_war freezie], |
||||
golems: %i[standard_golem medium_golem large_golem] |
||||
}.freeze |
||||
|
||||
ARCDPS_BOSSES = { |
||||
fractals: %i[ai arkk arts drkai elai ensol mama siax skorv], |
||||
raids: %i[adina ca cairn dei dhuum gors kc matt mo sab sabir sam sh sloth trio twstcstl vg xera], |
||||
strikes: %i[boneskin fraenir freezie icebrood supkodbros woj], |
||||
golems: %i[LGolem MedGolem StdGolem] |
||||
}.freeze |
||||
|
||||
I18N = { |
||||
fr: { |
||||
raids: 'Raids', |
||||
fractals: 'Fractales des Brumes', |
||||
strikes: 'Missons d’attaque', |
||||
golems: 'Golems', |
||||
|
||||
'98cm': 'Cauchemars', |
||||
'99cm': 'Observatoire détruit', |
||||
'100cm': 'Pic de Sunqua', |
||||
|
||||
spirit_vale: 'Vallée des esprits', |
||||
salvation_pass: 'Passage de la rédemption', |
||||
stronghold_faithfull: 'Forteresse des fidèles', |
||||
bastion_penitent: 'Bastion du pénitent', |
||||
hall_chains: 'Hall des chaînes', |
||||
mythwright_gambit: 'Gambit de Forgeconte', |
||||
key_ahdashim: 'Clé d’Ahdashim', |
||||
|
||||
amam: 'AMAM', |
||||
siaxx: 'Siaxx', |
||||
ensolyss: 'Ensolyss', |
||||
|
||||
skorvald: 'Skorvald', |
||||
artsariiv: 'Artsariiv', |
||||
arkk: 'Arkk', |
||||
|
||||
ai: 'Ai, Keeper of the Peak', |
||||
elemental_ai: 'Elemental Ai, Keeper of the Peak', |
||||
dark_ai: 'Dark Ai, Keeper of the Peak', |
||||
|
||||
vale_guardian: 'Gardien de la vallée', |
||||
gorseval: 'Gorseval le disparate', |
||||
sabetha: 'Sabetha la saboteuse', |
||||
|
||||
slothasor: 'Paressor', |
||||
trio: 'Trio de bandits', |
||||
matthias: 'Matthias Gabrel', |
||||
|
||||
keep_construct: 'Titan du fort', |
||||
twisted_castle: 'Chateau corrompu', |
||||
xera: 'Xera', |
||||
|
||||
cairn: 'Cairn', |
||||
mursaat: 'Ssurveillant mursaat', |
||||
samarog: 'Samarog', |
||||
deimos: 'Deimos', |
||||
|
||||
soulless_horror: 'Horreur sans âme', |
||||
dhuum: 'Dhuum', |
||||
|
||||
conjured_amalgamate: 'Amalgame conjurée', |
||||
|
||||
adina: 'Cardinale Adina', |
||||
sabir: 'Cardinal Sabir', |
||||
|
||||
icebrood: 'Sentinelle couvregivre', |
||||
kodans: 'Kodans', |
||||
fraenir: 'Fraenir de Jormag', |
||||
boneskinner: 'Dessosseur', |
||||
whisper_jormag: 'Murmure de Jormag', |
||||
freezie: 'Frisquet', |
||||
|
||||
standard_golem: 'Golem chat standard', |
||||
medium_golem: 'Golem chat intermédiaire', |
||||
large_golem: 'Grand chat golem', |
||||
} |
||||
}.freeze.fetch LANG |
@ -1,292 +0,0 @@ |
||||
#!/usr/bin/env ruby |
||||
require 'oj' |
||||
require 'time' |
||||
require 'amazing_print' |
||||
require 'erb' |
||||
require 'parallel' |
||||
require 'sqlite3' |
||||
|
||||
|
||||
class Hash |
||||
def transform_leaves(&block) |
||||
self.transform_values do |v| |
||||
case v |
||||
when Hash |
||||
v.transform_leaves &block |
||||
else |
||||
block.call v |
||||
end |
||||
end |
||||
end |
||||
|
||||
def symbolize_keys |
||||
self.transform_keys { |k| k.to_sym rescue k } |
||||
end |
||||
|
||||
def walk(&block) |
||||
self.class._walk self, [], &block |
||||
end |
||||
|
||||
private |
||||
def self._walk(hash, path, &block) |
||||
hash.each do |k, v| |
||||
p = [*path, k] |
||||
block.call :node, p, v |
||||
case v |
||||
when Hash |
||||
_walk v, p, &block |
||||
else |
||||
block.call :leaf, p, v |
||||
end |
||||
end |
||||
end |
||||
end |
||||
|
||||
DURATION = { |
||||
'h' => 60*60*1_000, |
||||
'm' => 60*1_000, |
||||
's' => 1_000, |
||||
'ms' => 1, |
||||
}.freeze |
||||
|
||||
def parse_duration(string) |
||||
string.split |
||||
.collect do |s| |
||||
duration, type = s.match(/(\d+)(.+)/).captures |
||||
duration = DURATION.fetch(type) * duration.to_i |
||||
end |
||||
.sum |
||||
end |
||||
|
||||
def duration_to_s(duration) |
||||
text = [] |
||||
DURATION.each do |k, v| |
||||
part = duration / v |
||||
text << "#{part}#{k}" if part > 0 |
||||
duration -= part * v |
||||
end |
||||
text.join ' ' |
||||
end |
||||
|
||||
ME = 'aeris.5846' |
||||
LANG = :fr |
||||
|
||||
IDS = { |
||||
amam: 17021, siaxx: 17028, ensolyss: 16948, |
||||
|
||||
skorvald: 17632, artsariiv: 17949, arkk: 17759, |
||||
|
||||
ai: 23254, elemental_ai: 23254, dark_ai: 23254, |
||||
|
||||
vale_guardian: 15438, gorseval: 15429, sabetha: 15375, |
||||
|
||||
slothasor: 16123, trio: 16088, matthias: 16115, |
||||
|
||||
keep_construct: 16235, twisted_castle: 16247, xera: 16246, |
||||
|
||||
cairn: 17194, mursaat: 17172, samarog: 17188, deimos: 17154, |
||||
|
||||
soulless_horror: 19767, dhuum: 19450, |
||||
|
||||
conjured_amalgamate: 43974, |
||||
|
||||
adina: 22006, sabir: 21964, |
||||
|
||||
icebrood: 22154, kodans: 22481, fraenir: 22492, boneskinner: 22521, |
||||
whisper_jormag: 22711, freezie: 21333, |
||||
|
||||
standard_golem: 16199, medium_golem: 19645, large_golem: 19676 |
||||
}.freeze |
||||
|
||||
BOSSES = { |
||||
fractals: { |
||||
'98cm': %i[amam siaxx ensolyss], |
||||
'99cm': %i[skorvald artsariiv arkk], |
||||
'100cm': %i[elemental_ai dark_ai] |
||||
}, |
||||
raids: { |
||||
spirit_vale: %i[vale_guardian gorseval sabetha], |
||||
salvation_pass: %i[slothasor trio matthias], |
||||
stronghold_faithfull: %i[keep_construct twisted_castle xera], |
||||
bastion_penitent: %i[cairn mursaat samarog deimos], |
||||
hall_chains: %i[soulless_horror river_souls statues_grenth dhuum], |
||||
mythwright_gambit: %i[conjured_amalgamate twin_largos qadim], |
||||
key_ahdashim: %i[adina sabir qadim_peerless] |
||||
}, |
||||
strikes: %i[icebrood kodans fraenir boneskinner whisper_jormag forging_steel cold_war freezie], |
||||
golems: %i[standard_golem medium_golem large_golem] |
||||
}.freeze |
||||
|
||||
I18N = { |
||||
fr: { |
||||
raids: 'Raids', |
||||
fractals: 'Fractales des Brumes', |
||||
strikes: 'Missons d’attaque', |
||||
golems: 'Golems', |
||||
|
||||
'98cm': 'Cauchemars', |
||||
'99cm': 'Observatoire détruit', |
||||
'100cm': 'Pic de Sunqua', |
||||
|
||||
spirit_vale: 'Vallée des esprits', |
||||
salvation_pass: 'Passage de la rédemption', |
||||
stronghold_faithfull: 'Forteresse des fidèles', |
||||
bastion_penitent: 'Bastion du pénitent', |
||||
hall_chains: 'Hall des chaînes', |
||||
mythwright_gambit: 'Gambit de Forgeconte', |
||||
key_ahdashim: 'Clé d’Ahdashim', |
||||
|
||||
amam: 'AMAM', |
||||
siaxx: 'Siaxx', |
||||
ensolyss: 'Ensolyss', |
||||
|
||||
skorvald: 'Skorvald', |
||||
artsariiv: 'Artsariiv', |
||||
arkk: 'Arkk', |
||||
|
||||
ai: 'Ai, Keeper of the Peak', |
||||
elemental_ai: 'Elemental Ai, Keeper of the Peak', |
||||
dark_ai: 'Dark Ai, Keeper of the Peak', |
||||
|
||||
vale_guardian: 'Gardien de la vallée', |
||||
gorseval: 'Gorseval le disparate', |
||||
sabetha: 'Sabetha la saboteuse', |
||||
|
||||
slothasor: 'Paressor', |
||||
trio: 'Trio de bandits', |
||||
matthias: 'Matthias Gabrel', |
||||
|
||||
keep_construct: 'Titan du fort', |
||||
twisted_castle: 'Chateau corrompu', |
||||
xera: 'Xera', |
||||
|
||||
cairn: 'Cairn', |
||||
mursaat: 'Ssurveillant mursaat', |
||||
samarog: 'Samarog', |
||||
deimos: 'Deimos', |
||||
|
||||
soulless_horror: 'Horreur sans âme', |
||||
dhuum: 'Dhuum', |
||||
|
||||
conjured_amalgamate: 'Amalgame conjurée', |
||||
|
||||
adina: 'Cardinale Adina', |
||||
sabir: 'Cardinal Sabir', |
||||
|
||||
icebrood: 'Sentinelle couvregivre', |
||||
kodans: 'Kodans', |
||||
fraenir: 'Fraenir de Jormag', |
||||
boneskinner: 'Dessosseur', |
||||
whisper_jormag: 'Murmure de Jormag', |
||||
freezie: 'Frisquet', |
||||
|
||||
standard_golem: 'Golem chat standard', |
||||
medium_golem: 'Golem chat intermédiaire', |
||||
large_golem: 'Grand chat golem', |
||||
} |
||||
}.freeze.fetch LANG |
||||
|
||||
class Reports |
||||
def initialize |
||||
@reports = Hash.new { |h, k| h[k] = [] } |
||||
end |
||||
|
||||
def <<(report) |
||||
id = report.fetch :id |
||||
@reports[id] << report |
||||
end |
||||
|
||||
def [](boss) |
||||
id = IDS[boss] |
||||
return nil unless id && @reports.has_key?(id) |
||||
reports = @reports.fetch(id).sort { |a, b| b.fetch(:date) <=> a.fetch(:date) } |
||||
cm, standard = reports.partition { |r| r.fetch :cm } |
||||
{ cm: cm, standard: standard } |
||||
end |
||||
|
||||
# def to_h |
||||
# BOSSES.transform_leaves { |bs| bs.collect { |b| self[b] }.compact.to_h } |
||||
# end |
||||
end |
||||
|
||||
def parse_arcdps_report(file) |
||||
File.open file do |fd| |
||||
json = Oj.load fd |
||||
id = json.fetch 'triggerID' |
||||
name = json.fetch 'fightName' |
||||
|
||||
cm = json.fetch 'isCM' |
||||
date = Time.parse json.fetch 'timeStartStd' |
||||
duration = parse_duration json.fetch 'duration' |
||||
|
||||
players = json.fetch 'players' |
||||
dps = players.collect { |p| p.fetch('dpsTargets').first.first.fetch('dps') } |
||||
total_dps = dps.sum |
||||
top_dps = dps.max |
||||
me = players.find { |p| p.fetch('account') == ME } |
||||
name = me.fetch 'name' |
||||
own_dps = me.fetch('dpsTargets').first.first.fetch 'dps' |
||||
profession = me.fetch 'profession' |
||||
|
||||
{ |
||||
id: id, cm: cm, date: date, duration: duration, |
||||
name: name, profession: profession, |
||||
total_dps: total_dps, top_dps: top_dps, own_dps: own_dps, |
||||
} |
||||
end |
||||
end |
||||
|
||||
@reports = Reports.new |
||||
|
||||
SQLite3::Database.new 'reports.db' do |db| |
||||
db.execute <<-SQL |
||||
CREATE TABLE IF NOT EXISTS reports ( |
||||
filename TEXT NOT NULL, |
||||
id INTEGER NOT NULL, |
||||
cm BOOLEAN NOT NULL, |
||||
date DATETIME NOT NULL, |
||||
duration INTEGER NOT NULL, |
||||
name TEXT NOT NULL, |
||||
profession TEXT NOT NULL, |
||||
total_dps INTEGER NOT NULL, |
||||
top_dps INTEGER NOT NULL, |
||||
own_dps INTEGER NOT NULL |
||||
) |
||||
SQL |
||||
db.execute <<-SQL |
||||
CREATE UNIQUE INDEX IF NOT EXISTS filename_index ON reports(filename) |
||||
SQL |
||||
db.execute <<-SQL |
||||
CREATE INDEX IF NOT EXISTS id_index ON reports(id) |
||||
SQL |
||||
db.execute <<-SQL |
||||
CREATE INDEX IF NOT EXISTS date_index ON reports(id, date DESC) |
||||
SQL |
||||
|
||||
db.results_as_hash = true |
||||
Parallel.each Dir["html/*_kill.json"].sort, in_threads: 16 do |file| |
||||
$stderr.puts file |
||||
filename = File.basename file, '.json' |
||||
row = db.get_first_row 'SELECT * FROM reports WHERE filename = ?', filename |
||||
report = if row |
||||
row = row.symbolize_keys |
||||
row[:cm] = row.fetch(:cm) == 1 |
||||
row[:date] = Time.at row.fetch :date |
||||
row |
||||
else |
||||
report = parse_arcdps_report file |
||||
id, cm, date, duration, name, profession, total_dps, top_dps, own_dps = report.values_at :id, :cm, :date, :duration, :name, :profession, :total_dps, :top_dps, :own_dps |
||||
report[:filename] = filename |
||||
db.execute <<-SQL, filename, id, (cm ? 1 : 0), date.to_i, duration, name, profession, total_dps, top_dps, own_dps |
||||
INSERT INTO reports ( filename, id, cm, date, duration, name, profession, total_dps, top_dps, own_dps ) |
||||
VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ? ) |
||||
SQL |
||||
report |
||||
end |
||||
@reports << report |
||||
end |
||||
end |
||||
|
||||
erb = ERB.new File.read 'index.html.erb' |
||||
html = erb.result binding |
||||
File.write 'html/index.html', html |
@ -1,32 +1,31 @@ |
||||
AddDuration=True |
||||
AddPoVProf=False |
||||
Anonymous=False |
||||
AutoAdd=True |
||||
AutoAddPath=arcdps.cbtlogs |
||||
AutoParse=True |
||||
CompressRaw=False |
||||
ComputeDamageModifiers=True |
||||
SaveAtOut=True |
||||
OutLocation=html |
||||
SaveOutHTML=True |
||||
HtmlExternalScripts=True |
||||
IndentJSON=False |
||||
IndentXML=False |
||||
LightTheme=False |
||||
SaveOutJSON=True |
||||
IndentJSON=True |
||||
SaveOutCSV=False |
||||
SaveOutTrace=False |
||||
SaveOutXML=False |
||||
IndentXML=False |
||||
Anonymous=False |
||||
CompressRaw=True |
||||
MultiThreaded=True |
||||
Outdated=False |
||||
OutLocation=html |
||||
ParseCombatReplay=True |
||||
ParseMultipleLogs=True |
||||
SkipFailedTries=False |
||||
AddDuration=True |
||||
AddPoVProf=False |
||||
ParsePhases=True |
||||
ParseCombatReplay=True |
||||
ComputeDamageModifiers=True |
||||
RawTimelineArrays=True |
||||
SaveAtOut=False |
||||
SaveOutCSV=False |
||||
SaveOutHTML=True |
||||
SaveOutJSON=True |
||||
SaveOutTrace=False |
||||
SaveOutXML=False |
||||
SendEmbedToWebhook=False |
||||
SendSimpleMessageToWebhook=False |
||||
SkipFailedTries=False |
||||
UploadToDPSReports=False |
||||
UploadToDPSReportsRH=False |
||||
UploadToRaidar=False |
||||
WebhookURL= |
||||
AutoAddPath=arcdps.cbtlogs |
||||
AutoAdd=True |
||||
AutoParse=True |
||||
|
@ -1,15 +1,258 @@ |
||||
#!/usr/bin/env bash |
||||
set -xe |
||||
ARCHIVES=() |
||||
while read LOG; do |
||||
ARCHIVE="${LOG%.*}.zevtc" |
||||
zip -9 "$ARCHIVE" "$LOG" |
||||
rm "$LOG" |
||||
ARCHIVES+=("$ARCHIVE") |
||||
done < <(find -name "*.evtc") |
||||
|
||||
mono GW2EI/GuildWars2EliteInsights.exe -p -c gw2ei.conf "${ARCHIVES[@]}" |
||||
|
||||
./generate_html |
||||
./sort_reports |
||||
make rsync |
||||
#!/usr/bin/env ruby |
||||
require 'parallel' |
||||
require 'zip' |
||||
require 'oj' |
||||
require 'sqlite3' |
||||
require 'time' |
||||
require 'erb' |
||||
require 'fileutils' |
||||
require './config' |
||||
|
||||
Zip.setup do |c| |
||||
c.default_compression = Zlib::BEST_COMPRESSION |
||||
c.continue_on_exists_proc = true |
||||
end |
||||
|
||||
class Hash |
||||
def leaves(&block) |
||||
Enumerator.new do |e| |
||||
self.class.leaves self, [], e, &block |
||||
end |
||||
end |
||||
|
||||
def symbolize_keys |
||||
self.transform_keys { |k| k.to_sym rescue k } |
||||
end |
||||
|
||||
def walk(&block) |
||||
self.class._walk self, [], &block |
||||
end |
||||
|
||||
private |
||||
def self._walk(hash, path, &block) |
||||
hash.each do |k, v| |
||||
p = [*path, k] |
||||
block.call :node, p, v |
||||
case v |
||||
when Hash |
||||
_walk v, p, &block |
||||
else |
||||
block.call :leaf, p, v |
||||
end |
||||
end |
||||
end |
||||
|
||||
def self.leaves(hash, path, enumerator, &block) |
||||
hash.each do |k, v| |
||||
p = [*path, k] |
||||
case v |
||||
when Hash |
||||
self.leaves v, p, enumerator, &block |
||||
else |
||||
result = block.call p, v |
||||
enumerator << result |
||||
end |
||||
end |
||||
end |
||||
end |
||||
|
||||
DURATION = { |
||||
'h' => 60*60*1_000, |
||||
'm' => 60*1_000, |
||||
's' => 1_000, |
||||
'ms' => 1, |
||||
}.freeze |
||||
|
||||
def parse_duration(string) |
||||
string.split |
||||
.collect do |s| |
||||
duration, type = s.match(/(\d+)(.+)/).captures |
||||
duration = DURATION.fetch(type) * duration.to_i |
||||
end |
||||
.sum |
||||
end |
||||
|
||||
def duration_to_s(duration) |
||||
text = [] |
||||
DURATION.each do |k, v| |
||||
part = duration / v |
||||
text << "#{part}#{k}" if part > 0 |
||||
duration -= part * v |
||||
end |
||||
text.join ' ' |
||||
end |
||||
|
||||
class Reports |
||||
def initialize |
||||
@reports = Hash.new { |h, k| h[k] = [] } |
||||
end |
||||
|
||||
def <<(report) |
||||
id = report.fetch :id |
||||
@reports[id] << report |
||||
end |
||||
|
||||
def [](boss) |
||||
id = IDS[boss] |
||||
return nil unless id && @reports.has_key?(id) |
||||
reports = @reports.fetch(id).sort { |a, b| b.fetch(:date) <=> a.fetch(:date) } |
||||
cm, standard = reports.partition { |r| r.fetch :cm } |
||||
{ cm: cm, standard: standard } |
||||
end |
||||
end |
||||
|
||||
def compress_evtc(evtc) |
||||
puts evtc |
||||
basename = File.basename evtc, '.evtc' |
||||
dirname = File.dirname evtc |
||||
zevtc = File.join dirname, basename + '.zevtc' |
||||
FileUtils.rm_f zevtc |
||||
Zip::File.open zevtc, Zip::File::CREATE do |zip| |
||||
zip.add evtc, evtc |
||||
end |
||||
FileUtils.rm evtc |
||||
zevtc |
||||
end |
||||
|
||||
def process_evtcs |
||||
Parallel.map(Dir['arcdps.cbtlogs/**/*.evtc'].sort) { |evtc| compress_evtc evtc } |
||||
end |
||||
|
||||
def process_zevtcs(zevtcs) |
||||
return if zevtcs.empty? |
||||
system 'mono', 'GW2EI/GuildWars2EliteInsights.exe', '-p', '-c', 'gw2ei.conf', *zevtcs |
||||
zevtcs.collect { |f| File.join 'html', File.basename(f, '.zevtc') + '.json.gz' } |
||||
end |
||||
|
||||
def reprocess_zevtcs |
||||
process_zevtcs Dir['arcdps.cbtlogs/**/*.zevtc'].sort |
||||
end |
||||
|
||||
EXTRACT_BOSS_FROM_FILENAME = /\d{8}-\d{6}_([^_]+)_\d+s_(kill|fail).html/.freeze |
||||
# BOSS_DIRECTORIES = BOSSES.leaves { |p, bs| d = p.first; bs.collect { |b| [b, d] } } |
||||
# .to_a.flatten(1).to_h |
||||
# .merge({ai: :fractals}).freeze |
||||
BOSS_DIRECTORIES = ARCDPS_BOSSES.collect { |t, bs| bs.collect { |b| [b, t] } } |
||||
.flatten(1).to_h.freeze |
||||
|
||||
def sort_html |
||||
Dir['html/*.html'].sort.each do |file| |
||||
basename = File.basename file |
||||
match = EXTRACT_BOSS_FROM_FILENAME.match file |
||||
next unless match |
||||
boss = match[1].to_sym |
||||
directory = BOSS_DIRECTORIES.fetch(boss).to_s |
||||
directory = File.join 'html', directory |
||||
type = match[2].to_sym |
||||
directory = File.join directory, 'fail' if type == :fail |
||||
FileUtils.mv file, directory |
||||
end |
||||
end |
||||
|
||||
def do_in_database |
||||
SQLite3::Database.new 'reports.db' do |db| |
||||
db.execute <<-SQL |
||||
CREATE TABLE IF NOT EXISTS reports ( |
||||
filename TEXT NOT NULL, |
||||
id INTEGER NOT NULL, |
||||
cm BOOLEAN NOT NULL, |
||||
date DATETIME NOT NULL, |
||||
duration INTEGER NOT NULL, |
||||
name TEXT NOT NULL, |
||||
profession TEXT NOT NULL, |
||||
total_dps INTEGER NOT NULL, |
||||
top_dps INTEGER NOT NULL, |
||||
own_dps INTEGER NOT NULL |
||||
) |
||||
SQL |
||||
db.execute <<-SQL |
||||
CREATE UNIQUE INDEX IF NOT EXISTS filename_index ON reports(filename) |
||||
SQL |
||||
db.execute <<-SQL |
||||
CREATE INDEX IF NOT EXISTS id_index ON reports(id) |
||||
SQL |
||||
db.execute <<-SQL |
||||
CREATE INDEX IF NOT EXISTS date_index ON reports(id, date DESC) |
||||
SQL |
||||
|
||||
db.results_as_hash = true |
||||
yield db |
||||
end |
||||
end |
||||
|
||||
def parse_json(file) |
||||
File.open file do |fd| |
||||
json = Oj.load Zlib::GzipReader.wrap fd |
||||
id = json.fetch 'triggerID' |
||||
name = json.fetch 'fightName' |
||||
|
||||
cm = json.fetch 'isCM' |
||||
date = Time.parse json.fetch 'timeStartStd' |
||||
duration = parse_duration json.fetch 'duration' |
||||
|
||||
players = json.fetch 'players' |
||||
dps = players.collect { |p| p.fetch('dpsTargets').first.first.fetch('dps') } |
||||
total_dps = dps.sum |
||||
top_dps = dps.max |
||||
me = players.find { |p| p.fetch('account') == ME } |
||||
name = me.fetch 'name' |
||||
own_dps = me.fetch('dpsTargets').first.first.fetch 'dps' |
||||
profession = me.fetch 'profession' |
||||
|
||||
{ |
||||
id: id, cm: cm, date: date, duration: duration, |
||||
name: name, profession: profession, |
||||
total_dps: total_dps, top_dps: top_dps, own_dps: own_dps, |
||||
} |
||||
end |
||||
end |
||||
|
||||
def process_json(db, json) |
||||
# ap json |
||||
filename = File.basename json, '.json.gz' |
||||
row = db.get_first_row 'SELECT * FROM reports WHERE filename = ?', filename |
||||
return if row |
||||
report = parse_json json |
||||
sql = <<-SQL |
||||
INSERT INTO reports ( filename, id, cm, date, duration, name, profession, total_dps, top_dps, own_dps ) |
||||
VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ? ) |
||||
SQL |
||||
db.execute sql, filename, report.fetch(:id), (report.fetch(:cm) ? 1 : 0), |
||||
report.fetch(:date).to_i, report.fetch(:duration), report.fetch(:name), |
||||
report.fetch(:profession), report.fetch(:total_dps), report.fetch(:top_dps), |
||||
report.fetch(:own_dps) |
||||
end |
||||
|
||||
def process_jsons(db, jsons) |
||||
Parallel.each jsons, in_threads: 16 do |json| |
||||
process_json db, json |
||||
end |
||||
end |
||||
|
||||
def reprocess_jsons(db) |
||||
process_jsons db, Dir["html/*_kill.json.gz"].sort |
||||
end |
||||
|
||||
def generate_html(db) |
||||
@reports = Reports.new |
||||
rows = db.execute 'SELECT * FROM reports' |
||||
rows.each do |row| |
||||
row = row.symbolize_keys |
||||
row[:cm] = row.fetch(:cm) == 1 |
||||
row[:date] = Time.at row.fetch :date |
||||
@reports << row |
||||
end |
||||
|
||||
erb = ERB.new File.read 'index.html.erb' |
||||
html = erb.result binding |
||||
File.write 'html/index.html', html |
||||
end |
||||
|
||||
zevtcs = process_evtcs |
||||
process_zevtcs zevtcs |
||||
sort_html |
||||
# # reprocess_zevtcs |
||||
do_in_database do |db| |
||||
reprocess_jsons db |
||||
generate_html db |
||||
end |
||||
|
@ -1,12 +0,0 @@ |
||||
#!/usr/bin/env bash |
||||
set -xe |
||||
FIND_ARGS=() |
||||
|
||||
if [ -n "$1" ]; then |
||||
FIND_ARGS+=(-newermt "$1") |
||||
fi |
||||
|
||||
find -name "*.zevtc" "${FIND_ARGS[@]}" -print0 | xargs -0 mono GW2EI/GuildWars2EliteInsights.exe -p -c gw2ei.conf |
||||
|
||||
./sort_reports |
||||
./send_reports |
@ -1,18 +0,0 @@ |
||||
#!/usr/bin/env bash |
||||
set -e |
||||
|
||||
function move_report() { |
||||
TYPE="$1" |
||||
REPORT="$2" |
||||
[[ "$REPORT" =~ _fail.html ]] && TYPE="$TYPE/fail" |
||||
mv "$REPORT" "html/$TYPE" |
||||
} |
||||
|
||||
for REPORT in html/*.html; do |
||||
if [ "$REPORT" == "html/index.html" ]; then true |
||||
elif [[ "$REPORT" =~ _(StdGolem|MedGolem|LGolem)_ ]]; then move_report golem "$REPORT" |
||||
elif [[ "$REPORT" =~ _(arkk|arts|ensol|mama|siax|skorv|elai|drkai|ai)_ ]]; then move_report fractals "$REPORT" |
||||
elif [[ "$REPORT" =~ _(boneskin|fraenir|icebrood|supkodbros|woj|freezie)_ ]]; then move_report strikes "$REPORT" |
||||
else move_report raids "$REPORT"; |
||||
fi |
||||
done |
Loading…
Reference in new issue