Exemplo n.º 1
0
class StructureAndFunctionMeasure(_BaseModel):
    class Meta:
        table_name = "forms_structure_and_function_measure"

    class TypeTypes(enum.Enum):
        LeftSide = "E"
        RightSide = "D"

        MaximumInspirationPressure = "PiMax"
        MaximumExpirationPressure = "PeMax"

        PainIntensity = "Intensidade da Dor"

    class SensoryTypeTypes(enum.Enum):
        LightTouch = "Toque Leve"
        Pressure = "Pressão"
        Stings = "Picadas"
        Temperature = "Temperatura"
        TactileLocation = "Localização Tática"
        SimultaneousBilateralTouch = "Toque Bilateral Simultâneo"
        Proprioception = "Propriocepção"

    structure_and_function = ForeignKeyField(StructureAndFunction)

    type = EnumField(TypeTypes, default=None, null=True)

    sensory_type = EnumField(SensoryTypeTypes, default=None, null=True)

    target = CharField(default=None, null=True)
    value = CharField()

    date = DateField()
Exemplo n.º 2
0
    def _parse_definition(self, name, definition):
        """
        Given an arbitrary API definition and the name for the definition, parses it into the appropriate BaseAPIField
        object and returns it.

        :param name: str
        :param definition: dict
        :return: BaseAPIField or None
        """
        parsed_field = None

        if not definition or definition.get("readOnly"):
            pass  # skip this field, maintain parsed_field = None

        elif definition.get(DataType.ENUM.value):
            parsed_field = EnumField(name=name, options=definition[DataType.ENUM.value])

        elif definition.get("type") and definition["type"] not in [DataType.ARRAY.value, DataType.OBJECT.value]:
            parsed_field = self._parse_typed_field(name, definition["type"], definition.get("format", ""), definition)

        elif definition.get("type") and definition["type"] == DataType.ARRAY.value:
            parsed_field = self._parse_array_field(name, definition)

        else:
            parsed_field = self._parse_object_field(name, definition)

        # Hook method for custom validation on a particular field:
        self._custom_field_validation(parsed_field, definition)

        return parsed_field
Exemplo n.º 3
0
class SociodemographicEvaluation(Form):
    class Meta:
        table_name = "forms_sociodemographic_evaluation"

    class CivilStatusTypes(enum.Enum):
        Single = "Solteiro(a)"
        Married = "Casado(a)"
        Divorced = "Divorciado(a)"
        Widowed = "Viúvo(a)"

    class LivesWithStatusTypes(enum.Enum):
        Alone = "Sozinho(a)"
        Relatives = "Familiares"
        Friends = "Amigos"
        Spouse = "Cônjuge"

    class EducationTypes(enum.Enum):
        Illiterate = "Analfabeto(a)"
        Primary = "Primeiro Grau"
        Secondary = "Segundo Grau"
        Tertiary = "Superior/Pós-graduado(a)"

    class OccupationalStatusTypes(enum.Enum):
        Student = "Estudante"
        Unemployed = "Desempregado(a)"
        Employed = "Empregado(a)"
        AwayForHealth = "Afastado(a) por problemas de saúde"
        Retired = "Aposentado(a)"

    civil_status = EnumField(CivilStatusTypes)
    lives_with_status = EnumField(LivesWithStatusTypes)

    education = EnumField(EducationTypes)
    occupational_status = EnumField(OccupationalStatusTypes)
    current_job = CharField(default=None, null=True)
    last_job = CharField(default=None, null=True)

    is_sick = BooleanField()
    diseases = JSONField(default=None, null=True)
    is_medicated = BooleanField()
    medicines = JSONField(default=None, null=True)
Exemplo n.º 4
0
class IsLanguage(Requirement):
    language = EnumField(Language, null=True)

    def serialize(self):
        return {
            **super().serialize(),
            'language': self.language.name,
        }

    @classmethod
    def deserialize(cls, values):
        return cls(language=Language[values['language']])
Exemplo n.º 5
0
class IsMinimumCondition(Requirement):
    condition = EnumField(Condition, null=True)

    def serialize(self):
        return {
            **super().serialize(),
            'condition': self.condition.name,
        }

    @classmethod
    def deserialize(cls, values):
        return cls(condition=Condition[values['condition']])
Exemplo n.º 6
0
class IsBorder(Requirement):
    border = EnumField(Border, null=True)

    def serialize(self):
        return {
            **super().serialize(),
            'border': self.border.name,
        }

    @classmethod
    def deserialize(cls, values):
        return cls(border=Border[values['border']], )
Exemplo n.º 7
0
class ReleaseImageBundle(TimestampedModel, models.Model):
    class Target(Enum):
        COCKATRICE = 0

    url = models.CharField(max_length=511)
    release = models.ForeignKey(
        CubeRelease,
        related_name='image_bundles',
        on_delete=models.CASCADE,
    )
    target = EnumField(Target)

    class Meta:
        unique_together = ('release', 'target')
Exemplo n.º 8
0
class PatientInformation(Form):
    class Meta:
        table_name = "forms_patient_information"

    class GenderTypes(enum.Enum):
        Masculine = "Masculino"
        Feminine = "Feminino"

    user = ForeignKeyField(User, unique=True)

    gender = EnumField(GenderTypes)
    birthday = DateField()

    acquaintance_phone = CharField()

    address = CharField()
    neighborhood = CharField()
    city = CharField()
    country = CharField()
Exemplo n.º 9
0
class StructureAndFunction(Form):
    class Meta:
        table_name = "forms_structure_and_function"

    class StructureAndFunctionTypes(enum.Enum):
        Goniometry = "goniometry"
        AshworthScale = "ashworth_scale"
        SensoryEvaluation = "sensory_evaluation"
        RespiratoryMuscleStrength = "respiratory_muscle_strength"
        Spirometry = "spirometry"
        PeakFlow = "peak_flow"
        Ventilometry = "ventilometry"
        PainEvaluation = "pain_evaluation"
        MuscleStrength = "muscle_strength"
        Baropodometry = "baropodometry"
        Electromyography = "electromyography"
        Biophotogrammetry = "biophotogrammetry"
        Dynamometry = "dynamometry"

    type = EnumField(StructureAndFunctionTypes)
Exemplo n.º 10
0
    def test_update_required_fields(self):
        """Tests updating a list of fields to be 'required'"""
        self.object_field.object_fields = []

        base_field = BaseAPIField(name="baseField", data_type=DataType.INTEGER)
        random_field = ArrayField(name="randomField")
        enum_field = EnumField(name="enumField")
        space_field = ObjectField(name="spaceField")

        self.object_field.add_fields([base_field, random_field, enum_field, space_field])

        assert base_field.required is False
        assert random_field.required is False
        assert enum_field.required is False
        assert space_field.required is False

        self.object_field.update_required_fields(["baseField", "randomField", "phoneField", "spaceField"])

        assert base_field.required is True
        assert random_field.required is True
        assert enum_field.required is False
        assert space_field.required is True
Exemplo n.º 11
0
class DraftSession(models.Model):
    class DraftState(Enum):
        DRAFTING = 0
        COMPLETED = 1
        ABANDONED = 2

    started_at = models.DateTimeField(editable=False,
                                      blank=False,
                                      auto_now_add=True)
    key = models.CharField(max_length=255)
    ended_at = models.DateTimeField(null=True)
    draft_format = models.CharField(max_length=127)
    reverse = models.BooleanField()
    state = EnumField(DraftState, default=DraftState.DRAFTING)
    infinites: Infinites = OrpField(model_type=Infinites)
    pool_specification = models.ForeignKey(
        PoolSpecification,
        on_delete=models.CASCADE,
        related_name='draft_sessions',
    )
    limited_session = models.ForeignKey(
        LimitedSession,
        on_delete=models.CASCADE,
        related_name='draft_session',
        null=True,
    )
    rating_maps = GenericRelation(
        'rating.RatingMap',
        'ratings_for_object_id',
        'ratings_for_content_type',
    )

    objects = DraftSessionQueryset.as_manager()

    @property
    def users(self):
        return (seat.user for seat in self.seats.all())
Exemplo n.º 12
0
    def test_generate_discriminator_value(self):
        """
        Tests that ObjectField generates the right value for a discriminator field.

        The discriminator is a field in the definition that can be used to filter which of the objects sub-fields should
        be when generating fake data. The `ObjectField.discriminator` attribute contains a string that indicates the
        name of the field in `ObjectField.object_fields` that acts as a discriminator.

        This function, `ObjectField.generate_discriminator_value` uses the fake data generator to set a random value for
        the field indicated by `ObjectField.discriminator`.

        `BaseAPIField.discriminator_values` holds the values for this discriminator that will allow the field to be used
        in fake data generation.
        """
        discriminator_field = EnumField(name="treeTypes", options=["maple", "oak", "pine"])

        # We haven't set a discriminator on the ObjectField yet, so we should get an empty string if we try to generate
        # a value for nothing:
        assert self.object_field.generate_discriminator_value(self.faker) == ""

        self.object_field.discriminator = discriminator_field.name

        # Trying again, we have a discriminator set on the ObjectField, but we haven't added the field yet:
        assert self.object_field.generate_discriminator_value(self.faker) == ""

        self.object_field.add_field(discriminator_field)

        # Now we should get data returned that is valid for this field - which in this case, means it must be one of the
        # enum options:
        assert self.object_field.generate_discriminator_value(self.faker) in discriminator_field.options

        # If we pass in an override for this field, it should use that value no matter what:
        discriminator_value = self.object_field.generate_discriminator_value(
            self.faker, overrides={discriminator_field.name: "cedar"}
        )
        assert discriminator_value == "cedar"
Exemplo n.º 13
0
class LimitedSession(models.Model):
    class LimitedSessionState(Enum):
        DECK_BUILDING = 0
        PLAYING = 1
        FINISHED = 2

    created_at = models.DateTimeField(editable = False, blank = False, auto_now_add = True)
    playing_at = models.DateTimeField(null = True)
    finished_at = models.DateTimeField(null = True)
    name = models.CharField(max_length = 255, default = get_random_name)
    state = EnumField(LimitedSessionState, default = LimitedSessionState.DECK_BUILDING)
    format = models.CharField(max_length = 255)
    game_type = models.CharField(max_length = 255)
    open_decks = models.BooleanField(default = False)
    open_pools = models.BooleanField(default = False)
    pool_specification = models.ForeignKey(PoolSpecification, on_delete = models.CASCADE, related_name = 'sessions')
    infinites: Infinites = OrpField(model_type = Infinites)
    allow_cheating: bool = models.BooleanField(default = False)
    tournament_type: t.Type[to.Tournament] = StringMapField(to.Tournament.tournaments_map)
    tournament_config = models.JSONField()
    match_type: MatchType = SerializeableField(MatchType)
    tournament = models.OneToOneField(Tournament, on_delete = models.SET_NULL, related_name = 'limited_session', null = True)

    def create_tournament(self) -> Tournament:
        if self.tournament is not None:
            raise ValueError('tournament already created')
        if self.pools.filter(pool_decks__isnull = True).exists():
            raise ValueError('not all participants have submitted a deck')

        with transaction.atomic():
            tournament = Tournament.objects.create(
                name = self.name,
                tournament_type = self.tournament_type,
                tournament_config = self.tournament_config,
                match_type = self.match_type,
            )

            for pool in self.pools.all():
                TournamentParticipant.objects.create(
                    tournament = tournament,
                    deck = pool.pool_decks.order_by('created_at').last(),
                    player = pool.user,
                )

            self.tournament = tournament

            self.save(update_fields = ('tournament',))

            tournament.advance()

            return tournament

    def complete(self) -> None:
        if self.state == self.LimitedSessionState.FINISHED:
            return
        self.state = self.LimitedSessionState.FINISHED
        self.finished_at = datetime.datetime.now()
        self.save(update_fields = ('state', 'finished_at'))

    @property
    def expected_match_amount(self) -> int:
        number_players = self.pools.all().count()
        if number_players <= 1:
            return 0
        return int(math.factorial(number_players) / (2 * math.factorial(number_players - 2)))

    @property
    def decks_public(self) -> bool:
        return (
            self.state.value >= self.LimitedSessionState.FINISHED.value
            or (
                self.state == self.LimitedSessionState.PLAYING
                and self.open_decks
            )
        )

    @property
    def pools_public(self) -> bool:
        return self.open_pools or self.decks_public
Exemplo n.º 14
0
 def setup_class(cls):
     """Initialize the EnumField that will be tested."""
     cls.options = ["PERMANENT_CHANGE_OF_STATION", "RETIREMENT", "SEPARATION", "GHC", "NTS"]
     cls.enum_field = EnumField(name="ordersType", options=cls.options)
     cls.faker = MilMoveData()
Exemplo n.º 15
0
    def test_generate_fake_data_discriminator(self, mocker):
        """
        Tests that generate_fake_data works correctly for an object with a discriminator.
        """
        self.setup_mocks(mocker)

        self.object_field.add_field(EnumField(name="petType", options=["dog", "cat", "fish"], required=True))
        self.object_field.discriminator = "petType"  # now the new field counts as a discriminator for the data

        # create some new sub-objects with fields
        dog = ObjectField(
            object_fields=[
                BaseAPIField(name="averageTailWags", data_type=DataType.INTEGER, required=True),
                BaseAPIField(name="city", data_type=DataType.CITY),
            ]
        )
        dog.add_discriminator_value("dog")  # all these fields now only apply to the "dog" petType
        self.object_field.combine_fields(dog)  # now we add the discriminated fields

        cat = ObjectField(
            object_fields=[
                BaseAPIField(name="lastHairballDate", data_type=DataType.DATE, required=True),
                BaseAPIField(name="city", data_type=DataType.CITY),  # dog also has one of these!
            ]
        )
        cat.add_discriminator_value("cat")  # all these fields now only apply to the "cat" petType
        self.object_field.combine_fields(cat)

        fish = ObjectField(
            object_fields=[
                EnumField(name="color", options=["blue", "magenta", "neon orange"], required=True),
            ]
        )
        fish.add_discriminator_value("fish")  # all these fields now only apply to the "fish" petType
        self.object_field.combine_fields(fish)

        # We should get all the base fields on this object - they have no discriminator values at all, which means all
        # are fine. And we know this will be a dog because our mock EnumField always gives us the first element:
        assert self.object_field.generate_fake_data(self.faker) == {
            "objectArray": [
                {"integer": self.MOCK_FAKE_DATA[DataType.INTEGER]},
                {"integer": self.MOCK_FAKE_DATA[DataType.INTEGER]},
            ],
            "phone": self.MOCK_FAKE_DATA[DataType.PHONE],
            "petType": "dog",
            "averageTailWags": self.MOCK_FAKE_DATA[DataType.INTEGER],
        }

        # Use overrides to get the fields for a cat petType:
        cat_overrides = {
            "petType": "cat",
            "city": "anywhere",
        }
        assert self.object_field.generate_fake_data(self.faker, overrides=cat_overrides) == {
            "objectArray": [
                {"integer": self.MOCK_FAKE_DATA[DataType.INTEGER]},
                {"integer": self.MOCK_FAKE_DATA[DataType.INTEGER]},
            ],
            "phone": self.MOCK_FAKE_DATA[DataType.PHONE],
            "petType": "cat",
            "lastHairballDate": self.MOCK_FAKE_DATA[DataType.DATE],
            "city": "anywhere",
        }

        # And a fish!
        assert self.object_field.generate_fake_data(self.faker, overrides={"petType": "fish"}, require_all=True) == {
            "nestedObject": {
                "sentence": self.MOCK_FAKE_DATA[DataType.SENTENCE],
                "emails": [
                    self.MOCK_FAKE_DATA[DataType.EMAIL],
                    self.MOCK_FAKE_DATA[DataType.EMAIL],
                ],
            },
            "objectArray": [
                {"integer": self.MOCK_FAKE_DATA[DataType.INTEGER], "name": self.MOCK_FAKE_DATA[DataType.FIRST_NAME]},
                {"integer": self.MOCK_FAKE_DATA[DataType.INTEGER], "name": self.MOCK_FAKE_DATA[DataType.FIRST_NAME]},
            ],
            "phone": self.MOCK_FAKE_DATA[DataType.PHONE],
            "integer": self.MOCK_FAKE_DATA[DataType.INTEGER],
            "sentence": self.MOCK_FAKE_DATA[DataType.SENTENCE],
            "petType": "fish",
            "color": "blue",  # the first element in the EnumField
        }
Exemplo n.º 16
0
class KineticFunctionalEvaluation(Form):
    class Meta:
        table_name = "forms_kinetic_functional_evaluation"

    class StructureAndFunctionTypes(enum.Flag):
        Goniometry = 1
        AshworthScale = 2
        SensoryEvaluation = 4
        RespiratoryMuscleStrength = 8
        Spirometry = 16
        PeakFlow = 32
        Ventilometry = 64
        PainEvaluation = 128
        MuscleStrength = 256
        Baropodometry = 512
        Electromyography = 1024
        Biophotogrammetry = 2048
        Dynamometry = 4096

        @classmethod
        def valid_string_values(cls):
            return {
                "Goniometria",
                "Escala de Ashworth",
                "Avaliação Sensorial",
                "Força Muscular Respiratória",
                "Espirometria",
                "Peak-Flow",
                "Ventilometria",
                "Avaliação da Dor",
                "Força Muscular",
                "Baropodometria",
                "Eletromiografia",
                "Biofotogrametria",
                "Dinamometria",
            }

        @classmethod
        def from_string(cls, string):
            convert_table = {
                "Goniometria": cls.Goniometry,
                "Escala de Ashworth": cls.AshworthScale,
                "Avaliação Sensorial": cls.SensoryEvaluation,
                "Força Muscular Respiratória": cls.RespiratoryMuscleStrength,
                "Espirometria": cls.Spirometry,
                "Peak-Flow": cls.PeakFlow,
                "Ventilometria": cls.Ventilometry,
                "Avaliação da Dor": cls.PainEvaluation,
                "Força Muscular": cls.MuscleStrength,
                "Baropodometria": cls.Baropodometry,
                "Eletromiografia": cls.Electromyography,
                "Biofotogrametria": cls.Biophotogrammetry,
                "Dinamometria": cls.Dynamometry,
            }
            return convert_table[string]

        @classmethod
        def to_string(cls, enum_item):
            convert_table = {
                cls.Goniometry: "Goniometria",
                cls.AshworthScale: "Escala de Ashworth",
                cls.SensoryEvaluation: "Avaliação Sensorial",
                cls.RespiratoryMuscleStrength: "Força Muscular Respiratória",
                cls.Spirometry: "Espirometria",
                cls.PeakFlow: "Peak-Flow",
                cls.Ventilometry: "Ventilometria",
                cls.PainEvaluation: "Avaliação da Dor",
                cls.MuscleStrength: "Força Muscular",
                cls.Baropodometry: "Baropodometria",
                cls.Electromyography: "Eletromiografia",
                cls.Biophotogrammetry: "Biofotogrametria",
                cls.Dynamometry: "Dinamometria",
            }
            return convert_table[enum_item]

    class ActivityAndParticipationTypes(enum.Flag):
        MarchEvaluation = 1
        SixMWalkTest = 2
        BergsBalanceScale = 4
        FunctionalScopeTest = 8
        TimeUpGo = 16
        ComfortableAndFastRunningSpeed = 32
        StepTest = 64
        QVCysticFibrosis = 128
        SF36 = 256
        WHODAS2 = 512
        MIF = 1024
        WOMAC = 2048
        DASH = 4096
        LondonScale = 8192
        EORCTQLQC30 = 16384
        SaintGeorge = 32768
        BarthelsScale = 65536

        @classmethod
        def valid_string_values(cls):
            return {
                "Avaliação de Marcha",
                "Teste de Caminhada 6M",
                "Escala de Equilíbrio de Berg",
                "Teste do Alcane Funcional",
                "Time Up Go (TUG)",
                "Velocidade de marcha confortável e rápida (10m)",
                "Teste do Degrau",
                "QV Fibrose Cística",
                "SF-36",
                "WHODAS 2.0",
                "MIF",
                "WOMAC",
                "DASH",
                "Escala London",
                "EORCT QLQ C-30",
                "Saint George",
                "Escala de Barthel",
            }

        @classmethod
        def from_string(cls, string):
            convert_table = {
                "Avaliação de Marcha": cls.MarchEvaluation,
                "Teste de Caminhada 6M": cls.SixMWalkTest,
                "Escala de Equilíbrio de Berg": cls.BergsBalanceScale,
                "Teste do Alcane Funcional": cls.FunctionalScopeTest,
                "Time Up Go (TUG)": cls.TimeUpGo,
                "Velocidade de marcha confortável e rápida (10m)":
                cls.ComfortableAndFastRunningSpeed,
                "Teste do Degrau": cls.StepTest,
                "QV Fibrose Cística": cls.QVCysticFibrosis,
                "SF-36": cls.SF36,
                "WHODAS 2.0": cls.WHODAS2,
                "MIF": cls.MIF,
                "WOMAC": cls.WOMAC,
                "DASH": cls.DASH,
                "Escala London": cls.LondonScale,
                "EORCT QLQ C-30": cls.EORCTQLQC30,
                "Saint George": cls.SaintGeorge,
                "Escala de Barthel": cls.BarthelsScale,
            }
            return convert_table[string]

        @classmethod
        def to_string(cls, enum_item):
            convert_table = {
                cls.MarchEvaluation: "Avaliação de Marcha",
                cls.SixMWalkTest: "Teste de Caminhada 6M",
                cls.BergsBalanceScale: "Escala de Equilíbrio de Berg",
                cls.FunctionalScopeTest: "Teste do Alcane Funcional",
                cls.TimeUpGo: "Time Up Go (TUG)",
                cls.ComfortableAndFastRunningSpeed:
                "Velocidade de marcha confortável e rápida (10m)",
                cls.StepTest: "Teste do Degrau",
                cls.QVCysticFibrosis: "QV Fibrose Cística",
                cls.SF36: "SF-36",
                cls.WHODAS2: "WHODAS 2.0",
                cls.MIF: "MIF",
                cls.WOMAC: "WOMAC",
                cls.DASH: "DASH",
                cls.LondonScale: "Escala London",
                cls.EORCTQLQC30: "EORCT QLQ C-30",
                cls.SaintGeorge: "Saint George",
                cls.BarthelsScale: "Escala de Barthel",
            }
            return convert_table[enum_item]

    #: Main and Functional Complaints.
    clinic_diagnostic = TextField()
    main_complaint = TextField()
    functional_complaint = TextField()

    #: Patient Clinical and Functional History.
    clinical_history = TextField()
    functional_history = TextField()

    #: Physical-Functional Tests Planning.
    structure_and_function = EnumField(StructureAndFunctionTypes,
                                       default=StructureAndFunctionTypes(0))
    activity_and_participation = EnumField(
        ActivityAndParticipationTypes,
        default=ActivityAndParticipationTypes(0))

    #: Physical-Functional Tests Results.
    physical_functional_tests_results = TextField(default=None, null=True)
    #: Complementary Exams Result.
    complementary_exams_results = TextField(default=None, null=True)

    #: Functional Kinetic Diagnosis.
    deficiency_diagnosis = TextField(default=None, null=True)
    activity_limitation_diagnosis = TextField(default=None, null=True)
    participation_restriction_diagnosis = TextField(default=None, null=True)
    environment_factors_diagnosis = TextField(default=None, null=True)
    functional_objectives_diagnosis = JSONField(default=None, null=True)
    therapeutic_plan_diagnosis = JSONField(default=None, null=True)

    reevaluation_dates = JSONField(default=None, null=True)

    #: Assessors.
    academic_assessor = CharField(default=None, null=True)
    preceptor_assessor = CharField(default=None, null=True)
Exemplo n.º 17
0
class Tournament(TimestampedModel):
    class TournamentState(Enum):
        ONGOING = 0
        FINISHED = 1
        CANCELED = 2

    name = models.CharField(max_length = 255, default = get_random_name)
    state = EnumField(TournamentState, default = TournamentState.ONGOING)
    tournament_type: t.Type[to.Tournament] = StringMapField(to.Tournament.tournaments_map)
    tournament_config = models.JSONField()
    match_type: MatchType = SerializeableField(MatchType)
    finished_at = models.DateTimeField(null = True)

    @property
    def tournament(self) -> to.Tournament[TournamentParticipant]:
        if not hasattr(self, '_tournament'):
            players = frozenset(self.participants.all())
            self._tournament = self.tournament_type(
                players,
                seed_map = {
                    p: p.seed
                    for p in
                    players
                },
                **self.tournament_config,
            )
        return self._tournament

    @property
    def completed_rounds(self) -> t.Sequence[to.CompletedRound[TournamentParticipant]]:
        return [
            _round.completed_round
            for _round in
            self.rounds.prefetch_related('matches', 'matches__seats').order_by('index')
        ]

    def next_round(self) -> t.Optional[to.Round[TournamentParticipant]]:
        return self.tournament.get_round(self.completed_rounds)

    def schedule_next_round(self) -> t.Optional[TournamentRound]:
        _round = self.next_round()
        if not _round:
            return None
        with transaction.atomic():
            previous_max = self.rounds.aggregate(Max('index'))['index__max']
            scheduled_round = TournamentRound.objects.create(
                tournament = self,
                index = 0 if previous_max is None else previous_max + 1,
            )
            for _match in _round.matches:
                scheduled_match = ScheduledMatch.objects.create(
                    round = scheduled_round
                )
                for player in _match.players:
                    seat = ScheduledSeat.objects.create(
                        match = scheduled_match,
                        participant = player,
                    )
                if len(_match.players) == 1:
                    MatchResult.objects.create(
                        scheduled_match = scheduled_match
                    )
                    SeatResult.objects.create(
                        scheduled_seat = seat,
                        wins = 0,
                    )
        return scheduled_round

    def _complete_limited_session(self) -> None:
        try:
            self.limited_session.complete()
        except ObjectDoesNotExist:
            pass

    def _new_season(self) -> None:
        try:
            self.season
        except ObjectDoesNotExist:
            return
        _create_seasons.delay()

    def complete(self) -> t.Sequence[t.Collection[TournamentParticipant]]:
        results = self.tournament.get_ranked_players(self.completed_rounds)
        with transaction.atomic():
            for idx, tier in enumerate(results):
                for participant in tier:
                    participant.placement = idx
                    participant.save(update_fields = ('placement',))
            for winner in results[0]:
                TournamentWinner.objects.create(
                    tournament = self,
                    participant = winner,
                )
            self.state = self.TournamentState.FINISHED
            self.finished_at = datetime.datetime.now()
            self.save(update_fields = ('state', 'finished_at'))
            self._complete_limited_session()
        self._new_season()
        return results

    def advance(self) -> None:
        if ScheduledMatch.objects.filter(
            round__tournament = self,
            round__index = self.rounds.aggregate(Max('index'))['index__max'] or 0,
            result__isnull = True,
        ).exists():
            return

        if not self.schedule_next_round():
            self.complete()

    def cancel(self) -> Tournament:
        with transaction.atomic():
            self.state = self.TournamentState.CANCELED
            self.finished_at = datetime.datetime.now()
            self.save(update_fields = ('state', 'finished_at'))
            self._complete_limited_session()
        return self