Files
zkill-susser/analytics/analytics.go

418 lines
13 KiB
Go

package analytics
import (
"fmt"
"strings"
"zkillsusser/types"
)
type FlatKillmailComplete struct {
types.FlatKillmail
Items []types.FlatKillmailItem `json:"items"`
}
// Time aggregation results
type TimeAggregationByHour struct {
KillHour uint8 `json:"kill_hour"`
KillCount uint64 `json:"kill_count"`
UniqueVictims uint64 `json:"unique_victims"`
SystemsHit uint64 `json:"systems_hit"`
}
type TimeAggregationByDay struct {
KillDayOfWeek uint8 `json:"kill_day_of_week"`
DayName string `json:"day_name"`
KillCount uint64 `json:"kill_count"`
UniqueVictims uint64 `json:"unique_victims"`
}
type TimeAggregationByDate struct {
KillDate string `json:"kill_date"`
KillCount uint64 `json:"kill_count"`
UniqueVictims uint64 `json:"unique_victims"`
SystemsHit uint64 `json:"systems_hit"`
}
type TimeAggregationByMonth struct {
Month string `json:"month"`
KillCount uint64 `json:"kill_count"`
UniqueVictims uint64 `json:"unique_victims"`
}
// Location aggregation results
type LocationAggregationBySystem struct {
SolarSystemID int32 `json:"solar_system_id"`
SolarSystemName string `json:"solar_system_name"`
RegionName string `json:"region_name"`
Security float32 `json:"security"`
KillCount uint64 `json:"kill_count"`
UniqueVictims uint64 `json:"unique_victims"`
ShipVariety uint64 `json:"ship_variety"`
}
type LocationAggregationByRegion struct {
RegionName string `json:"region_name"`
KillCount uint64 `json:"kill_count"`
SystemsWithKills uint64 `json:"systems_with_kills"`
UniqueVictims uint64 `json:"unique_victims"`
}
type LocationAggregationByConstellation struct {
ConstellationName string `json:"constellation_name"`
RegionName string `json:"region_name"`
KillCount uint64 `json:"kill_count"`
SystemsInConstellation uint64 `json:"systems_in_constellation"`
}
type LocationAggregationBySecurity struct {
SecStatus string `json:"sec_status"`
KillCount uint64 `json:"kill_count"`
Systems uint64 `json:"systems"`
}
// Ship aggregation results
type ShipAggregationByVictimShip struct {
ShipTypeID int32 `json:"victim_ship_type_id"`
ShipTypeName string `json:"victim_ship_type_name"`
ShipGroupName string `json:"victim_ship_group_name"`
ShipCategoryName string `json:"victim_ship_category_name"`
KillCount uint64 `json:"kill_count"`
UniquePilots uint64 `json:"unique_pilots_killed"`
}
type ShipAggregationByAttackerShip struct {
ShipTypeName string `json:"ship_type_name"`
ShipGroupName string `json:"ship_group_name"`
TimesUsed uint64 `json:"times_used"`
KillsParticipated uint64 `json:"kills_participated"`
TotalDamage int64 `json:"total_damage"`
FinalBlows uint64 `json:"final_blows"`
}
// Player/Entity aggregation results
type PlayerAggregationByVictimCharacter struct {
CharacterName string `json:"victim_character_name"`
Corporation string `json:"victim_corporation_name"`
Alliance string `json:"victim_alliance_name"`
Deaths uint64 `json:"deaths"`
ShipsLost uint64 `json:"ships_lost"`
}
type PlayerAggregationByVictimCorporation struct {
Corporation string `json:"victim_corporation_name"`
Alliance string `json:"victim_alliance_name"`
Losses uint64 `json:"losses"`
MembersKilled uint64 `json:"members_killed"`
}
type PlayerAggregationByVictimAlliance struct {
Alliance string `json:"victim_alliance_name"`
Losses uint64 `json:"losses"`
CorpsHit uint64 `json:"corps_hit"`
MembersKilled uint64 `json:"members_killed"`
}
type PlayerAggregationByAttackerCharacter struct {
CharacterName string `json:"character_name"`
Corporation string `json:"corporation_name"`
Alliance string `json:"alliance_name"`
KillsParticipated uint64 `json:"kills_participated"`
FinalBlows uint64 `json:"final_blows"`
TotalDamage int64 `json:"total_damage"`
}
type PlayerAggregationByAttackerCorporation struct {
Corporation string `json:"corporation_name"`
Alliance string `json:"alliance_name"`
KillsParticipated uint64 `json:"kills_participated"`
MembersInvolved uint64 `json:"members_involved"`
}
type PlayerAggregationByAttackerAlliance struct {
Alliance string `json:"alliance_name"`
KillsParticipated uint64 `json:"kills_participated"`
CorpsInvolved uint64 `json:"corps_involved"`
MembersInvolved uint64 `json:"members_involved"`
}
// Module/Item aggregation results
type ModuleAggregationBySlotType struct {
SlotType string `json:"slot_type"`
ItemsFitted uint64 `json:"items_fitted"`
ShipsWithSlot uint64 `json:"ships_with_slot"`
ModuleVariety uint64 `json:"module_variety"`
}
type ModuleAggregationByModule struct {
ItemTypeID int32 `json:"item_type_id"`
ItemTypeName string `json:"item_type_name"`
ItemGroupName string `json:"item_group_name"`
ItemCategoryName string `json:"item_category_name"`
TimesFitted uint64 `json:"times_fitted"`
ShipsWithModule uint64 `json:"ships_with_module"`
}
type ModuleCoOccurrence struct {
ItemTypeName string `json:"item_type_name"`
SlotType string `json:"slot_type"`
TimesTogether uint64 `json:"times_fitted_together"`
Percentage float64 `json:"percentage"`
}
// buildWhereClause builds a WHERE clause from filters
func buildWhereClause(filters types.AnalyticsFilters) (string, []interface{}) {
var conditions []string
var args []interface{}
if len(filters.KillHour) > 0 {
placeholders := make([]string, len(filters.KillHour))
for i := range placeholders {
placeholders[i] = "?"
}
conditions = append(conditions, "kill_hour IN ("+strings.Join(placeholders, ",")+")")
for _, h := range filters.KillHour {
args = append(args, h)
}
}
if len(filters.KillDayOfWeek) > 0 {
placeholders := make([]string, len(filters.KillDayOfWeek))
for i := range placeholders {
placeholders[i] = "?"
}
conditions = append(conditions, "kill_day_of_week IN ("+strings.Join(placeholders, ",")+")")
for _, d := range filters.KillDayOfWeek {
args = append(args, d)
}
}
if len(filters.KillDate) > 0 {
placeholders := make([]string, len(filters.KillDate))
for i := range placeholders {
placeholders[i] = "?"
}
conditions = append(conditions, "kill_date IN ("+strings.Join(placeholders, ",")+")")
for _, d := range filters.KillDate {
args = append(args, d)
}
}
if len(filters.Month) > 0 {
placeholders := make([]string, len(filters.Month))
for i := range placeholders {
placeholders[i] = "?"
}
conditions = append(conditions, "toYYYYMM(killmail_time) IN ("+strings.Join(placeholders, ",")+")")
for _, m := range filters.Month {
args = append(args, m)
}
}
if len(filters.SolarSystemID) > 0 {
placeholders := make([]string, len(filters.SolarSystemID))
for i := range placeholders {
placeholders[i] = "?"
}
conditions = append(conditions, "solar_system_id IN ("+strings.Join(placeholders, ",")+")")
for _, id := range filters.SolarSystemID {
args = append(args, id)
}
}
if len(filters.RegionName) > 0 {
placeholders := make([]string, len(filters.RegionName))
for i := range placeholders {
placeholders[i] = "?"
}
conditions = append(conditions, "region_name IN ("+strings.Join(placeholders, ",")+")")
for _, r := range filters.RegionName {
args = append(args, r)
}
}
if len(filters.ConstellationName) > 0 {
placeholders := make([]string, len(filters.ConstellationName))
for i := range placeholders {
placeholders[i] = "?"
}
conditions = append(conditions, "constellation_name IN ("+strings.Join(placeholders, ",")+")")
for _, c := range filters.ConstellationName {
args = append(args, c)
}
}
if len(filters.SecurityStatus) > 0 {
var secConditions []string
for _, s := range filters.SecurityStatus {
switch s {
case "High Sec":
secConditions = append(secConditions, "security >= 0.5")
case "Low Sec":
secConditions = append(secConditions, "security > 0.0 AND security < 0.5")
case "Null Sec":
secConditions = append(secConditions, "security <= 0.0")
}
}
if len(secConditions) > 0 {
conditions = append(conditions, "("+strings.Join(secConditions, " OR ")+")")
}
}
if len(filters.VictimShipTypeID) > 0 {
placeholders := make([]string, len(filters.VictimShipTypeID))
for i := range placeholders {
placeholders[i] = "?"
}
conditions = append(conditions, "victim_ship_type_id IN ("+strings.Join(placeholders, ",")+")")
for _, id := range filters.VictimShipTypeID {
args = append(args, id)
}
}
if len(filters.VictimShipGroupName) > 0 {
placeholders := make([]string, len(filters.VictimShipGroupName))
for i := range placeholders {
placeholders[i] = "?"
}
conditions = append(conditions, "victim_ship_group_name IN ("+strings.Join(placeholders, ",")+")")
for _, g := range filters.VictimShipGroupName {
args = append(args, g)
}
}
if len(filters.VictimShipCategory) > 0 {
placeholders := make([]string, len(filters.VictimShipCategory))
for i := range placeholders {
placeholders[i] = "?"
}
conditions = append(conditions, "victim_ship_category_name IN ("+strings.Join(placeholders, ",")+")")
for _, c := range filters.VictimShipCategory {
args = append(args, c)
}
}
if len(filters.VictimCharacterName) > 0 {
placeholders := make([]string, len(filters.VictimCharacterName))
for i := range placeholders {
placeholders[i] = "?"
}
conditions = append(conditions, "victim_character_name IN ("+strings.Join(placeholders, ",")+")")
for _, c := range filters.VictimCharacterName {
args = append(args, c)
}
}
if len(filters.VictimCorporation) > 0 {
placeholders := make([]string, len(filters.VictimCorporation))
for i := range placeholders {
placeholders[i] = "?"
}
conditions = append(conditions, "victim_corporation_name IN ("+strings.Join(placeholders, ",")+")")
for _, c := range filters.VictimCorporation {
args = append(args, c)
}
}
if len(filters.VictimAlliance) > 0 {
placeholders := make([]string, len(filters.VictimAlliance))
for i := range placeholders {
placeholders[i] = "?"
}
conditions = append(conditions, "victim_alliance_name IN ("+strings.Join(placeholders, ",")+")")
for _, a := range filters.VictimAlliance {
args = append(args, a)
}
}
if filters.HasModule != nil {
subquery := `SELECT DISTINCT killmail_id FROM zkill.killmail_items WHERE item_type_id = ? AND slot_type != 'cargo'`
conditions = append(conditions, fmt.Sprintf("killmail_id IN (%s)", subquery))
args = append(args, filters.HasModule.ModuleID)
}
whereClause := ""
if len(conditions) > 0 {
whereClause = "WHERE " + strings.Join(conditions, " AND ")
}
return whereClause, args
}
// buildAttackerWhereClause builds WHERE clause for attacker queries
func buildAttackerWhereClause(filters types.AnalyticsFilters) (string, []interface{}) {
var conditions []string
var args []interface{}
if len(filters.AttackerShipType) > 0 {
placeholders := make([]string, len(filters.AttackerShipType))
for i := range placeholders {
placeholders[i] = "?"
}
conditions = append(conditions, "ship_type_name IN ("+strings.Join(placeholders, ",")+")")
for _, s := range filters.AttackerShipType {
args = append(args, s)
}
}
if len(filters.AttackerShipGroup) > 0 {
placeholders := make([]string, len(filters.AttackerShipGroup))
for i := range placeholders {
placeholders[i] = "?"
}
conditions = append(conditions, "ship_group_name IN ("+strings.Join(placeholders, ",")+")")
for _, g := range filters.AttackerShipGroup {
args = append(args, g)
}
}
if len(filters.AttackerCharacter) > 0 {
placeholders := make([]string, len(filters.AttackerCharacter))
for i := range placeholders {
placeholders[i] = "?"
}
conditions = append(conditions, "character_name IN ("+strings.Join(placeholders, ",")+")")
for _, c := range filters.AttackerCharacter {
args = append(args, c)
}
}
if len(filters.AttackerCorporation) > 0 {
placeholders := make([]string, len(filters.AttackerCorporation))
for i := range placeholders {
placeholders[i] = "?"
}
conditions = append(conditions, "corporation_name IN ("+strings.Join(placeholders, ",")+")")
for _, c := range filters.AttackerCorporation {
args = append(args, c)
}
}
if len(filters.AttackerAlliance) > 0 {
placeholders := make([]string, len(filters.AttackerAlliance))
for i := range placeholders {
placeholders[i] = "?"
}
conditions = append(conditions, "alliance_name IN ("+strings.Join(placeholders, ",")+")")
for _, a := range filters.AttackerAlliance {
args = append(args, a)
}
}
whereClause := ""
if len(conditions) > 0 {
whereClause = "AND " + strings.Join(conditions, " AND ")
}
return whereClause, args
}
// getKillmailIDSubquery returns a subquery to filter by killmail_id from killmails table
func getKillmailIDSubquery(filters types.AnalyticsFilters) (string, []interface{}) {
whereClause, args := buildWhereClause(filters)
if whereClause == "" {
return "(SELECT killmail_id FROM zkill.killmails)", args
}
return fmt.Sprintf("(SELECT killmail_id FROM zkill.killmails %s)", whereClause), args
}