mirror of
https://gitlab.com/animath/si/plateforme.git
synced 2024-12-25 07:02:22 +00:00
Auto update Google Sheet after jury management
Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
This commit is contained in:
parent
37ad3cf8a6
commit
3fae6a00dd
@ -422,10 +422,14 @@ class Tournament(models.Model):
|
|||||||
|
|
||||||
def create_spreadsheet(self):
|
def create_spreadsheet(self):
|
||||||
if self.notes_sheet_id:
|
if self.notes_sheet_id:
|
||||||
|
gc = gspread.service_account_from_dict(settings.GOOGLE_SERVICE_CLIENT)
|
||||||
|
spreadsheet = gc.open_by_key(self.notes_sheet_id)
|
||||||
|
spreadsheet.update_locale("fr_FR")
|
||||||
return self.notes_sheet_id
|
return self.notes_sheet_id
|
||||||
|
|
||||||
gc = gspread.service_account_from_dict(settings.GOOGLE_SERVICE_CLIENT)
|
gc = gspread.service_account_from_dict(settings.GOOGLE_SERVICE_CLIENT)
|
||||||
spreadsheet = gc.create(f"Feuille de notes - {self.name}", folder_id=settings.NOTES_DRIVE_FOLDER_ID)
|
spreadsheet = gc.create(f"Feuille de notes - {self.name}", folder_id=settings.NOTES_DRIVE_FOLDER_ID)
|
||||||
|
spreadsheet.update_locale("fr_FR")
|
||||||
self.notes_sheet_id = spreadsheet.id
|
self.notes_sheet_id = spreadsheet.id
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
@ -637,7 +641,7 @@ class Pool(models.Model):
|
|||||||
for _passage in passages), start=["Juré⋅e", ""]),
|
for _passage in passages), start=["Juré⋅e", ""]),
|
||||||
]
|
]
|
||||||
|
|
||||||
notes = []
|
notes = [[]] # Begin with empty hidden line to ensure pretty design
|
||||||
for jury in self.juries.all():
|
for jury in self.juries.all():
|
||||||
line = [str(jury), jury.id]
|
line = [str(jury), jury.id]
|
||||||
for passage in passages:
|
for passage in passages:
|
||||||
@ -647,6 +651,7 @@ class Pool(models.Model):
|
|||||||
if pool_size == 4:
|
if pool_size == 4:
|
||||||
line.append(note.observer_oral)
|
line.append(note.observer_oral)
|
||||||
notes.append(line)
|
notes.append(line)
|
||||||
|
notes.append([]) # Add empty line to ensure pretty design
|
||||||
|
|
||||||
def getcol(number: int) -> str:
|
def getcol(number: int) -> str:
|
||||||
"""
|
"""
|
||||||
@ -662,15 +667,15 @@ class Pool(models.Model):
|
|||||||
subtotal = ["Sous-total", ""]
|
subtotal = ["Sous-total", ""]
|
||||||
footer = [average, coeffs, subtotal, 32 * [""]]
|
footer = [average, coeffs, subtotal, 32 * [""]]
|
||||||
|
|
||||||
min_row = 4
|
min_row = 5
|
||||||
max_row = min_row + self.juries.count() - 1
|
max_row = min_row + self.juries.count()
|
||||||
min_column = 3
|
min_column = 3
|
||||||
for i, passage in enumerate(passages):
|
for i, passage in enumerate(passages):
|
||||||
for j, note in enumerate(passage.averages):
|
for j, note in enumerate(passage.averages):
|
||||||
column = getcol(min_column + i * passage_width + j)
|
column = getcol(min_column + i * passage_width + j)
|
||||||
average.append(f"=MOYENNE.SI(${getcol(min_column + i * passage_width)}${min_row}"
|
average.append(f"=MOYENNE.SI(${getcol(min_column + i * passage_width)}${min_row - 1}"
|
||||||
f":${getcol(min_column + i * passage_width)}{max_row}; \">0\"; "
|
f":${getcol(min_column + i * passage_width)}{max_row}; \">0\"; "
|
||||||
f"{column}${min_row}:{column}{max_row})")
|
f"{column}${min_row - 1}:{column}{max_row})")
|
||||||
def_w_col = getcol(min_column + passage_width * i)
|
def_w_col = getcol(min_column + passage_width * i)
|
||||||
def_o_col = getcol(min_column + passage_width * i + 1)
|
def_o_col = getcol(min_column + passage_width * i + 1)
|
||||||
subtotal.extend([f"={def_w_col}{max_row + 1} * {def_w_col}{max_row + 2}"
|
subtotal.extend([f"={def_w_col}{max_row + 1} * {def_w_col}{max_row + 2}"
|
||||||
@ -733,6 +738,7 @@ class Pool(models.Model):
|
|||||||
|
|
||||||
all_values = header + notes + footer + ranking
|
all_values = header + notes + footer + ranking
|
||||||
|
|
||||||
|
worksheet.batch_clear([f"A1:AF{max_row + 5 + pool_size}"])
|
||||||
worksheet.update("A1:AF", all_values, raw=False)
|
worksheet.update("A1:AF", all_values, raw=False)
|
||||||
|
|
||||||
format_requests = []
|
format_requests = []
|
||||||
@ -765,19 +771,21 @@ class Pool(models.Model):
|
|||||||
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:AF3", f"A{max_row + 1}:B{max_row + 3}", f"A{max_row + 5}:E{max_row + 5}"]
|
bold_ranges = [("A1:AF", False), ("A1:AF3", True),
|
||||||
for bold_range in bold_ranges:
|
(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:
|
||||||
format_requests.append({
|
format_requests.append({
|
||||||
"repeatCell": {
|
"repeatCell": {
|
||||||
"range": a1_range_to_grid_range(bold_range, worksheet.id),
|
"range": a1_range_to_grid_range(bold_range, worksheet.id),
|
||||||
"cell": {"userEnteredFormat": {"textFormat": {"bold": True}}},
|
"cell": {"userEnteredFormat": {"textFormat": {"bold": bold}}},
|
||||||
"fields": "userEnteredFormat(textFormat)",
|
"fields": "userEnteredFormat(textFormat)",
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
# Set background color for headers and footers
|
# Set background color for headers and footers
|
||||||
bg_colors = [(f"A1:{getcol(2 + pool_size * passage_width)}3", (0.8, 0.8, 0.8)),
|
bg_colors = [("A1:AF", (1, 1, 1)),
|
||||||
(f"A{min_row}:B{max_row}", (0.95, 0.95, 0.95)),
|
(f"A1:{getcol(2 + pool_size * 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{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)),
|
||||||
(f"C{max_row + 1}:{getcol(2 + pool_size * passage_width)}{max_row + 3}", (0.9, 0.9, 0.9)),
|
(f"C{max_row + 1}:{getcol(2 + pool_size * passage_width)}{max_row + 3}", (0.9, 0.9, 0.9)),
|
||||||
(f"A{max_row + 5}:E{max_row + 5}", (0.8, 0.8, 0.8)),
|
(f"A{max_row + 5}:E{max_row + 5}", (0.8, 0.8, 0.8)),
|
||||||
@ -830,14 +838,30 @@ class Pool(models.Model):
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
# Hide second column (Jury ID)
|
# Hide second column (Jury ID) and first and last jury rows
|
||||||
|
hidden_dimensions = [(1, "COLUMNS"), (3, "ROWS"), (max_row - 1, "ROWS")]
|
||||||
format_requests.append({
|
format_requests.append({
|
||||||
"updateDimensionProperties": {
|
"updateDimensionProperties": {
|
||||||
"range": {
|
"range": {
|
||||||
"sheetId": worksheet.id,
|
"sheetId": worksheet.id,
|
||||||
"dimension": "COLUMNS",
|
"dimension": "ROWS",
|
||||||
"startIndex": 1,
|
"startIndex": 0,
|
||||||
"endIndex": 2,
|
"endIndex": 1000,
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"hiddenByUser": False,
|
||||||
|
},
|
||||||
|
"fields": "hiddenByUser",
|
||||||
|
}
|
||||||
|
})
|
||||||
|
for dimension_id, dimension_type in hidden_dimensions:
|
||||||
|
format_requests.append({
|
||||||
|
"updateDimensionProperties": {
|
||||||
|
"range": {
|
||||||
|
"sheetId": worksheet.id,
|
||||||
|
"dimension": dimension_type,
|
||||||
|
"startIndex": dimension_id,
|
||||||
|
"endIndex": dimension_id + 1,
|
||||||
},
|
},
|
||||||
"properties": {
|
"properties": {
|
||||||
"hiddenByUser": True,
|
"hiddenByUser": True,
|
||||||
@ -847,12 +871,17 @@ class Pool(models.Model):
|
|||||||
})
|
})
|
||||||
|
|
||||||
# Define borders
|
# Define borders
|
||||||
border_ranges = [(f"A1:{getcol(2 + pool_size * passage_width)}{max_row + 3}", "1111"),
|
border_ranges = [("A1:AF", "0000"),
|
||||||
|
(f"A1:{getcol(2 + pool_size * 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"),
|
||||||
(f"C1:{getcol(2 + (pool_size - 1) * passage_width)}1", "1113")]
|
(f"C1:{getcol(2 + (pool_size - 1) * passage_width)}1", "1113")]
|
||||||
for i in range(pool_size - 1):
|
for i in range(pool_size - 1):
|
||||||
border_ranges.append((f"{getcol(2 + (i + 1) * passage_width)}2"
|
border_ranges.append((f"{getcol(1 + (i + 1) * passage_width)}2"
|
||||||
|
f":{getcol(2 + (i + 1) * passage_width)}2", "1113"))
|
||||||
|
border_ranges.append((f"{getcol(2 + (i + 1) * passage_width)}3"
|
||||||
|
f":{getcol(2 + (i + 1) * passage_width)}{max_row + 2}", "1113"))
|
||||||
|
border_ranges.append((f"{getcol(1 + (i + 1) * passage_width)}{max_row + 3}"
|
||||||
f":{getcol(2 + (i + 1) * passage_width)}{max_row + 3}", "1113"))
|
f":{getcol(2 + (i + 1) * passage_width)}{max_row + 3}", "1113"))
|
||||||
sides_names = ['top', 'bottom', 'left', 'right']
|
sides_names = ['top', 'bottom', 'left', 'right']
|
||||||
styles = ["NONE", "SOLID", "SOLID_MEDIUM", "SOLID_THICK", "DOUBLE"]
|
styles = ["NONE", "SOLID", "SOLID_MEDIUM", "SOLID_THICK", "DOUBLE"]
|
||||||
@ -882,9 +911,9 @@ 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:AF3",
|
protected_ranges = ["A1:AF4",
|
||||||
f"A{min_row}:B{max_row}",
|
f"A{min_row}:B{max_row}",
|
||||||
f"A{max_row + 1}:AF{max_row + 5 + pool_size}"]
|
f"A{max_row}:AF{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": {
|
||||||
@ -900,6 +929,33 @@ class Pool(models.Model):
|
|||||||
body = {"requests": format_requests}
|
body = {"requests": format_requests}
|
||||||
worksheet.client.batch_update(spreadsheet.id, body)
|
worksheet.client.batch_update(spreadsheet.id, body)
|
||||||
|
|
||||||
|
def update_juries_lines_spreadsheet(self):
|
||||||
|
gc = gspread.service_account_from_dict(settings.GOOGLE_SERVICE_CLIENT)
|
||||||
|
spreadsheet = gc.open_by_key(self.tournament.notes_sheet_id)
|
||||||
|
worksheet = spreadsheet.worksheet(f"Poule {self.short_name}")
|
||||||
|
|
||||||
|
average_cell = worksheet.find("Moyenne")
|
||||||
|
min_row = 5
|
||||||
|
max_row = average_cell.row - 1
|
||||||
|
juries_visible = worksheet.get(f"A{min_row}:B{max_row}")
|
||||||
|
juries_visible = [t for t in juries_visible if t and len(t) == 2]
|
||||||
|
rows_to_delete = []
|
||||||
|
for i, (_jury_name, jury_id) in enumerate(juries_visible):
|
||||||
|
if not jury_id.isnumeric() or int(jury_id) not in self.juries.values_list("id", flat=True):
|
||||||
|
rows_to_delete.append(min_row + i)
|
||||||
|
for row_to_delete in rows_to_delete:
|
||||||
|
worksheet.delete_rows(row_to_delete)
|
||||||
|
max_row -= len(rows_to_delete)
|
||||||
|
|
||||||
|
for jury in self.juries.all():
|
||||||
|
if str(jury.id) not in list(map(lambda x: x[1], juries_visible)):
|
||||||
|
worksheet.insert_row([str(jury), jury.id], max_row)
|
||||||
|
max_row += 1
|
||||||
|
|
||||||
|
def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
|
||||||
|
self.update_juries_lines_spreadsheet()
|
||||||
|
super().save(force_insert, force_update, using, update_fields)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return _("Pool of day {round} for tournament {tournament} with teams {teams}")\
|
return _("Pool of day {round} for tournament {tournament} with teams {teams}")\
|
||||||
.format(round=self.round,
|
.format(round=self.round,
|
||||||
@ -1265,6 +1321,9 @@ class Note(models.Model):
|
|||||||
self.observer_oral = observer_oral
|
self.observer_oral = observer_oral
|
||||||
|
|
||||||
def update_spreadsheet(self):
|
def update_spreadsheet(self):
|
||||||
|
if not self.has_any_note():
|
||||||
|
return
|
||||||
|
|
||||||
gc = gspread.service_account_from_dict(settings.GOOGLE_SERVICE_CLIENT)
|
gc = gspread.service_account_from_dict(settings.GOOGLE_SERVICE_CLIENT)
|
||||||
passage = Passage.objects.prefetch_related('pool__tournament', 'pool__participations').get(pk=self.passage.pk)
|
passage = Passage.objects.prefetch_related('pool__tournament', 'pool__participations').get(pk=self.passage.pk)
|
||||||
spreadsheet_id = passage.pool.tournament.notes_sheet_id
|
spreadsheet_id = passage.pool.tournament.notes_sheet_id
|
||||||
|
@ -1035,6 +1035,7 @@ class PoolRemoveJuryView(VolunteerMixin, DetailView):
|
|||||||
jury = pool.juries.get(pk=kwargs['jury_id'])
|
jury = pool.juries.get(pk=kwargs['jury_id'])
|
||||||
pool.juries.remove(jury)
|
pool.juries.remove(jury)
|
||||||
pool.save()
|
pool.save()
|
||||||
|
Note.objects.filter(jury=jury, passage__pool=pool).delete()
|
||||||
messages.success(request, _("The jury {name} has been successfully removed!")
|
messages.success(request, _("The jury {name} has been successfully removed!")
|
||||||
.format(name=f"{jury.user.first_name} {jury.user.last_name}"))
|
.format(name=f"{jury.user.first_name} {jury.user.last_name}"))
|
||||||
return redirect(reverse_lazy('participation:pool_jury', args=(pool.pk,)))
|
return redirect(reverse_lazy('participation:pool_jury', args=(pool.pk,)))
|
||||||
|
Loading…
Reference in New Issue
Block a user