Skip to content

Automated tournament shortened name derivation#112

Merged
Mateusz-Dobrzynski merged 9 commits into
masterfrom
SCRUM-94-Make-tournament-shortened-name-derivation-automatic
May 26, 2026
Merged

Automated tournament shortened name derivation#112
Mateusz-Dobrzynski merged 9 commits into
masterfrom
SCRUM-94-Make-tournament-shortened-name-derivation-automatic

Conversation

@coffeeaddictamoeba
Copy link
Copy Markdown
Contributor

@coffeeaddictamoeba coffeeaddictamoeba commented May 18, 2026

Automated tournament shortened name derivation
Modified all tournaments-related files so shortened name is generated automatically on the tournament name basis.
Example of name shortening:
"ThisIsMyTestTournament" -> "T20tl0mk"

Logic behind name shortening:

  1. Take the first letter: "T"
  2. Take the length of name: "20"
  3. Take the last letter: "t"
  4. Add a unique 4-character sequence to prevent repetitions: "l0mk"

Testing

  • I have covered my code with tests.
  • I have run integration tests with cargo test --tests and ensured that they pass.

Comment thread src/tournaments/mod.rs
Comment on lines 145 to -212
@@ -206,10 +210,11 @@ impl Tournament {
patch: TournamentPatch,
pool: &Pool<Postgres>,
) -> Result<Tournament, OmniError> {
let name = patch.full_name.unwrap_or(self.full_name);
let tournament = Tournament {
id: self.id,
full_name: patch.full_name.unwrap_or(self.full_name),
shortened_name: patch.shortened_name.unwrap_or(self.shortened_name),
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Provided that my understanding is correct, the shortened name will always be generated using your function. This is wrong, as the user should be able to define their own shortened name, which is a part of a tournament branding.

Comment thread src/tournaments/mod.rs Outdated
Comment on lines +377 to +429
fn shorten(word: &str) -> String {
let len = word.chars().count();
let id = short_id();

if len <= 5 {
return capitalize(&format!("{}{}", word, id));
}

let first = word.chars().next().unwrap();
let last = word.chars().last().unwrap();

capitalize(&format!("{}{}{}{}", first, len - 2, last, id))
}

fn short_id() -> String {
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_nanos();

let count = COUNTER.fetch_add(1, Ordering::Relaxed) as u128;

let value = now ^ count;

base36(value % 1679616) // 36^4 = 4 chars
}

fn base36(mut value: u128) -> String {
const CHARS: &[u8] = b"0123456789abcdefghijklmnopqrstuvwxyz";

if value == 0 {
return "0".to_string();
}

let mut result = Vec::new();

while value > 0 {
let index = (value % 36) as usize;
result.push(CHARS[index] as char);
value /= 36;
}

result.iter().rev().collect()
}

fn capitalize(word: &str) -> String {
let mut chars = word.chars();

match chars.next() {
None => String::new(),
Some(first) => first.to_uppercase().collect::<String>() + chars.as_str(),
}
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you try so hard to guarantee the shortened name's uniqueness, while there's aren't any business requirements imposing it? Given the task at hand (automatically shortening a tournament's name), don't you think this piece of code is overly complex? Also, this is purely subjective, but I can imagine some users perceiving random, inexplicable strings with numbers and letters to be straight-out ugly. Capitalization of first letters of each word (e.g. This is a test tournament => TIATT would be enough.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While the existing tests are modified, the implemented feature isn't. This should be changed.

Copy link
Copy Markdown
Member

@Mateusz-Dobrzynski Mateusz-Dobrzynski left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Introduce missing test cases.
  • Remove the shorten function duplicate.

Important: when you're done, make sure to re-request my review on this PR, otherwise I won't get notified that it's ready.

Comment on lines 35 to 48
async fn tournament_creation_should_be_possible_for_infrastructure_admin() {
// GIVEN
let app = TestApp::spawn().await;
let short_name_str = "WrLD";
let shortened_name: Option<String> = Some(short_name_str.to_string());

// WHEN
let token = get_session_token_for_infrastructure_admin(&app).await;
let res = create_tournament(&app, "Wrocławska Liga Debat", &token).await;
let res =
create_tournament(&app, "Wrocławska Liga Debat", shortened_name, &token).await;

// THEN
assert_eq!(res.status(), StatusCode::OK);
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There should be an assertion on whether the user-defined shortened name is preserved or not. Similarly, generating the shortened name should be tested.

Comment on lines 94 to 124
fn shorten(name: &str) -> String {
let words: Vec<String> = name
.split_whitespace()
.map(|word| {
word.chars()
.filter(|c| c.is_alphabetic())
.collect::<String>()
})
.filter(|word| !word.is_empty())
.collect();

let mut result = String::new();

match words.len() {
0 => {}
1 => {
result.extend(words[0].chars().take(3));
}
2 => {
result.extend(words[0].chars().take(2));
result.extend(words[1].chars().take(1));
}
_ => {
for word in words.iter().take(3) {
result.extend(word.chars().take(1));
}
}
}

result.iter().rev().collect()
capitalize(&result)
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this function declared both here and in mod.rs? Can't it be defined in one place and imported in the other?

Comment thread src/tournaments/mod.rs Outdated
Comment on lines 378 to 408
fn shorten(name: &str) -> String {
let words: Vec<String> = name
.split_whitespace()
.map(|word| {
word.chars()
.filter(|c| c.is_alphabetic())
.collect::<String>()
})
.filter(|word| !word.is_empty())
.collect();

let mut result = String::new();

match words.len() {
0 => {}
1 => {
result.extend(words[0].chars().take(3));
}
2 => {
result.extend(words[0].chars().take(2));
result.extend(words[1].chars().take(1));
}
_ => {
for word in words.iter().take(3) {
result.extend(word.chars().take(1));
}
}
}

result.iter().rev().collect()
capitalize(&result)
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please cover this function with a unit test.

Copy link
Copy Markdown
Member

@Mateusz-Dobrzynski Mateusz-Dobrzynski left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems that the only thing to fix now are the merge conflicts.

@Mateusz-Dobrzynski Mateusz-Dobrzynski merged commit 656b20b into master May 26, 2026
2 checks passed
@Mateusz-Dobrzynski Mateusz-Dobrzynski deleted the SCRUM-94-Make-tournament-shortened-name-derivation-automatic branch May 26, 2026 08:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants