From e980a029cb11bddd9298dc50daa7d15c3d9acd75 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ji=C5=99=C3=AD=20Setni=C4=8Dka?= <setnicka@seznam.cz>
Date: Sun, 14 Mar 2021 00:15:38 +0100
Subject: [PATCH] =?UTF-8?q?DB:=20Body=20mohou=20b=C3=BDt=20desetinn=C3=A9,?=
=?UTF-8?q?=20nastaven=C3=AD=20podle=20points=5Fstep=20v=20rounds?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Přesnost v databázi je nastavena na jedno desetinné místo - numeric(5,1).
V rozhraní se nastavuje v rámci nastavení kola.
Issue #189
---
db/db.ddl | 7 ++++---
db/upgrade-20210328.sql | 13 +++++++++++++
mo/db.py | 22 +++++++++++++++++++---
mo/score.py | 11 ++++++-----
mo/web/org_round.py | 5 +++++
mo/web/templates/org_round.html | 1 +
6 files changed, 48 insertions(+), 11 deletions(-)
create mode 100644 db/upgrade-20210328.sql
diff --git a/db/db.ddl b/db/db.ddl
index 7e27eb95..fd3ce6c2 100644
--- a/db/db.ddl
+++ b/db/db.ddl
@@ -107,6 +107,7 @@ CREATE TABLE rounds (
score_mode score_mode NOT NULL DEFAULT 'basic', -- mód výsledkovky
score_winner_limit int DEFAULT NULL, -- bodový limit na označení za vítěze
score_successful_limit int DEFAULT NULL, -- bodový limit na označení za úspěšného řešitele
+ points_step numeric(2,1) NOT NULL DEFAULT 1, -- s jakou přesností jsou přidělovány body (celé aneb 1, 0.5, 0.1)
has_messages boolean NOT NULL DEFAULT false, -- má zprávičky
UNIQUE (year, category, seq, part)
);
@@ -162,7 +163,7 @@ CREATE TABLE tasks (
round_id int NOT NULL REFERENCES rounds(round_id),
code varchar(255) NOT NULL, -- např. "P-I-1"
name varchar(255) NOT NULL,
- max_points int DEFAULT NULL, -- maximální počet bodů, pokud je nastaven, nelze zadat více bodů
+ max_points numeric(5,1) DEFAULT NULL, -- maximální počet bodů, pokud je nastaven, nelze zadat více bodů
UNIQUE (round_id, code)
);
@@ -200,7 +201,7 @@ CREATE TABLE solutions (
user_id int NOT NULL REFERENCES users(user_id),
final_submit int DEFAULT NULL REFERENCES papers(paper_id), -- verze odevzdání, která se má hodnotit
final_feedback int DEFAULT NULL REFERENCES papers(paper_id), -- verze komentáře opravovatelů, kterou má vidět účastník
- points int DEFAULT NULL,
+ points numeric(5,1) DEFAULT NULL,
note text NOT NULL DEFAULT '', -- komentář pro řešitele
org_note text NOT NULL DEFAULT '', -- komentář viditelný jen organizátorům
PRIMARY KEY (task_id, user_id)
@@ -213,7 +214,7 @@ CREATE TABLE points_history (
points_history_id serial PRIMARY KEY,
task_id int NOT NULL REFERENCES tasks(task_id),
participant_id int NOT NULL REFERENCES users(user_id),
- points int DEFAULT NULL,
+ points numeric(5,1) DEFAULT NULL,
points_by int NOT NULL REFERENCES users(user_id), -- kdo přidělil body
points_at timestamp with time zone NOT NULL -- a kdy
);
diff --git a/db/upgrade-20210328.sql b/db/upgrade-20210328.sql
new file mode 100644
index 00000000..f237e0c8
--- /dev/null
+++ b/db/upgrade-20210328.sql
@@ -0,0 +1,13 @@
+SET ROLE 'mo_osmo';
+
+ALTER TABLE rounds
+ ADD COLUMN points_step numeric(2,1) NOT NULL DEFAULT 1; -- s jakou přesností jsou přidělovány body (celé aneb 1, 0.5, 0.1)
+
+ALTER TABLE solutions
+ ALTER COLUMN points SET DATA TYPE numeric(5,1);
+
+ALTER TABLE points_history
+ ALTER COLUMN points SET DATA TYPE numeric(5,1);
+
+ALTER TABLE tasks
+ ALTER COLUMN max_points SET DATA TYPE numeric(5,1);
diff --git a/mo/db.py b/mo/db.py
index 5d2c5018..81a3c0b8 100644
--- a/mo/db.py
+++ b/mo/db.py
@@ -15,6 +15,7 @@ from sqlalchemy.orm.attributes import get_history
from sqlalchemy.dialects.postgresql import JSONB
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.sql.expression import CTE
+from sqlalchemy.sql.sqltypes import Numeric
from typing import Optional, List, Tuple
import mo
@@ -186,6 +187,15 @@ round_score_mode_names = {
}
+# V DB jako numeric(2,1), používá se tak snadněji, než enum
+round_points_step_names = {
+ 1: "Celé body",
+ 0.5: "Půlbody",
+ 0.1: "Desetinné body",
+}
+round_points_step_choices = round_points_step_names.items()
+
+
class Round(Base):
__tablename__ = 'rounds'
__table_args__ = (
@@ -209,6 +219,7 @@ class Round(Base):
score_mode = Column(Enum(RoundScoreMode, name='score_mode'), nullable=False, server_default=text("'basic'::score_mode"))
score_winner_limit = Column(Integer)
score_successful_limit = Column(Integer)
+ points_step = Column(Numeric, nullable=False)
has_messages = Column(Boolean, nullable=False, server_default=text("false"))
master = relationship('Round', primaryjoin='Round.master_round_id == Round.round_id', remote_side='Round.round_id', post_update=True)
@@ -246,6 +257,11 @@ class Round(Base):
else:
return self.state
+ def points_step_name(self) -> str:
+ if float(self.points_step) in round_points_step_names:
+ return round_points_step_names[float(self.points_step)]
+ return str(self.points_step)
+
class User(Base):
__tablename__ = 'users'
@@ -415,7 +431,7 @@ class Task(Base):
round_id = Column(Integer, ForeignKey('rounds.round_id'), nullable=False)
code = Column(String(255), nullable=False)
name = Column(String(255), nullable=False)
- max_points = Column(Integer)
+ max_points = Column(Numeric)
round = relationship('Round')
@@ -524,7 +540,7 @@ class PointsHistory(Base):
points_history_id = Column(Integer, primary_key=True, server_default=text("nextval('points_history_points_history_id_seq'::regclass)"))
task_id = Column(Integer, ForeignKey('tasks.task_id'), nullable=False)
participant_id = Column(Integer, ForeignKey('users.user_id'), nullable=False)
- points = Column(Integer)
+ points = Column(Numeric)
points_by = Column(Integer, ForeignKey('users.user_id'), nullable=False)
points_at = Column(DateTime(True), nullable=False)
@@ -540,7 +556,7 @@ class Solution(Base):
user_id = Column(Integer, ForeignKey('users.user_id'), primary_key=True, nullable=False)
final_submit = Column(Integer, ForeignKey('papers.paper_id'))
final_feedback = Column(Integer, ForeignKey('papers.paper_id'))
- points = Column(Integer)
+ points = Column(Numeric)
note = Column(Text, nullable=False, server_default=text("''::text"))
org_note = Column(Text, nullable=False, server_default=text("''::text"))
diff --git a/mo/score.py b/mo/score.py
index ccbefbf0..01a26d78 100644
--- a/mo/score.py
+++ b/mo/score.py
@@ -1,3 +1,4 @@
+import decimal
from fractions import Fraction
from sqlalchemy import and_
from sqlalchemy.orm import joinedload
@@ -56,8 +57,8 @@ class ScoreResult:
def get_sols_map(self) -> Dict[int, db.Solution]:
return self._sols[0]
- def get_total_points(self) -> int:
- sum = 0
+ def get_total_points(self) -> decimal.Decimal:
+ sum = decimal.Decimal(0)
for sol in self.get_sols():
if sol.points:
sum += sol.points
@@ -67,17 +68,17 @@ class ScoreResult:
class ScoreTask:
task: db.Task
num_solutions: int
- sum_points: int
+ sum_points: decimal.Decimal
def __init__(self, task: db.Task):
self.task = task
self.num_solutions = 0
- self.sum_points = 0
+ self.sum_points = decimal.Decimal(0)
def get_difficulty(self) -> Fraction:
if self.num_solutions == 0:
return Fraction(0)
- return Fraction(self.sum_points, self.num_solutions)
+ return Fraction(Fraction(self.sum_points), self.num_solutions)
def get_difficulty_str(self) -> str:
return f'{self.sum_points}/{self.num_solutions}'
diff --git a/mo/web/org_round.py b/mo/web/org_round.py
index 96b94c61..70727966 100644
--- a/mo/web/org_round.py
+++ b/mo/web/org_round.py
@@ -431,6 +431,10 @@ class RoundEditForm(FlaskForm):
"Hranice bodů pro úspěšné řešitele", validators=[validators.Optional()],
description="Řešitelé s alespoň tolika body budou označeni za úspěšné řešitele, prázdná hodnota = žádné neoznačovat",
)
+ points_step = wtforms.SelectField(
+ "Přesnost bodování", choices=db.round_points_step_choices,
+ description="Ovlivňuje možnost zadávání nových bodů, již uložené body nezmění"
+ )
has_messages = wtforms.BooleanField("Zprávičky pro účastníky (aktivuje možnost vytvářet novinky zobrazované účastníkům)")
submit = wtforms.SubmitField('Uložit')
@@ -450,6 +454,7 @@ def org_round_edit(id: int):
del form.score_mode
del form.score_winner_limit
del form.score_successful_limit
+ del form.points_step
if form.validate_on_submit():
form.populate_obj(round)
diff --git a/mo/web/templates/org_round.html b/mo/web/templates/org_round.html
index aa1a62a6..ac932105 100644
--- a/mo/web/templates/org_round.html
+++ b/mo/web/templates/org_round.html
@@ -61,6 +61,7 @@
<tr><td>Výsledková listina<td>{{ round.master.score_mode.friendly_name() }}
<tr><td>Hranice bodů pro vítěze<td>{{ round.master.score_winner_limit|none_value(Markup('<i>nenastaveno</i>')) }}
<tr><td>Hranice bodů pro úspěšné řešitele<td>{{ round.master.score_successful_limit|none_value(Markup('<i>nenastaveno</i>')) }}
+ <tr><td>Přesnost bodování<td>{{ round.points_step_name() }}
</table>
<div style="clear: both;"></div>
--
GitLab