Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
Odevzdávací Systém MO
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Deploy
Model registry
Analyze
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
GitLab community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
MO-P
Odevzdávací Systém MO
Commits
93703d9f
Project 'mj/mo-submit' was moved to 'mo-p/osmo'. Please update any links and bookmarks that may still have the old path.
Commit
93703d9f
authored
Sep 24, 2021
by
Martin Mareš
Browse files
Options
Downloads
Patches
Plain Diff
Hierarchie: Kola a statistiky
parent
7acb33d7
No related branches found
No related tags found
No related merge requests found
Changes
2
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
mo/web/org_round.py
+91
-35
91 additions, 35 deletions
mo/web/org_round.py
mo/web/templates/org_round.html
+56
-31
56 additions, 31 deletions
mo/web/templates/org_round.html
with
147 additions
and
66 deletions
mo/web/org_round.py
+
91
−
35
View file @
93703d9f
from
dataclasses
import
dataclass
import
decimal
from
flask
import
render_template
,
g
,
redirect
,
flash
,
request
import
locale
...
...
@@ -8,9 +9,9 @@ from bleach.sanitizer import ALLOWED_TAGS
import
markdown
import
os
from
sqlalchemy
import
func
from
sqlalchemy.orm
import
joinedload
from
sqlalchemy.orm
import
joinedload
,
aliased
from
sqlalchemy.sql.functions
import
coalesce
from
typing
import
Optional
from
typing
import
Optional
,
List
,
Dict
,
Tuple
import
werkzeug.exceptions
import
wtforms
from
wtforms
import
validators
,
ValidationError
...
...
@@ -157,44 +158,93 @@ def create_subcontests(master_round: db.Round, master_contest: db.Contest):
app
.
logger
.
info
(
f
"
Podsoutěž #
{
subcontest
.
contest_id
}
založena:
{
db
.
row2dict
(
subcontest
)
}
"
)
@app.route
(
'
/org/contest/r/<int:round_id>/
'
,
methods
=
(
'
GET
'
,
'
POST
'
))
def
org_round
(
round_id
:
int
):
@dataclass
class
ContestStat
:
region
:
db
.
Place
contest
:
Optional
[
db
.
Contest
]
=
None
num_contests
:
int
=
0
num_active_pants
:
int
=
0
num_unconfirmed_pants
:
int
=
0
def
region_stats
(
round
:
db
.
Round
,
region
:
db
.
Place
)
->
List
[
ContestStat
]:
stats
:
Dict
[
int
,
ContestStat
]
=
{}
sess
=
db
.
get_session
()
ctx
=
get_context
(
round_id
=
round_id
)
round
=
ctx
.
round
rights
=
ctx
.
rights
participants_count
=
sess
.
query
(
db
.
Participation
.
contest_id
,
func
.
count
(
db
.
Participation
.
user_id
).
label
(
'
count
'
)
).
group_by
(
db
.
Participation
.
contest_id
).
subquery
()
# Účastníci jsou jen pod master contesty
contests_counts
=
(
sess
.
query
(
db
.
Contest
,
coalesce
(
participants_count
.
c
.
count
,
0
)
).
outerjoin
(
participants_count
,
db
.
Contest
.
master_contest_id
==
participants_count
.
c
.
contest_id
)
.
filter
(
db
.
Contest
.
round
==
round
)
.
options
(
joinedload
(
db
.
Contest
.
place
))
if
region
.
level
>
round
.
level
:
return
[]
if
(
region
.
level
>=
round
.
level
-
1
or
region
.
level
==
2
and
round
.
level
==
4
):
# List individual contests
q
=
sess
.
query
(
db
.
Contest
).
filter_by
(
round
=
round
)
q
=
db
.
filter_place_nth_parent
(
q
,
db
.
Contest
.
place_id
,
round
.
level
-
region
.
level
,
region
.
place_id
)
q
=
q
.
options
(
joinedload
(
db
.
Contest
.
place
))
for
c
in
q
.
all
():
s
=
ContestStat
(
region
=
c
.
place
,
contest
=
c
,
num_contests
=
1
)
stats
[
c
.
place
.
place_id
]
=
s
have_contests
=
True
else
:
# List sub-regions
regs
=
sess
.
query
(
db
.
Place
).
filter
(
db
.
Place
.
parent_place
==
region
).
all
()
for
r
in
regs
:
s
=
ContestStat
(
region
=
r
)
stats
[
r
.
place_id
]
=
s
have_contests
=
False
region_ids
=
[
s
.
region
.
place_id
for
s
in
stats
.
values
()]
if
not
have_contests
:
rcs
=
(
sess
.
query
(
db
.
RegionContestStat
)
.
filter_by
(
round
=
round
)
.
filter
(
db
.
RegionContestStat
.
region
.
in_
(
region_ids
))
.
all
())
for
r
in
rcs
:
stats
[
r
.
region
].
num_contests
=
r
.
count
contests_counts
.
sort
(
key
=
lambda
c
:
locale
.
strxfrm
(
c
[
0
].
place
.
name
))
sol_counts_q
=
(
sess
.
query
(
db
.
Solution
.
task_id
,
func
.
count
(
db
.
Solution
.
task_id
))
.
filter
(
db
.
Solution
.
task_id
.
in_
(
sess
.
query
(
db
.
Task
.
task_id
).
filter_by
(
round
=
round
)
))
rs
=
(
sess
.
query
(
db
.
RegionParticipantStat
)
.
filter_by
(
round_id
=
round
.
master_round_id
)
.
filter
(
db
.
RegionParticipantStat
.
region
.
in_
(
region_ids
))
.
all
())
for
r
in
rs
:
if
r
.
state
==
db
.
PartState
.
active
:
stats
[
r
.
region
].
num_active_pants
=
r
.
count
elif
r
.
state
==
db
.
PartState
.
registered
:
stats
[
r
.
region
].
num_unconfirmed_pants
=
r
.
count
out
=
list
(
stats
.
values
())
out
.
sort
(
key
=
lambda
s
:
locale
.
strxfrm
(
s
.
region
.
name
or
""
))
return
out
def
region_totals
(
region
:
db
.
Place
,
stats
:
List
[
ContestStat
])
->
ContestStat
:
return
ContestStat
(
region
=
region
,
num_contests
=
sum
(
s
.
num_contests
for
s
in
stats
),
num_active_pants
=
sum
(
s
.
num_active_pants
for
s
in
stats
),
num_unconfirmed_pants
=
sum
(
s
.
num_unconfirmed_pants
for
s
in
stats
),
)
sol_counts
=
{}
for
task_id
,
count
in
sol_counts_q
.
group_by
(
db
.
Solution
.
task_id
).
all
():
sol_counts
[
task_id
]
=
count
def
task_stats
(
round
:
db
.
Round
,
region
:
db
.
Place
)
->
List
[
Tuple
[
db
.
Task
,
int
]]:
sess
=
db
.
get_session
()
tasks
=
sess
.
query
(
db
.
Task
).
filter_by
(
round
=
round
).
all
()
tasks
.
sort
(
key
=
lambda
t
:
t
.
code
)
for
task
in
tasks
:
task
.
sol_count
=
sol_counts
[
task
.
task_id
]
if
task
.
task_id
in
sol_counts
else
0
ts
=
(
sess
.
query
(
db
.
RegionTaskStat
)
.
filter_by
(
round
=
round
,
region
=
region
.
place_id
)
.
all
())
count_by_id
=
{
s
.
task_id
:
s
.
count
for
s
in
ts
}
return
[(
t
,
count_by_id
.
get
(
t
.
task_id
,
0
))
for
t
in
tasks
]
@app.route
(
'
/org/contest/r/<int:round_id>/
'
,
methods
=
(
'
GET
'
,
'
POST
'
))
@app.route
(
'
/org/contest/r/<int:round_id>/h/<int:hier_id>
'
,
methods
=
(
'
GET
'
,
'
POST
'
))
def
org_round
(
round_id
:
int
,
hier_id
:
Optional
[
int
]
=
None
):
ctx
=
get_context
(
round_id
=
round_id
,
hier_id
=
hier_id
)
round
=
ctx
.
round
rights
=
ctx
.
rights
form_delete_task
=
TaskDeleteForm
()
if
rights
.
have_right
(
Right
.
manage_round
)
and
delete_task
(
round_id
,
form_delete_task
):
...
...
@@ -208,13 +258,19 @@ def org_round(round_id: int):
group_rounds
=
round
.
get_group_rounds
(
True
)
group_rounds
.
sort
(
key
=
lambda
r
:
r
.
round_code
())
region
=
ctx
.
hier_place
or
db
.
get_root_place
()
reg_stats
=
region_stats
(
round
,
region
)
reg_total
=
region_totals
(
region
,
reg_stats
)
task_info
=
task_stats
(
round
,
region
)
return
render_template
(
'
org_round.html
'
,
ctx
=
ctx
,
rights
=
rights
,
round
=
round
,
group_rounds
=
group_rounds
,
roles
=
[
r
.
friendly_name
()
for
r
in
rights
.
get_roles
()],
contests_counts
=
contests_counts
,
tasks
=
tasks
,
form_delete_task
=
form_delete_task
,
reg_stats
=
reg_stats
,
reg_total
=
reg_total
,
task_info
=
task_info
,
form_delete_task
=
form_delete_task
,
form_add_contest
=
form_add_contest
,
statement_exists
=
mo
.
web
.
util
.
task_statement_exists
(
round
),
)
...
...
...
...
This diff is collapsed.
Click to expand it.
mo/web/templates/org_round.html
+
56
−
31
View file @
93703d9f
{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% set can_manage_round = rights.have_right(Right.manage_round) %}
{% set in_hier = ctx.hier_id != None %}
{% set can_manage_round = rights.have_right(Right.manage_round) and not in_hier %}
{% set can_manage_contest = rights.have_right(Right.manage_contest) %}
{% set can_view_contestants = rights.have_right(Right.view_contestants) %}
{% set can_handle_submits = rights.have_right(Right.view_submits) %}
...
...
@@ -8,7 +9,13 @@
{% set can_view_statement = rights.can_view_statement() %}
{% set can_add_contest = g.gatekeeper.rights_generic().have_right(Right.add_contest) %}
{% block title %}{{ round.name }} {{ round.round_code() }}{% endblock %}
{% block title %}
{% if in_hier %}
{{ round.round_code() }}: {{ ctx.hier_place.name }}
{% else %}
{{ round.name }} {{ round.round_code() }}
{% endif %}
{% endblock %}
{% block breadcrumbs %}
{{ ctx.breadcrumbs() }}
{% endblock %}
...
...
@@ -81,7 +88,7 @@
{% if can_view_contestants %}
<a
class=
"btn btn-primary"
href=
'{{ ctx.url_for('
org_generic_list
')
}}'
>
Seznam účastníků
</a>
{% endif %}
{% if can_view_contestants and round.state in [RoundState.grading, RoundState.closed, RoundState.delegate] %}
{% if can_view_contestants and round.state in [RoundState.grading, RoundState.closed, RoundState.delegate]
and not in_hier
%}
<a
class=
"btn btn-primary"
href=
'{{ ctx.url_for('
org_score
')
}}'
>
Výsledky
</a>
{% endif %}
{% if can_manage_contest %}
...
...
@@ -94,35 +101,53 @@
{% if round.has_messages %}
<a
class=
"btn btn-default"
href=
'{{ ctx.url_for('
org_round_messages
')
}}'
>
Zprávičky
</a>
{% endif %}
{% if g.user.is_admin %}
{% if g.user.is_admin
and not in_hier
%}
<a
class=
"btn btn-default"
href=
'{{ log_url('
round
',
round.round_id
)
}}'
>
Historie
</a>
{% endif %}
</div>
{% endif %}
<h3>
Soutěže
</h3>
{% if contests_counts %}
{% if reg_total.num_contests %}
{% set show_contests = reg_stats[0].contest != None %}
<table
class=
data
>
<thead>
<tr>
{% if show_contests %}
<th>
{{ round.get_level().name|capitalize }}
<th>
Stav
{% else %}
<th>
{{ reg_stats[0].region.type_name()|capitalize }}
<th>
Počet soutěží
{% endif %}
<th>
Počet účastníků
<th>
Počet nepotvrzených
</tr>
</thead>
{% for
(c, count) in contests_coun
ts %}
{% for
rs in reg_sta
ts %}
<tr>
<td><a
href=
'{{ url_for('
org_contest
',
ct_id=
c.contest_id)
}}'
>
{{ c.place.name }}
</a>
{% with state=c.state %}
{% if show_contests %}
<td><a
href=
'{{ url_for('
org_contest
',
ct_id=
rs.contest.contest_id)
}}'
>
{{ rs.region.name }}
</a>
{% with state=rs.contest.state %}
<td
class=
'rstate-{{state.name}}'
>
{{ state.friendly_name() }}
{% endwith %}
<td>
{{ count }}
{% else %}
<td><a
href=
'{{ ctx.url_for('
org_round
',
hier_id=
rs.region.place_id)
}}'
>
{{ rs.region.name }}
</a>
<td>
{{ rs.num_contests }}
{% endif %}
<td>
{{ rs.num_active_pants }}
<td>
{{ rs.num_unconfirmed_pants }}
{% endfor %}
<tfoot>
<tr>
<th>
Celkem
{% if show_contests %}
<th>
<th>
{{ contests_counts|sum(attribute=1) }}
{% else %}
<th>
{{ reg_total.num_contests }}
{% endif %}
<th>
{{ reg_total.num_active_pants }}
<th>
{{ reg_total.num_unconfirmed_pants }}
</tr>
</tfoot>
</table>
...
...
@@ -139,7 +164,7 @@
{% endif %}
<h3>
Úlohy
</h3>
{% if task
s
%}
{% if task
_info
%}
<table
class=
data
>
<thead>
<tr>
...
...
@@ -151,11 +176,11 @@
{% if can_handle_submits or can_upload %}
<th>
Dávkové operace{% endif %}
</tr>
</thead>
{% for task in task
s
%}
{% for task
, sol_count
in task
_info
%}
<tr>
<td>
{{ task.code }}
<td>
{{ task.name }}
<td>
{{
task.
sol_count }}
<td>
{{ sol_count }}
<td>
{{ task.max_points|decimal|none_value('–') }}
{% if can_manage_round %}
<td><div
class=
"btn-group"
>
...
...
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
sign in
to comment