Skip to content

Commit

Permalink
Some column updates (#30)
Browse files Browse the repository at this point in the history
* Reorder columns to emphasize totals

* faction lookups

* be explicity about ships in multiple factions

* Good enough way to to get unique

---------

Co-authored-by: Antony Saba <antony@sabatechconsulting.com>
  • Loading branch information
awsaba and Antony Saba authored Jun 19, 2024
1 parent 719815e commit 2a82d48
Show file tree
Hide file tree
Showing 3 changed files with 144 additions and 66 deletions.
2 changes: 1 addition & 1 deletion src/expansions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ mod test {
continue;
}
let result = match item_count.item.r#type {
ItemType::Ship => d.get_ship(&item_count.item.xws).is_some(),
ItemType::Ship => d.get_ship_model(&item_count.item.xws).is_some(),
ItemType::Pilot => d.get_pilot(&item_count.item.xws).is_some(),
ItemType::Upgrade => d.get_upgrade(&item_count.item.xws).is_some(),
_ => continue,
Expand Down
157 changes: 96 additions & 61 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ impl Collection {
pub struct ShipRecord {
pub name: String,
pub xws: String,
pub factions: String,

pub count: u32,

Expand All @@ -89,11 +90,12 @@ pub struct ShipRecord {
impl ShipRecord {
/// Turns skus and xws id's into display names.
pub fn build(xws: &str, count: u32, data: &Data, catalog: &Catalog) -> Result<Self, ErrorKind> {
match data.get_ship(xws) {
match data.get_ship_model(xws) {
None => Err(ErrorKind::NotFound),
Some(s) => Ok(Self {
name: s.name.to_owned(),
xws: s.xws.to_owned(),
name: s.name,
xws: s.xws,
factions: s.faction,
sources: catalog
.sources
.get(&Item {
Expand Down Expand Up @@ -135,7 +137,9 @@ impl PilotRecord {
match data.get_pilot(xws) {
None => Err(ErrorKind::NotFound),
Some((s, p)) => Ok(Self {
faction: s.faction.to_owned(),
faction: data
.get_faction(s.faction.as_str())
.map_or(s.faction.to_owned(), |f| f.name.to_owned()),
ship: s.name.to_owned(),
name: p.name.to_owned(),
xws: p.xws.to_owned(),
Expand Down Expand Up @@ -239,10 +243,7 @@ fn format_restriction(
Restriction::Ships => &r
.ships
.iter()
.map(|xws| {
data.get_ship(xws.as_str())
.map_or(xws.as_str(), |s| s.name.as_str())
})
.map(|xws| data.get_ship_name(xws.as_str()).unwrap_or(xws.as_str()))
.for_each(|v| tmp.push(v)),
Restriction::Sizes => &r.sizes.iter().for_each(|v| tmp.push(v)),
Restriction::Arcs => &r.arcs.iter().for_each(|v| tmp.push(v)),
Expand Down Expand Up @@ -379,7 +380,6 @@ fn total_func(item: &Item, singles_cell: String, catalog: &Catalog) -> String {
func
}

const SHIP_COLS: [&str; 5] = ["Name", "Total", "Singles", "XWS", "Sources"];
fn add_ships_sheet(
workbook: &mut Workbook,
catalog: &Catalog,
Expand All @@ -388,15 +388,12 @@ fn add_ships_sheet(
inventory: &BTreeMap<Item, u32>,
) -> Result<(), XlsxError> {
let ships = workbook.add_worksheet().set_name("Ships")?;
for (i, col) in SHIP_COLS.iter().enumerate() {
ships.write(0, i as u16, *col)?;
}

let mut ship_row = 1;
let ship_singles_col = 2;
for item in inventory.keys() {
if item.r#type == ItemType::Ship {
let model = match data.get_ship(&item.xws) {
let model = match data.get_ship_model(&item.xws) {
Some(m) => m,
None => {
println!("xslx: missing ship {}", item.xws);
Expand All @@ -415,10 +412,11 @@ fn add_ships_sheet(
2,
*collection.singles.get(item).unwrap_or(&0) as i32,
)?;
ships.write(ship_row, 3, &model.xws)?;
ships.write(ship_row, 3, &model.faction)?;
ships.write(ship_row, 4, &item.xws)?;
ships.write(
ship_row,
4,
5,
catalog
.sources
.get(item)
Expand All @@ -429,10 +427,29 @@ fn add_ships_sheet(
ship_row += 1;
}
}
let columns = vec![
TableColumn::new()
.set_header("Name")
.set_total_label("Totals"),
TableColumn::new()
.set_header("Total")
.set_total_function(TableFunction::Sum),
TableColumn::new()
.set_header("Singles")
.set_total_function(TableFunction::Sum),
TableColumn::new().set_header("Factions"),
TableColumn::new()
.set_header("XWS")
.set_total_function(TableFunction::Count),
TableColumn::new().set_header("Sources"),
];
let mut table = Table::new();
table.set_name("ShipTable");
table.set_style(TableStyle::Medium3);
ships.add_table(0, 0, ship_row - 1, SHIP_COLS.len() as u16 - 1, &table)?;
let table = table
.set_name("ShipTable")
.set_style(TableStyle::Medium3)
.set_columns(&columns)
.set_total_row(true);
ships.add_table(0, 0, ship_row, columns.len() as u16 - 1, &table)?;
ships.autofit();
Ok(())
}
Expand All @@ -447,7 +464,7 @@ fn add_pilots_sheet(
let pilots = workbook.add_worksheet().set_name("Pilots")?;

let mut pilot_row = 1;
let pilot_singles_col = 7;
let pilot_singles_col = 4;
for item in inventory.keys() {
if item.r#type == ItemType::Pilot {
// TODO: probably don't need to
Expand All @@ -459,33 +476,41 @@ fn add_pilots_sheet(
}
};

pilots.write(pilot_row, 0, &ship.name)?;
pilots.write(pilot_row, 1, &pilot.name)?;
pilots.write(pilot_row, 2, pilot.initiative)?;
pilots.write(pilot_row, 0, &pilot.name)?;
pilots.write(pilot_row, 1, &ship.name)?;
pilots.write(
pilot_row,
3,
2,
pilot.caption.as_ref().map_or_else(|| "", |c| c.as_str()),
)?;
pilots.write(pilot_row, 4, &ship.faction)?;

pilots.write_dynamic_formula(
pilot_row,
3,
total_func(item, row_col_to_cell(pilot_row, pilot_singles_col), catalog).as_str(),
)?;
pilots.write(
pilot_row,
5,
pilot
.standard_loadout
.as_ref()
.map_or_else(|| false, |v| !v.is_empty()),
4,
*collection.singles.get(item).unwrap_or(&0) as i32,
)?;
pilots.write_dynamic_formula(

pilots.write(
pilot_row,
6,
total_func(item, row_col_to_cell(pilot_row, pilot_singles_col), catalog).as_str(),
5,
data.get_faction(ship.faction.as_str())
.map_or(ship.faction.to_owned(), |f| f.name.to_owned()),
)?;
pilots.write(pilot_row, 6, pilot.initiative)?;
pilots.write(
pilot_row,
7,
*collection.singles.get(item).unwrap_or(&0) as i32,
pilot
.standard_loadout
.as_ref()
.map_or_else(|| false, |v| !v.is_empty()),
)?;

pilots.write(pilot_row, 8, &pilot.xws)?;
pilots.write(
pilot_row,
Expand All @@ -500,27 +525,32 @@ fn add_pilots_sheet(
pilot_row += 1;
}
}
let mut table = Table::new();
table.set_name("pilotTable");
table.set_style(TableStyle::Medium4);
table.set_total_row(true);
let columns = vec![
TableColumn::new()
.set_header("Ship")
.set_total_label("Total"),
TableColumn::new().set_header("Name"),
TableColumn::new().set_header("Initiative"),
.set_header("Name")
.set_total_label("Totals"),
TableColumn::new().set_header("Ship"),
TableColumn::new().set_header("Caption"),
TableColumn::new().set_header("Faction"),
TableColumn::new().set_header("Standard Loadout"),
TableColumn::new()
.set_header("Total")
.set_total_function(TableFunction::Sum),
TableColumn::new().set_header("Singles"),
TableColumn::new().set_header("XWS"),
TableColumn::new()
.set_header("Singles")
.set_total_function(TableFunction::Sum),
TableColumn::new().set_header("Faction"),
TableColumn::new().set_header("Initiative"),
TableColumn::new().set_header("Standard Loadout"),
TableColumn::new()
.set_header("XWS")
.set_total_function(TableFunction::Count),
TableColumn::new().set_header("Sources"),
];
table.set_columns(&columns);
let mut table = Table::new();
let table = table
.set_name("pilotTable")
.set_style(TableStyle::Medium4)
.set_columns(&columns)
.set_total_row(true);
pilots.add_table(0, 0, pilot_row, columns.len() as u16 - 1, &table)?;
pilots.autofit();
Ok(())
Expand All @@ -536,7 +566,7 @@ fn add_upgrades_sheet(
let upgrades = workbook.add_worksheet().set_name("Upgrades")?;

let mut upgrade_row = 1;
let upgrade_singles_col = 9;
let upgrade_singles_col = 3;
for item in inventory.keys() {
if item.r#type == ItemType::Upgrade {
let upgrade = match data.get_upgrade(&item.xws) {
Expand All @@ -552,16 +582,9 @@ fn add_upgrades_sheet(
upgrades.write(upgrade_row, 0, &upgrade.name)?;
upgrades.write(upgrade_row, 1, &record.r#type)?;

upgrades.write(upgrade_row, 2, &record.faction_restriction)?;
upgrades.write(upgrade_row, 3, &record.ship_restriction)?;
upgrades.write(upgrade_row, 4, &record.size_restriction)?;
upgrades.write(upgrade_row, 5, &record.arc_restriction)?;
upgrades.write(upgrade_row, 6, &record.force_side_restriction)?;
upgrades.write(upgrade_row, 7, &record.keyword_restriction)?;

upgrades.write_dynamic_formula(
upgrade_row,
8,
2,
total_func(
item,
row_col_to_cell(upgrade_row, upgrade_singles_col),
Expand All @@ -571,9 +594,17 @@ fn add_upgrades_sheet(
)?;
upgrades.write(
upgrade_row,
9,
upgrade_singles_col,
*collection.singles.get(item).unwrap_or(&0) as i32,
)?;

upgrades.write(upgrade_row, 4, &record.faction_restriction)?;
upgrades.write(upgrade_row, 5, &record.ship_restriction)?;
upgrades.write(upgrade_row, 6, &record.size_restriction)?;
upgrades.write(upgrade_row, 7, &record.arc_restriction)?;
upgrades.write(upgrade_row, 8, &record.force_side_restriction)?;
upgrades.write(upgrade_row, 9, &record.keyword_restriction)?;

upgrades.write(upgrade_row, 10, &upgrade.xws)?;
upgrades.write(
upgrade_row,
Expand All @@ -595,19 +626,23 @@ fn add_upgrades_sheet(
let columns = vec![
TableColumn::new()
.set_header("Name")
.set_total_label("Total"),
.set_total_label("Totals"),
TableColumn::new().set_header("Type"),
TableColumn::new()
.set_header("Total")
.set_total_function(TableFunction::Sum),
TableColumn::new()
.set_header("Singles")
.set_total_function(TableFunction::Sum),
TableColumn::new().set_header("Faction Restriction"),
TableColumn::new().set_header("Ship Restriction"),
TableColumn::new().set_header("Size Restriction"),
TableColumn::new().set_header("Arc Restriction"),
TableColumn::new().set_header("Force Side Restriction"),
TableColumn::new().set_header("Keyword Restriction"),
TableColumn::new()
.set_header("Total")
.set_total_function(TableFunction::Sum),
TableColumn::new().set_header("Singles"),
TableColumn::new().set_header("XWS"),
.set_header("XWS")
.set_total_function(TableFunction::Count),
TableColumn::new().set_header("Sources"),
];
table.set_columns(&columns);
Expand Down
51 changes: 47 additions & 4 deletions src/xwingdata2/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,6 @@ fn load_type<T: for<'a> Deserialize<'a>>(root: &Path, paths: &[String]) -> Resul
let mut result = Vec::new();

for path in paths {
//println!("loading: {}", &faction_path);
let path = root.join(path);
let buffer = fs::read_to_string(path)?;
let mut factions: Vec<T> = serde_json::from_str(&buffer)?;
Expand Down Expand Up @@ -291,12 +290,56 @@ impl Data {
self.upgrades.iter().find(|&u| u.xws == xws)
}

pub fn get_ship(&self, xws: &str) -> Option<&Ship> {
self.ships.iter().find(|&s| s.xws == xws)
/// Return the name of the first ship matching the given xws.
pub fn get_ship_name(&self, xws: &str) -> Option<&str> {
self.ships
.iter()
.find(|&s| s.xws == xws)
.map(|s| s.name.as_str())
}

/// Returns a combined copy of just the model info, with factions joined
/// into a single string. The models share the same xws ID, but are
/// listed multiple times across factions for some ships
pub fn get_ship_model(&self, xws: &str) -> Option<Ship> {
match self
.ships
.iter()
.filter(|s| s.xws == xws)
.collect::<Vec<&Ship>>()
.as_slice()
{
[] => None,
[s] => Some(Ship {
name: s.name.clone(),
xws: s.name.clone(),
faction: self
.factions
.iter()
.find(|f| f.xws == s.faction)
.map_or(s.faction.clone(), |f| f.name.clone()),
pilots: vec![],
}),
s => Some(Ship {
name: s[0].name.clone(),
xws: s[0].xws.clone(),
faction: s
.iter()
.map(|s| {
self.factions
.iter()
.find(|f| f.xws == s.faction)
.map_or(s.faction.as_str(), |f| f.name.as_str())
})
.collect::<Vec<&str>>()
.join(","),
pilots: vec![],
}),
}
}

pub fn get_faction(&self, xws: &str) -> Option<&Faction> {
self.factions.iter().find(|&s| s.xws == xws)
self.factions.iter().find(|&f| f.xws == xws)
}
}

Expand Down

0 comments on commit 2a82d48

Please sign in to comment.