Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#78] Added the Variant class. #122

Merged
merged 1 commit into from
Nov 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions alembic/versions/91ed52b72b82_created_variant_class.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
"""Created Variant class.

Revision ID: 91ed52b72b82
Revises: 644f5251fc0d
Create Date: 2024-11-22 07:57:46.848687
"""

from alembic import op

import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision = "91ed52b72b82"
down_revision = "644f5251fc0d"


def upgrade():
"""Upgrade the tables."""
op.create_table(
"Variants",
sa.Column("id", sa.Integer(), nullable=False),
sa.ForeignKeyConstraint(
["id"],
["Tasks.id"],
),
sa.PrimaryKeyConstraint("id"),
)
op.alter_column(
"Projects",
"fps",
existing_type=sa.REAL(),
type_=sa.Float(precision=3),
existing_nullable=True,
)
op.alter_column(
"Shots",
"fps",
existing_type=sa.REAL(),
type_=sa.Float(precision=3),
existing_nullable=True,
)


def downgrade():
"""Downgrade the tables."""
# ### commands auto generated by Alembic - please adjust! ###
op.alter_column(
"Shots",
"fps",
existing_type=sa.Float(precision=3),
type_=sa.REAL(),
existing_nullable=True,
)
op.alter_column(
"Projects",
"fps",
existing_type=sa.Float(precision=3),
type_=sa.REAL(),
existing_nullable=True,
)
op.drop_table("Variants")
2 changes: 2 additions & 0 deletions src/stalker/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
from stalker.models.template import FilenameTemplate
from stalker.models.ticket import Ticket, TicketLog
from stalker.models.type import EntityType, Type
from stalker.models.variant import Variant
from stalker.models.version import Version
from stalker.models.wiki import Page

Expand Down Expand Up @@ -125,6 +126,7 @@
"UnitMixin",
"User",
"Vacation",
"Variant",
"Version",
"WorkingHours",
"WorkingHoursMixin",
Expand Down
9 changes: 8 additions & 1 deletion src/stalker/db/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
logger: logging.Logger = log.get_logger(__name__)

# TODO: Try to get it from the API (it was not working inside a package before)
alembic_version: str = "644f5251fc0d"
alembic_version: str = "91ed52b72b82"


def setup(settings: Optional[Dict[str, Any]] = None) -> None:
Expand Down Expand Up @@ -145,6 +145,7 @@ def init() -> None:
"Type",
"User",
"Vacation",
"Variant",
"Version",
]

Expand Down Expand Up @@ -205,6 +206,12 @@ def init() -> None:
status_codes=defaults.task_status_codes,
user=admin,
)
create_entity_statuses(
entity_type="Variant",
status_names=defaults.task_status_names,
status_codes=defaults.task_status_codes,
user=admin,
)
create_entity_statuses(
entity_type="Review",
status_names=defaults.review_status_names,
Expand Down
42 changes: 21 additions & 21 deletions src/stalker/models/task.py
Original file line number Diff line number Diff line change
Expand Up @@ -568,7 +568,7 @@ class Task(
this resource is used for the whole task. This is useful when several
alternative resources have been specified. Normally the selected resource
can change after each break. A break is an interval of at least one
timeslot where no resources were available.
time slot where no resources were available.

:attr:`.persistent_allocation` attribute is True by default.

Expand Down Expand Up @@ -649,7 +649,7 @@ class Task(
For parent tasks that have both effort based and duration based children
tasks the percent complete is calculated as if the
:attr:`.total_logged_seconds` is properly filled for duration based
tasks proportinal to the elapsed time from the :attr:`.start` attr
tasks proportional to the elapsed time from the :attr:`.start` attr
value.

Even tough, the percent_complete attribute of a task is
Expand Down Expand Up @@ -841,7 +841,7 @@ class Task(
Project :class:`.Structure` s.

The :attr:`.path` attribute is a repository relative path, where as
the :attr:`.absolute_path` is the full path and includs the OS
the :attr:`.absolute_path` is the full path and includes the OS
dependent repository path.

.. versionadded: 0.2.13
Expand All @@ -851,7 +851,7 @@ class Task(
to the same :class:`.Good`.

Its main purpose of existence is to be able to generate
:class:`.BugdetEntry` instances from the tasks that are related to the
:class:`.BudgetEntry` instances from the tasks that are related to the
same :class:`.Good` and because the Goods are defining the cost and MSRP
of different things, it is possible to create BudgetEntries and thus
:class;`.Budget` s with this information.
Expand Down Expand Up @@ -879,7 +879,7 @@ class Task(
responsible (List[User]): A list of :class:`.User` instances that is responsible
of this task.
watchers (List[User]): A list of :class:`.User` those are added this Task
instance to their watchlist.
instance to their watch list.
start (datetime.datetime): The start date and time of this task instance. It is
only used if the :attr:`.schedule_constraint` attribute is set to
:attr:`.CONSTRAIN_START` or :attr:`.CONSTRAIN_BOTH`. The default value
Expand Down Expand Up @@ -921,7 +921,7 @@ class Task(
of a task with alternative resources. Should be one of ['minallocated',
'maxloaded', 'minloaded', 'order', 'random'] and the default value is
'minallocated'. For more information read the :class:`.Task` class
documetation.
documentation.
persistent_allocation (bool): Specifies that once a resource is picked from the
list of alternatives this resource is used for the whole task. The default
value is True. For more information read the :class:`.Task` class
Expand Down Expand Up @@ -1672,7 +1672,7 @@ def _validate_priority(self, key: str, priority: Union[int, float]) -> int:
this range.

Raises:
TypeError: If the given priorty value is not an integer or float.
TypeError: If the given priority value is not an integer or float.

Returns:
int: The validated priority value.
Expand Down Expand Up @@ -3433,54 +3433,54 @@ def to_tjp(self) -> str:
# with new duration
@event.listens_for(TimeLog._start, "set")
def update_time_log_task_parents_for_start(
tlog: TimeLog,
timelog: TimeLog,
new_start: datetime.datetime,
old_start: datetime.datetime,
initiator: AttributeEvent,
) -> None:
"""Update the parent task of the TimeLog.task if the new_start value is changed.

Args:
tlog (TimeLog): The TimeLog instance.
timelog (TimeLog): The TimeLog instance.
new_start (datetime.datetime): The datetime.datetime instance showing the new
value.
old_start (datetime.datetime): The datetime.datetime instance showing the old
value.
initiator (AttributeEvent): Currently not used.
"""
logger.debug(f"Received set event for new_start in target : {tlog}")
if tlog.end and old_start and new_start:
old_duration = tlog.end - old_start
new_duration = tlog.end - new_start
__update_total_logged_seconds__(tlog, new_duration, old_duration)
logger.debug(f"Received set event for new_start in target : {timelog}")
if timelog.end and old_start and new_start:
old_duration = timelog.end - old_start
new_duration = timelog.end - new_start
__update_total_logged_seconds__(timelog, new_duration, old_duration)


@event.listens_for(TimeLog._end, "set")
def update_time_log_task_parents_for_end(
tlog: TimeLog,
timelog: TimeLog,
new_end: datetime.datetime,
old_end: datetime.datetime,
initiator: sqlalchemy.orm.attributes.AttributeEvent,
) -> None:
"""Update the parent task of the TimeLog.task if the new_end value is changed.

Args:
tlog (TimeLog): The TimeLog instance.
timelog (TimeLog): The TimeLog instance.
new_end (datetime.datetime): The datetime.datetime instance showing the new
value.
old_end (datetime.datetime): The datetime.datetime instance showing the old
value.
initiator (sqlalchemy.orm.attributes.AttributeEvent): Currently not used.
"""
logger.debug(f"Received set event for new_end in target: {tlog}")
logger.debug(f"Received set event for new_end in target: {timelog}")
if (
tlog.start
timelog.start
and isinstance(old_end, datetime.datetime)
and isinstance(new_end, datetime.datetime)
):
old_duration = old_end - tlog.start
new_duration = new_end - tlog.start
__update_total_logged_seconds__(tlog, new_duration, old_duration)
old_duration = old_end - timelog.start
new_duration = new_end - timelog.start
__update_total_logged_seconds__(timelog, new_duration, old_duration)


def __update_total_logged_seconds__(
Expand Down
39 changes: 39 additions & 0 deletions src/stalker/models/variant.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# -*- coding: utf-8 -*-
"""Variant related functions and classes are situated here."""

from sqlalchemy import ForeignKey
from sqlalchemy.orm import Mapped, mapped_column

from stalker.models.task import Task



class Variant(Task):
"""A Task derivative to keep track of Variants in a Task hierarchy.

The basic reason to have the Variant class is to upgrade the variants,
into a Task derivative so that it is possible to create dependencies
between different variants and being able to review them individually.

You see, in previous versions of Stalker, the variants were handled as a
part of the Version instances with a str attribute called `variant_name`.
The down side of that design was not being able to distinguish any reviews
per variant.

So, when a Model task is approved, all its variant approved all together,
even if one of the variants were still getting worked on.

The new design prevents that and gives the variant the level of attention
they deserved.

Variants doesn't introduce any new arguments or attributes. They are just
initialized like any other Tasks.
"""

__tablename__ = "Variants"
__mapper_args__ = {"polymorphic_identity": "Variant"}
variant_id: Mapped[int] = mapped_column(
"id",
ForeignKey("Tasks.id"),
primary_key=True,
)
1 change: 0 additions & 1 deletion src/stalker/models/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,6 @@ class Version(Link, DAGMixin):
)

is_published: Mapped[Optional[bool]] = mapped_column(default=False)

created_with: Mapped[Optional[str]] = mapped_column(String(256))

def __init__(
Expand Down
Loading