Compare commits

...

3 Commits

Author SHA1 Message Date
Emmy D'Anello 2e99b3ea8e
Fix GSheet parser
Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
2024-07-06 10:08:38 +02:00
Emmy D'Anello 9721898731
Fix GSheet column width
Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
2024-07-06 10:03:27 +02:00
Emmy D'Anello 5c3b3d26c8
Fix GSheet translated texts
Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
2024-07-06 09:41:41 +02:00
1 changed files with 53 additions and 44 deletions

View File

@ -470,18 +470,19 @@ class Tournament(models.Model):
gc = gspread.service_account_from_dict(settings.GOOGLE_SERVICE_CLIENT) gc = gspread.service_account_from_dict(settings.GOOGLE_SERVICE_CLIENT)
spreadsheet = gc.open_by_key(self.notes_sheet_id) spreadsheet = gc.open_by_key(self.notes_sheet_id)
worksheets = spreadsheet.worksheets() worksheets = spreadsheet.worksheets()
if _("Final ranking") not in [ws.title for ws in worksheets]: if str(_("Final ranking")) not in [ws.title for ws in worksheets]:
worksheet = spreadsheet.add_worksheet(_("Final ranking"), 30, 10) worksheet = spreadsheet.add_worksheet(str(_("Final ranking")), 30, 10)
else: else:
worksheet = spreadsheet.worksheet(_("Final ranking")) worksheet = spreadsheet.worksheet(str(_("Final ranking")))
if worksheet.index != self.pools.count(): if worksheet.index != self.pools.count():
worksheet.update_index(self.pools.count()) worksheet.update_index(self.pools.count())
header = [[_("Team"), _("Scores day 1"), _("Tweaks day 1"), _("Scores day 2"), _("Tweaks day 2")] header = [[str(_("Team")), str(_("Scores day 1")), str(_("Tweaks day 1")),
+ ([_("Total D1 + D2"), _("Scores day 3"), _("Tweaks day 3")] str(_("Scores day 2")), str(_("Tweaks day 2"))]
+ ([str(_("Total D1 + D2")), str(_("Scores day 3")), str(_("Tweaks day 3"))]
if settings.NB_ROUNDS >= 3 else []) if settings.NB_ROUNDS >= 3 else [])
+ [_("Total"), _("Rank")]] + [str(_("Total")), str(_("Rank"))]]
lines = [] lines = []
participations = self.participations.filter(pools__round=1, pools__tournament=self).distinct().all() participations = self.participations.filter(pools__round=1, pools__tournament=self).distinct().all()
total_col, rank_col = ("F", "G") if settings.NB_ROUNDS == 2 else ("I", "J") total_col, rank_col = ("F", "G") if settings.NB_ROUNDS == 2 else ("I", "J")
@ -548,7 +549,8 @@ class Tournament(models.Model):
+ (f" + (PI() - 2) * $G{i + 2} + $H{i + 2}" if settings.NB_ROUNDS >= 3 else "")) + (f" + (PI() - 2) * $G{i + 2} + $H{i + 2}" if settings.NB_ROUNDS >= 3 else ""))
line.append(f"=RANG(${total_col}{i + 2}; ${total_col}$2:${total_col}${participations.count() + 1})") line.append(f"=RANG(${total_col}{i + 2}; ${total_col}$2:${total_col}${participations.count() + 1})")
final_ranking = [["", "", "", ""], ["", "", "", ""], [_("Team"), _("Score"), _("Rank"), _("Mention")], final_ranking = [["", "", "", ""], ["", "", "", ""],
[str(_("Team")), str(_("Score")), str(_("Rank")), str(_("Mention"))],
[f"=SORT($A$2:$A${participations.count() + 1}; " [f"=SORT($A$2:$A${participations.count() + 1}; "
f"${total_col}$2:${total_col}${participations.count() + 1}; FALSE)", f"${total_col}$2:${total_col}${participations.count() + 1}; FALSE)",
f"=SORT(${total_col}$2:${total_col}${participations.count() + 1}; " f"=SORT(${total_col}$2:${total_col}${participations.count() + 1}; "
@ -573,7 +575,7 @@ class Tournament(models.Model):
format_requests = [] format_requests = []
# Set the width of the columns # Set the width of the columns
column_widths = [("A", 300), ("B", 150), ("C", 150), ("D", 150), ("E", 150), ("F", 150), ("G", 150), column_widths = [("A", 350), ("B", 150), ("C", 150), ("D", 150), ("E", 150), ("F", 150), ("G", 150),
("H", 150), ("I", 150), ("J", 150)] ("H", 150), ("I", 150), ("J", 150)]
for column, width in column_widths: for column, width in column_widths:
grid_range = a1_range_to_grid_range(column, worksheet.id) grid_range = a1_range_to_grid_range(column, worksheet.id)
@ -638,7 +640,7 @@ class Tournament(models.Model):
(f"A{participations.count() + 5}:C{2 * participations.count() + 4}", (0.9, 0.9, 0.9)),] (f"A{participations.count() + 5}:C{2 * participations.count() + 4}", (0.9, 0.9, 0.9)),]
if settings.NB_ROUNDS >= 3: if settings.NB_ROUNDS >= 3:
bg_colors.append((f"F2:G{participations.count() + 1}", (0.9, 0.9, 0.9))) bg_colors.append((f"F2:G{participations.count() + 1}", (0.9, 0.9, 0.9)))
bg_colors.append((f"H2:I{participations.count() + 1}", (0.9, 0.9, 0.9))) bg_colors.append((f"I2:J{participations.count() + 1}", (0.9, 0.9, 0.9)))
else: else:
bg_colors.append((f"F2:G{participations.count() + 1}", (0.9, 0.9, 0.9))) bg_colors.append((f"F2:G{participations.count() + 1}", (0.9, 0.9, 0.9)))
for bg_range, bg_color in bg_colors: for bg_range, bg_color in bg_colors:
@ -693,7 +695,7 @@ class Tournament(models.Model):
"addProtectedRange": { "addProtectedRange": {
"protectedRange": { "protectedRange": {
"range": a1_range_to_grid_range(protected_range, worksheet.id), "range": a1_range_to_grid_range(protected_range, worksheet.id),
"description": _("Don't update the table structure for a better automated integration."), "description": str(_("Don't update the table structure for a better automated integration.")),
"warningOnly": True, "warningOnly": True,
}, },
} }
@ -711,9 +713,9 @@ class Tournament(models.Model):
gc = gspread.service_account_from_dict(settings.GOOGLE_SERVICE_CLIENT) gc = gspread.service_account_from_dict(settings.GOOGLE_SERVICE_CLIENT)
spreadsheet = gc.open_by_key(self.notes_sheet_id) spreadsheet = gc.open_by_key(self.notes_sheet_id)
worksheet = spreadsheet.worksheet(_("Final ranking")) worksheet = spreadsheet.worksheet(str(_("Final ranking")))
score_cell = worksheet.find(_("Score")) score_cell = worksheet.find(str(_("Score")))
max_row = score_cell.row - 3 max_row = score_cell.row - 3
if max_row == 1: if max_row == 1:
# There is no team # There is no team
@ -1212,6 +1214,11 @@ class Pool(models.Model):
def update_spreadsheet(self): # noqa: C901 def update_spreadsheet(self): # noqa: C901
translation.activate(settings.PREFERRED_LANGUAGE_CODE) translation.activate(settings.PREFERRED_LANGUAGE_CODE)
pool_size = self.participations.count()
has_observer = settings.TFJM_APP == "ETEAM" and pool_size >= 4
passage_width = 6 + (2 if has_observer else 0)
passages = self.passages.all()
# Create tournament sheet if it does not exist # Create tournament sheet if it does not exist
self.tournament.create_spreadsheet() self.tournament.create_spreadsheet()
@ -1219,30 +1226,26 @@ class Pool(models.Model):
spreadsheet = gc.open_by_key(self.tournament.notes_sheet_id) spreadsheet = gc.open_by_key(self.tournament.notes_sheet_id)
worksheets = spreadsheet.worksheets() worksheets = spreadsheet.worksheets()
if f"{_('Pool')} {self.short_name}" not in [ws.title for ws in worksheets]: if f"{_('Pool')} {self.short_name}" not in [ws.title for ws in worksheets]:
worksheet = spreadsheet.add_worksheet(f"{_('Pool')} {self.short_name}", 100, 34) worksheet = spreadsheet.add_worksheet(f"{_('Pool')} {self.short_name}",
30, 2 + passages.count() * passage_width)
else: else:
worksheet = spreadsheet.worksheet(f"{_('Pool')} {self.short_name}") worksheet = spreadsheet.worksheet(f"{_('Pool')} {self.short_name}")
if any(ws.title == "Sheet1" for ws in worksheets): if any(ws.title == "Sheet1" for ws in worksheets):
spreadsheet.del_worksheet(spreadsheet.worksheet("Sheet1")) spreadsheet.del_worksheet(spreadsheet.worksheet("Sheet1"))
pool_size = self.participations.count()
has_observer = settings.TFJM_APP == "ETEAM" and pool_size >= 4
passage_width = 6 + (2 if has_observer else 0)
passages = self.passages.all()
header = [ header = [
sum(([_("Problem #{problem}").format(problem=passage.solution_number)] + (passage_width - 1) * [""] sum(([str(_("Problem #{problem}").format(problem=passage.solution_number))] + (passage_width - 1) * [""]
for passage in passages), start=[_("Problem"), ""]), for passage in passages), start=[str(_("Problem")), ""]),
sum(([f"{_('Defender')} ({passage.defender.team.trigram})", "", sum(([f"{_('Defender')} ({passage.defender.team.trigram})", "",
f"{_('Opponent')} ({passage.opponent.team.trigram})", "", f"{_('Opponent')} ({passage.opponent.team.trigram})", "",
f"{_('Reviewer')} ({passage.reviewer.team.trigram})", ""] f"{_('Reviewer')} ({passage.reviewer.team.trigram})", ""]
+ ([f"{('Observer')} ({passage.observer.team.trigram})", ""] if has_observer else []) + ([f"{_('Observer')} ({passage.observer.team.trigram})", ""] if has_observer else [])
for passage in passages), start=["Rôle", ""]), for passage in passages), start=[str(_("Role")), ""]),
sum(([f"{_('Writing')} (/{20 if settings.TFJM_APP == "TFJM" else 10})", sum(([f"{_('Writing')} (/{20 if settings.TFJM_APP == "TFJM" else 10})",
f"{_('Oral')} (/{20 if settings.TFJM_APP == 'TFJM' else 10})" f"{_('Oral')} (/{20 if settings.TFJM_APP == 'TFJM' else 10})",
f"{_('Writing')} (/10)", f"{_('Oral')} (/10)", f"{_('Writing')} (/10)", f"{_('Oral')} (/10)"] f"{_('Writing')} (/10)", f"{_('Oral')} (/10)", f"{_('Writing')} (/10)", f"{_('Oral')} (/10)"]
+ ([f"{_('Writing')} (/10)", f"{_('Oral')} (/10)"] if has_observer else []) + ([f"{_('Writing')} (/10)", f"{_('Oral')} (/10)"] if has_observer else [])
for _passage in passages), start=[_("Juree"), ""]), for _passage in passages), start=[str(_("Juree")), ""]),
] ]
notes = [[]] # Begin with empty hidden line to ensure pretty design notes = [[]] # Begin with empty hidden line to ensure pretty design
@ -1265,15 +1268,15 @@ class Pool(models.Model):
return '' return ''
return getcol((number - 1) // 26) + chr(65 + (number - 1) % 26) return getcol((number - 1) // 26) + chr(65 + (number - 1) % 26)
average = [_("Average"), ""] average = [str(_("Average")), ""]
coeffs = sum(([passage.coeff_defender_writing, passage.coeff_defender_oral, coeffs = sum(([passage.coeff_defender_writing, passage.coeff_defender_oral,
passage.coeff_opponent_writing, passage.coeff_opponent_oral, passage.coeff_opponent_writing, passage.coeff_opponent_oral,
passage.coeff_reviewer_writing, passage.coeff_reviewer_oral] passage.coeff_reviewer_writing, passage.coeff_reviewer_oral]
+ ([passage.coeff_observer_writing, passage.coeff_observer_oral] if has_observer else []) + ([passage.coeff_observer_writing, passage.coeff_observer_oral] if has_observer else [])
for passage in passages), for passage in passages),
start=[_("Coefficient"), ""]) start=[str(_("Coefficient")), ""])
subtotal = [_("Subtotal"), ""] subtotal = [str(_("Subtotal")), ""]
footer = [average, coeffs, subtotal, 34 * [""]] footer = [average, coeffs, subtotal, (2 + pool_size * passage_width) * [""]]
min_row = 5 min_row = 5
max_row = min_row + self.juries.count() max_row = min_row + self.juries.count()
@ -1306,7 +1309,7 @@ class Pool(models.Model):
f" + {obs_o_col}{max_row + 1} * {obs_o_col}{max_row + 2}", ""]) f" + {obs_o_col}{max_row + 1} * {obs_o_col}{max_row + 2}", ""])
ranking = [ ranking = [
[_("Team"), "", _("Problem"), _("Total"), _("Rank")], [str(_("Team")), "", str(_("Problem")), str(_("Total")), str(_("Rank"))],
] ]
all_passages = Passage.objects.filter(pool__tournament=self.tournament, all_passages = Passage.objects.filter(pool__tournament=self.tournament,
pool__round=self.round, pool__round=self.round,
@ -1352,8 +1355,9 @@ class Pool(models.Model):
all_values = header + notes + footer + ranking all_values = header + notes + footer + ranking
worksheet.batch_clear([f"A1:AH{max_row + 5 + pool_size}"]) max_col = getcol(2 + pool_size * passage_width)
worksheet.update("A1:AH", all_values, raw=False) worksheet.batch_clear([f"A1:{max_col}{max_row + 5 + pool_size}"])
worksheet.update(all_values, f"A1:{max_col}", raw=False)
format_requests = [] format_requests = []
@ -1372,6 +1376,11 @@ class Pool(models.Model):
f":{getcol(6 + i * passage_width)}{max_row + 3}") f":{getcol(6 + i * passage_width)}{max_row + 3}")
merge_cells.append(f"{getcol(7 + i * passage_width)}{max_row + 3}" merge_cells.append(f"{getcol(7 + i * passage_width)}{max_row + 3}"
f":{getcol(8 + i * passage_width)}{max_row + 3}") f":{getcol(8 + i * passage_width)}{max_row + 3}")
if has_observer:
merge_cells.append(f"{getcol(9 + i * passage_width)}2:{getcol(10 + i * passage_width)}2")
merge_cells.append(f"{getcol(9 + i * passage_width)}{max_row + 3}"
f":{getcol(10 + i * passage_width)}{max_row + 3}")
merge_cells.append(f"A{max_row + 1}:B{max_row + 1}") merge_cells.append(f"A{max_row + 1}:B{max_row + 1}")
merge_cells.append(f"A{max_row + 2}:B{max_row + 2}") merge_cells.append(f"A{max_row + 2}:B{max_row + 2}")
merge_cells.append(f"A{max_row + 3}:B{max_row + 3}") merge_cells.append(f"A{max_row + 3}:B{max_row + 3}")
@ -1379,13 +1388,13 @@ class Pool(models.Model):
for i in range(pool_size + 1): for i in range(pool_size + 1):
merge_cells.append(f"A{max_row + 5 + i}:B{max_row + 5 + i}") merge_cells.append(f"A{max_row + 5 + i}:B{max_row + 5 + i}")
format_requests.append({"unmergeCells": {"range": a1_range_to_grid_range("A1:AH", worksheet.id)}}) format_requests.append({"unmergeCells": {"range": a1_range_to_grid_range(f"A1:{max_col}", worksheet.id)}})
for name in merge_cells: for name in merge_cells:
grid_range = a1_range_to_grid_range(name, worksheet.id) grid_range = a1_range_to_grid_range(name, worksheet.id)
format_requests.append({"mergeCells": {"mergeType": MergeType.merge_all, "range": grid_range}}) format_requests.append({"mergeCells": {"mergeType": MergeType.merge_all, "range": grid_range}})
# Make titles bold # Make titles bold
bold_ranges = [("A1:AH", False), ("A1:AH3", True), bold_ranges = [(f"A1:{max_col}", False), (f"A1:{max_col}3", True),
(f"A{max_row + 1}:B{max_row + 3}", True), (f"A{max_row + 5}:E{max_row + 5}", True)] (f"A{max_row + 1}:B{max_row + 3}", True), (f"A{max_row + 5}:E{max_row + 5}", True)]
for bold_range, bold in bold_ranges: for bold_range, bold in bold_ranges:
format_requests.append({ format_requests.append({
@ -1397,7 +1406,7 @@ class Pool(models.Model):
}) })
# Set background color for headers and footers # Set background color for headers and footers
bg_colors = [("A1:AH", (1, 1, 1)), bg_colors = [(f"A1:{max_col}", (1, 1, 1)),
(f"A1:{getcol(2 + passages.count() * passage_width)}3", (0.8, 0.8, 0.8)), (f"A1:{getcol(2 + passages.count() * passage_width)}3", (0.8, 0.8, 0.8)),
(f"A{min_row - 1}:B{max_row}", (0.95, 0.95, 0.95)), (f"A{min_row - 1}:B{max_row}", (0.95, 0.95, 0.95)),
(f"A{max_row + 1}:B{max_row + 3}", (0.8, 0.8, 0.8)), (f"A{max_row + 1}:B{max_row + 3}", (0.8, 0.8, 0.8)),
@ -1432,10 +1441,10 @@ class Pool(models.Model):
}) })
# Set the width of the columns # Set the width of the columns
column_widths = [("A", 300), ("B", 30)] column_widths = [("A", 350), ("B", 30)]
for passage in passages: for passage in passages:
column_widths.append((f"{getcol(3 + passage_width * (passage.position - 1))}" column_widths.append((f"{getcol(3 + passage_width * (passage.position - 1))}"
f":{getcol(8 + passage_width * (passage.position - 1))}", 75)) f":{getcol(2 + passage_width * passage.position)}", 80))
for column, width in column_widths: for column, width in column_widths:
grid_range = a1_range_to_grid_range(column, worksheet.id) grid_range = a1_range_to_grid_range(column, worksheet.id)
format_requests.append({ format_requests.append({
@ -1486,7 +1495,7 @@ class Pool(models.Model):
}) })
# Define borders # Define borders
border_ranges = [("A1:AH", "0000"), border_ranges = [(f"A1:{max_col}", "0000"),
(f"A1:{getcol(2 + passages.count() * passage_width)}{max_row + 3}", "1111"), (f"A1:{getcol(2 + passages.count() * passage_width)}{max_row + 3}", "1111"),
(f"A{max_row + 5}:E{max_row + pool_size + 5}", "1111"), (f"A{max_row + 5}:E{max_row + pool_size + 5}", "1111"),
(f"A1:B{max_row + 3}", "1113"), (f"A1:B{max_row + 3}", "1113"),
@ -1532,8 +1541,8 @@ class Pool(models.Model):
"values": [{"userEnteredValue": f'=ET(REGEXMATCH(TO_TEXT({column}4); "^-?[0-9]+$"); ' "values": [{"userEnteredValue": f'=ET(REGEXMATCH(TO_TEXT({column}4); "^-?[0-9]+$"); '
f'{column}4>={min_note}; {column}4<={max_note})'},], f'{column}4>={min_note}; {column}4<={max_note})'},],
}, },
"inputMessage": (_("Input must be a valid integer between {min_note} and {max_note}.") "inputMessage": str(_("Input must be a valid integer between {min_note} and {max_note}.")
.format(min_note=min_note, max_note=max_note)), .format(min_note=min_note, max_note=max_note)),
"strict": True, "strict": True,
}, },
} }
@ -1561,15 +1570,15 @@ class Pool(models.Model):
}) })
# Protect the header, the juries list, the footer and the ranking # Protect the header, the juries list, the footer and the ranking
protected_ranges = ["A1:AH4", protected_ranges = [f"A1:{max_col}4",
f"A{min_row}:B{max_row}", f"A{min_row}:B{max_row}",
f"A{max_row}:AH{max_row + 5 + pool_size}"] f"A{max_row}:{max_col}{max_row + 5 + pool_size}"]
for protected_range in protected_ranges: for protected_range in protected_ranges:
format_requests.append({ format_requests.append({
"addProtectedRange": { "addProtectedRange": {
"protectedRange": { "protectedRange": {
"range": a1_range_to_grid_range(protected_range, worksheet.id), "range": a1_range_to_grid_range(protected_range, worksheet.id),
"description": _("Don't update the table structure for a better automated integration."), "description": str(_("Don't update the table structure for a better automated integration.")),
"warningOnly": True, "warningOnly": True,
}, },
} }
@ -1585,7 +1594,7 @@ class Pool(models.Model):
spreadsheet = gc.open_by_key(self.tournament.notes_sheet_id) spreadsheet = gc.open_by_key(self.tournament.notes_sheet_id)
worksheet = spreadsheet.worksheet(f"{_('Pool')} {self.short_name}") worksheet = spreadsheet.worksheet(f"{_('Pool')} {self.short_name}")
average_cell = worksheet.find(_("Average")) average_cell = worksheet.find(str(_("Average")))
min_row = 5 min_row = 5
max_row = average_cell.row - 1 max_row = average_cell.row - 1
juries_visible = worksheet.get(f"A{min_row}:B{max_row}") juries_visible = worksheet.get(f"A{min_row}:B{max_row}")
@ -1607,7 +1616,7 @@ class Pool(models.Model):
spreadsheet = gc.open_by_key(self.tournament.notes_sheet_id) spreadsheet = gc.open_by_key(self.tournament.notes_sheet_id)
worksheet = spreadsheet.worksheet(f"{_('Pool')} {self.short_name}") worksheet = spreadsheet.worksheet(f"{_('Pool')} {self.short_name}")
average_cell = worksheet.find(_("Average")) average_cell = worksheet.find(str(_("Average")))
min_row = 5 min_row = 5
max_row = average_cell.row - 2 max_row = average_cell.row - 2
data = worksheet.get_values(f"A{min_row}:AH{max_row}") data = worksheet.get_values(f"A{min_row}:AH{max_row}")