diff --git a/etc/config.py.example b/etc/config.py.example
index d214b75aa9c195f23f7d8428f64c082564212248..2d14749df02c79a6e27ca37d4b5e8e29463faffb 100644
--- a/etc/config.py.example
+++ b/etc/config.py.example
@@ -47,3 +47,7 @@ JOB_GC_PERIOD = 60
 
 # Za jak dlouho expiruje dokončená dávka [min]
 JOB_EXPIRATION = 5
+
+# Automatické přihlašování účastníků do testovací soutěže
+# (kolo aktuální_ročník-T-1, celostátní soutěž)
+AUTO_REGISTER_TEST = True
diff --git a/mo/web/user.py b/mo/web/user.py
index e54667f5e6b7cfc28ac7022658cafa175ef0e269..d4c70451c82986e50568152161d2d9a1efdf413e 100644
--- a/mo/web/user.py
+++ b/mo/web/user.py
@@ -3,9 +3,11 @@ from flask_wtf import FlaskForm
 import flask_wtf.file
 from sqlalchemy import and_
 from sqlalchemy.orm import joinedload
+from typing import List, Tuple
 import werkzeug.exceptions
 import wtforms
 
+import mo
 import mo.config as config
 import mo.db as db
 import mo.submit
@@ -18,21 +20,56 @@ import mo.web.util
 
 @app.route('/user')
 def user_index():
-    sess = db.get_session()
-
-    pions = (sess.query(db.Participation, db.Contest, db.Round)
-             .select_from(db.Participation)
-             .join(db.Contest, db.Contest.master_contest_id == db.Participation.contest_id)
-             .join(db.Round)
-             .filter(db.Participation.user == g.user)
-             .options(joinedload(db.Contest.place))
-             .order_by(db.Round.year.desc(), db.Round.category, db.Round.seq, db.Round.part)
-             .all())
+    pcrs = load_pcrs()
+    if getattr(config, 'AUTO_REGISTER_TEST', False) and not any(round.category == 'T' for pion, contest, round in pcrs):
+        if register_to_test():
+            pcrs = load_pcrs()
 
     return render_template(
         'user_index.html',
-        pions=pions,
+        pions=pcrs,
+    )
+
+
+def load_pcrs() -> List[Tuple[db.Participation, db.Contest, db.Round]]:
+    return (db.get_session().query(db.Participation, db.Contest, db.Round)
+            .select_from(db.Participation)
+            .join(db.Contest, db.Contest.master_contest_id == db.Participation.contest_id)
+            .join(db.Round)
+            .filter(db.Participation.user == g.user)
+            .options(joinedload(db.Contest.place))
+            .order_by(db.Round.year.desc(), db.Round.category, db.Round.seq, db.Round.part)
+            .all())
+
+
+def register_to_test() -> bool:
+    sess = db.get_session()
+    round = sess.query(db.Round).filter_by(year=mo.current_year, category='T', seq=1, part=0).one_or_none()
+    if not round:
+        app.logger.error(f'Nemohu najít kolo {mo.current_year}-T-1')
+        return False
+
+    if round.level != 0:
+        app.logger.error(f'Kolo {round.round_code_short()} není na celostátní úrovni')
+        return False
+
+    contest = sess.query(db.Contest).filter_by(round=round).limit(1).one_or_none()
+    if not contest:
+        app.logger.error(f'Kolo {round.round_code_short()} nemá soutěž')
+        return False
+
+    pion = db.Participation(user=g.user, contest=contest, place=contest.place, state=db.PartState.registered)
+    sess.add(pion)
+    sess.flush()
+    mo.util.log(
+        type=db.LogType.participant,
+        what=g.user.user_id,
+        details={'action': 'add-to-contest', 'new': db.row2dict(pion)},
     )
+    sess.commit()
+
+    app.logger.info(f'Účastník #{g.user.user_id} automaticky registrován do soutěže #{contest.contest_id}')
+    return True
 
 
 def get_contest(id: int) -> db.Contest: