Exemplo n.º 1
0
    def test_print_open_report(
        self,
        runner: CliRunner,
        repo_e2e: Repository,
        faker: Faker,
    ) -> None:
        """Test that open returns the expected output."""
        task = Task(description="Description",
                    due=faker.date_time(),
                    priority=3)
        repo_e2e.add(task)
        repo_e2e.commit()
        expected_output = [
            r".*",
            r" +ID +│ +Description +│ +Pri.*",
            r".*",
            fr" +{task.id_} +│ +{task.description} +│ +{task.priority}.*",
            r".*",
        ]

        result = runner.invoke(cli, ["open"])

        assert result.exit_code == 0
        assert report_prints_expected(result.stdout, expected_output,
                                      result.stderr)
Exemplo n.º 2
0
    def test_print_report_report_allows_sorting(
        self,
        runner: CliRunner,
        repo_e2e: Repository,
        faker: Faker,
    ) -> None:
        """
        Given: Three tasks
        When: printing the open report sorting first by ascending priority, and then
            by descending id.
        Then: The tasks are printed in the desired order
        """
        tasks = [
            Task(id_=0, description="Last", priority=3),
            Task(id_=1, description="Middle", priority=3),
            Task(id_=2, description="First", priority=1),
        ]
        for task in tasks:
            repo_e2e.add(task)
        repo_e2e.commit()
        expected_output = [
            r".*",
            r" +ID +│ +Description +│ +Pri.*",
            r".*",
            fr" +{tasks[2].id_} +│ +{tasks[2].description} +│ +{tasks[2].priority}.*",
            fr" +{tasks[1].id_} +│ +{tasks[1].description} +│ +{tasks[1].priority}.*",
            fr" +{tasks[0].id_} +│ +{tasks[0].description} +│ +{tasks[0].priority}.*",
            r".*",
        ]

        result = runner.invoke(cli, ["report", "open", "sort:+priority,-id_"])

        assert result.exit_code == 0
        assert report_prints_expected(result.stdout, expected_output,
                                      result.stderr)
Exemplo n.º 3
0
    def test_repo_add_entity_merges_with_stored_values_before_adding(
        self,
        database: Any,
        repo: Repository,
        repo_tester: RepositoryTester[Repository],
    ) -> None:
        """
        Given: A repository with an entity
        When: Adding that entity with updated values
        Then: The entities are merged before they are commited.

        The Genre model has the `rating` attribute in the `skip_on_merge` configuration
        therefore even if the added entity has a different value, it's not propagated.
        """
        entity = GenreFactory.build()
        repo_tester.insert_entity(database, entity)
        original_entity = entity.copy()
        entity.rating = 3
        entity.name = "new name"
        repo.add(entity, merge=True)

        repo.commit()  # act

        stored_entity = repo_tester.get_all(database, Genre)[0]
        assert stored_entity.rating == original_entity.rating
        assert stored_entity.name == "new name"
Exemplo n.º 4
0
    def test_print_closed_report_can_specify_filter(
            self, runner: CliRunner, insert_tasks_e2e: List[Task],
            repo_e2e: Repository) -> None:
        """
        Given: Two closed tasks, one deleted and another done
        When: the closed report is called with the filter that matches only one of them
        Then: only that task is shown
        """
        task = insert_tasks_e2e[0]
        task.description = "description"
        task.area = "special"
        task.priority = 1
        task.close(TaskState.DELETED)
        repo_e2e.add(task)
        insert_tasks_e2e[1].close()
        repo_e2e.add(insert_tasks_e2e[1])
        repo_e2e.commit()
        expected_output = [
            r".*",
            r" +ID +│ +Description +│ +Area .*",
            r".*",
            fr" +{task.id_} +│ +{task.description} +│ +{task.area}.*",
            r".*",
        ]

        result = runner.invoke(cli, ["closed", "area:special"])

        assert result.exit_code == 0
        assert report_prints_expected(result.stdout, expected_output,
                                      result.stderr)
Exemplo n.º 5
0
    def test_print_recurring_report(self, runner: CliRunner,
                                    repo_e2e: Repository) -> None:
        """Test that recurring returns the expected output."""
        parent = RecurrentTaskFactory.create(description="D",
                                             priority=1,
                                             area="A")
        repo_e2e.add(parent)
        repo_e2e.commit()

        # ECE001: Expression is too complex. Life is tough
        expected_output = [  # noqa: ECE001
            r".*",
            r" +ID +│ +Descr.* +│ +Recur +│ +RecurType +│ +Area +| +Pri +│ +Due.*",
            r".*",
            fr" +{parent.id_} +│ +{parent.description} +│ +{parent.recurrence} +│ +"
            fr"{parent.recurrence_type.value.title()} +│ +{parent.area} +│ +"
            fr"{parent.priority} +│ +{parent.due.year}.*",
            r".*",
        ]

        result = runner.invoke(cli, ["recurring"])

        assert result.exit_code == 0
        assert report_prints_expected(result.stdout, expected_output,
                                      result.stderr)
Exemplo n.º 6
0
def freeze_tasks(
    repo: Repository,
    selector: TaskSelector,
) -> None:
    """Freeze a list of tasks based on a task filter."""
    tasks = _tasks_from_selector(repo, selector)
    for task in tasks:
        if type(task) == Task:
            child_task = task
            if child_task.parent_id is None:
                raise ValueError(
                    f"Task {child_task.id_}: {child_task.description} is not the child"
                    " of any recurrent task, so it can't be frozen")
            parent_task = repo.get(child_task.parent_id, [RecurrentTask])
        elif type(task) == RecurrentTask:
            parent_task = task
            try:
                child_task = repo.search(
                    {
                        "active": True,
                        "parent_id": task.id_
                    }, [Task])[0]
            except EntityNotFoundError as error:
                raise EntityNotFoundError(
                    f"The recurrent task {task.id_}: {task.description} has no active "
                    "children") from error
        parent_task.freeze()
        repo.add(parent_task)
        repo.delete(child_task)
        log.info(
            f"Frozen recurrent task {parent_task.id_}: {parent_task.description} and "
            f"deleted it's last child {child_task.id_}")
    repo.commit()
Exemplo n.º 7
0
def add_task(repo: Repository,
             change: TaskChanges) -> Union[RecurrentTask, Task]:
    """Create a new task.

    If it's a RecurrentTask, it returns the parent.
    """
    task: Optional[TaskType] = None

    if len(change.tags_to_add) > 0:
        change.task_attributes["tags"] = change.tags_to_add

    if change.task_attributes.get("recurrence_type", None) in [
            "recurring",
            "repeating",
    ]:
        task = repo.add(RecurrentTask(**change.task_attributes))
        child_task = repo.add(task.breed_children())

        log.info(f"Added {task.recurrence_type} task {task.id_}:"
                 f" {task.description}")
        log.info(f"Added first child task with id {child_task.id_}")
    else:
        task = repo.add(Task(**change.task_attributes))
        log.info(f"Added task {task.id_}: {task.description}")

    repo.commit()

    return task
Exemplo n.º 8
0
    def test_modify_task_can_remove_tag_that_starts_with_p(
        self,
        runner: CliRunner,
        insert_tasks_e2e: List[Task],
        faker: Faker,
        repo_e2e: Repository,
        caplog: LogCaptureFixture,
    ) -> None:
        """
        Given: A task with a tag that starts with a p
        When: removing the tag
        Then: the tag is removed

        It's necessary in case we start using the `--parent` flag as `-p`, in that case
        when using `pydo mod 0 -python`, it interprets that the parent flag is set
        and that the tag is ython.
        """
        task = insert_tasks_e2e[0]
        task.tags = ["python"]
        repo_e2e.add(task)
        repo_e2e.commit()

        result = runner.invoke(cli, ["mod", str(task.id_), "-python"])

        modified_task = repo_e2e.get(task.id_, [Task])
        assert result.exit_code == 0
        assert re.match(f"Modified task {task.id_}", caplog.records[0].msg)
        assert modified_task.tags == []
        assert modified_task.description == task.description
Exemplo n.º 9
0
def test_task_report_can_print_tags(
    repo: Repository, config: Config, capsys: CaptureFixture[Any]
) -> None:
    """Test that the open report prints the task tags."""
    # Generate the tasks
    task = factories.TaskFactory.create(
        description="Description", tags=["tag1", "tag2"]
    )
    repo.add(task)
    repo.commit()
    # Generate the output
    expected_output = [
        r".*",
        r" +ID.*│ Tags.*",
        r".*",
        r".* +│ tag1, tag2",
        r".*",
    ]

    out, err = run_report(
        "print_task_report",
        {"repo": repo, "config": config, "report_name": "open"},
        capsys,
    )  # act

    assert report_prints_expected(out, expected_output, err)
Exemplo n.º 10
0
def task_(repo: Repository) -> Task:
    """Insert a Task in the FakeRepository."""
    task = factories.TaskFactory.create(state="backlog")
    repo.add(task)
    repo.commit()

    return task
Exemplo n.º 11
0
    def test_print_recurring_report_can_specify_filter(
        self,
        runner: CliRunner,
        insert_parent_tasks_e2e: Tuple[List[RecurrentTask], List[Task]],
        repo_e2e: Repository,
    ) -> None:
        """Test that recurring report accepts a task filter."""
        parent = RecurrentTaskFactory.create(description="D",
                                             area="special",
                                             priority=1)
        repo_e2e.add(parent)
        repo_e2e.commit()
        # ECE001: Expression is too complex. Life is tough
        expected_output = [  # noqa: ECE001
            r".*",
            r" +ID +│ +Descri.* +│ +Recur +│ +RecurType +│ +Area +| +Pri +│ +Due.*",
            r".*",
            fr" +{parent.id_} +│ +{parent.description} +│ +{parent.recurrence} +│ +"
            fr"{parent.recurrence_type.value.title()} +│ +{parent.area} +│ +"
            fr"{parent.priority} +│ +{parent.due.year}.*",
            r".*",
        ]

        result = runner.invoke(cli, ["recurring", "area:special"])

        assert result.exit_code == 0
        assert report_prints_expected(result.stdout, expected_output,
                                      result.stderr)
Exemplo n.º 12
0
    def test_print_frozen_report_can_specify_filter(
        self,
        runner: CliRunner,
        insert_frozen_parent_task_e2e: RecurrentTask,
        repo_e2e: Repository,
    ) -> None:
        """Test that frozen accepts a task filter."""
        task = insert_frozen_parent_task_e2e
        task.description = "d"
        task.area = "special"
        task.priority = 1
        repo_e2e.add(task)
        repo_e2e.commit()
        # ECE001: Expression is too complex. Life is tough
        expected_output = [  # noqa: ECE001
            r".*",
            r" +ID +│ +Description +│ +Recur +│ +RecurType +│ +Area +| +Pri +│ +Due.*",
            r".*",
            fr" +{task.id_} +│ +{task.description} +│ +{task.recurrence} +│ +"
            fr"{task.recurrence_type.value.title()} +│ +{task.area} +│ +"
            fr"{task.priority} +│ +{task.due.year}.*",
            r".*",
        ]

        result = runner.invoke(cli, ["frozen", "area:special"])

        assert result.exit_code == 0
        assert report_prints_expected(result.stdout, expected_output,
                                      result.stderr)
Exemplo n.º 13
0
def insert_multiple_tasks(repo: Repository) -> List[Task]:
    """Insert three Tasks in the repository."""
    tasks = sorted(factories.TaskFactory.create_batch(20, state="backlog"))
    [repo.add(task) for task in tasks]
    repo.commit()

    return tasks
Exemplo n.º 14
0
def test_tags_shows_open_tasks_without_tag(
    repo: Repository, config: Config, capsys: CaptureFixture[Any]
) -> None:
    """
    Given: Two tasks with no tags, only one open
    When: Printing the tags report
    Then: Only the open task is shown in the report
    """
    tasks = factories.TaskFactory.create_batch(2)
    tasks[1].close("done")
    for task in tasks:
        repo.add(task)
    repo.commit()
    expected_output = [
        r".*",
        r" +Name +│ Open Tasks *",
        r".*",
        r" +None +│ +1",
        r".*",
    ]
    capsys.readouterr()

    out, err = run_report("tags", {"repo": repo}, capsys)  # act

    assert report_prints_expected(out, expected_output, err)
Exemplo n.º 15
0
def test_areas_prints_only_areas_with_open_tasks(
    repo: Repository, config: Config, capsys: CaptureFixture[Any]
) -> None:
    """
    Given: Three tasks with different areas, where only one is open
    When: Printing the areas report
    Then: Only the area with the open task is shown
    """
    tasks = factories.TaskFactory.create_batch(3)
    tasks[1].close("done")
    tasks[2].close("deleted")
    for task in tasks:
        repo.add(task)
    repo.commit()
    expected_output = [
        r".*",
        r" +Name +│ Open Tasks *",
        r".*",
        fr" +{tasks[0].area} +│ +1",
        r".*",
    ]
    capsys.readouterr()

    out, err = run_report("areas", {"repo": repo}, capsys)  # act

    assert report_prints_expected(out, expected_output, err)
Exemplo n.º 16
0
def test_areas_prints_only_counts_open_tasks(
    repo: Repository, config: Config, capsys: CaptureFixture[Any]
) -> None:
    """
    Given: Three tasks with the same area, one open, one completed, and other deleted
    When: Printing the areas report
    Then: Only the open task is shown
    """
    tasks = factories.TaskFactory.create_batch(3, area="Area 1")
    tasks[1].close("done")
    tasks[2].close("deleted")
    for task in tasks:
        repo.add(task)
    repo.commit()
    expected_output = [
        r".*",
        r" +Name +│ Open Tasks *",
        r".*",
        r" +Area 1 +│ +1",
        r".*",
    ]
    capsys.readouterr()

    out, err = run_report("areas", {"repo": repo}, capsys)  # act

    assert report_prints_expected(out, expected_output, err)
Exemplo n.º 17
0
def insert_frozen_parent_task_e2e(repo_e2e: Repository) -> RecurrentTask:
    """Insert a RecurrentTask in frozen state."""
    parent_task = factories.RecurrentTaskFactory.create(state="backlog")
    parent_task.freeze()
    repo_e2e.add(parent_task)
    repo_e2e.commit()

    return parent_task
Exemplo n.º 18
0
def tasks_(repo: Repository) -> List[Task]:
    """Insert three Tasks in the FakeRepository."""
    tasks = sorted(factories.TaskFactory.create_batch(3, state="backlog"))
    for task in tasks:
        repo.add(task)
    repo.commit()

    return tasks
Exemplo n.º 19
0
def insert_parent_task(repo: Repository, ) -> Tuple[RecurrentTask, Task]:
    """Insert a RecurrentTask and it's children Task in the FakeRepository."""
    parent_task = factories.RecurrentTaskFactory.create(state="backlog")
    child_task = parent_task.breed_children()

    repo.add(parent_task)
    repo.add(child_task)
    repo.commit()

    return parent_task, child_task
Exemplo n.º 20
0
def insert_frozen_parent_tasks_e2e(
        repo_e2e: Repository) -> List[RecurrentTask]:
    """Insert many RecurrentTask in frozen state."""
    parent_tasks = factories.RecurrentTaskFactory.create_batch(3,
                                                               state="backlog")
    for parent_task in parent_tasks:
        parent_task.freeze()
        repo_e2e.add(parent_task)
    repo_e2e.commit()

    return parent_tasks
Exemplo n.º 21
0
def insert_tasks_e2e(repo_e2e: Repository) -> List[Task]:
    """Insert many tasks in the end to end repository."""
    tasks = factories.TaskFactory.create_batch(3, priority=3, state="backlog")
    different_task = factories.TaskFactory.create(priority=2, state="backlog")
    tasks.append(different_task)

    for task in tasks:
        repo_e2e.add(task)
        repo_e2e.commit()

    return tasks
Exemplo n.º 22
0
    def test_repository_can_search_by_bool_property(
        self,
        repo: Repository,
    ) -> None:
        """Search should return the objects that have a bool property."""
        expected_entity = BoolEntity(name="Name", active=True)
        repo.add(expected_entity)
        repo.commit()

        result = repo.search({"active": True}, BoolEntity)

        assert result == [expected_entity]
Exemplo n.º 23
0
    def test_print_tags_report(self, runner: CliRunner, repo_e2e: Repository,
                               insert_tasks_e2e: List[Task]) -> None:
        """Test that tags returns the expected output."""
        tasks = insert_tasks_e2e
        tasks[0].tags = ["tag1"]
        repo_e2e.add(tasks[0])
        repo_e2e.commit()

        result = runner.invoke(cli, ["tags"])

        assert result.exit_code == 0
        assert re.search(r" +Name.*Open Tasks", result.output)
Exemplo n.º 24
0
    def test_repository_raises_error_if_get_finds_more_than_one_entity(
            self, repo: Repository) -> None:
        """
        Given: Two entities of different type with the same ID
        When: We get the ID without specifying the model
        Then: a TooManyEntitiesError error is raised
        """
        entities = AuthorFactory.batch(2, name="same name")
        repo.add(entities)
        repo.commit()

        with pytest.raises(TooManyEntitiesError, match=""):
            repo.get("same name", Author, "name")  # act
Exemplo n.º 25
0
    def test_repo_can_search_in_list_of_str_attribute(self, repo: Repository) -> None:
        """
        Given: A repository with an entity that contains an attribute with a list of str
        When: search is called with a regexp that  matches one of the list elements
        Then: the entity is returned
        """
        expected_entity = ListEntityFactory.build()
        repo.add(expected_entity)
        repo.commit()
        regexp = rf"{expected_entity.elements[0][:-1]}."

        result = repo.search({"elements": regexp}, ListEntity)

        assert result == [expected_entity]
Exemplo n.º 26
0
def insert_parent_tasks_e2e(
    repo_e2e: Repository, ) -> Tuple[List[RecurrentTask], List[Task]]:
    """Insert a RecurrentTask and it's children Task in the repository."""
    parent_tasks = factories.RecurrentTaskFactory.create_batch(3,
                                                               state="backlog")
    child_tasks = [
        parent_task.breed_children() for parent_task in parent_tasks
    ]

    [repo_e2e.add(parent_task) for parent_task in parent_tasks]
    [repo_e2e.add(child_task) for child_task in child_tasks]
    repo_e2e.commit()

    return parent_tasks, child_tasks
Exemplo n.º 27
0
def modify_tasks(
    repo: Repository,
    selector: TaskSelector,
    change: TaskChanges,
    modify_parent: bool = False,
    is_recurrent: bool = False,
) -> None:
    """Modify the attributes of the tasks matching a filter."""
    if not is_recurrent:
        selector.model = Task
        task_type = "task"
    else:
        selector.model = RecurrentTask
        task_type = "recurrent task"

    tasks = _tasks_from_selector(repo, selector)

    for task in tasks:
        original_task = task.copy(deep=True)
        for tag in change.tags_to_remove:
            try:
                task.tags.remove(tag)
            except ValueError:
                log.warning(f"Task {task.id_} doesn't have "
                            f"the tag {tag} assigned.")

        for tag in change.tags_to_add:
            task.tags.append(tag)

        for attribute, value in change.task_attributes.items():
            task.__setattr__(attribute, value)

        if task != original_task:
            task.modified = datetime.datetime.now()
            repo.add(task)
            log.info(f"Modified {task_type} {task.id_}.")

        if modify_parent:
            if task.parent_id is not None:
                parent_change = change.copy()
                with suppress(EntityNotFoundError):
                    parent_selector = TaskSelector(task_ids=[task.parent_id])
                    modify_tasks(repo,
                                 parent_selector,
                                 parent_change,
                                 is_recurrent=True)
            else:
                log.warning(f"Task {task.id_} doesn't have a parent task.")
    repo.commit()
Exemplo n.º 28
0
    def test_repository_cant_save_an_entity_with_a_negative_id(
        self, repo: Repository, inserted_int_entity: Entity, merge: bool
    ) -> None:
        """
        Given: A repository with an entity
        When: adding an entity with a negative id
        Then: the id of the new entity is one unit greater than the last one.
        """
        entity = inserted_int_entity.__class__(id=-3, name="Entity with negative id")
        repo.add(entity, merge=merge)

        repo.commit()  # act

        saved_entity = repo.last(type(inserted_int_entity))
        # ignore: we know for sure that the id_ is an int
        assert saved_entity.id_ == inserted_int_entity.id_ + 1  # type: ignore
        assert saved_entity.name == "Entity with negative id"
Exemplo n.º 29
0
    def test_repository_raises_error_if_get_finds_more_than_one_entity(
        self, repo: Repository, inserted_entity: Entity
    ) -> None:
        """
        Given: Two entities of different type with the same ID
        When: We get the ID without specifying the model
        Then: a TooManyEntitiesError error is raised
        """
        other_entity = OtherEntity(id_=inserted_entity.id_, name="Other entity")
        repo.models = [type(inserted_entity), OtherEntity]  # type: ignore
        repo.add(other_entity)
        repo.commit()
        with pytest.warns(
            UserWarning, match="In 2022-06-10.*deprecated"
        ), pytest.raises(TooManyEntitiesError, match=""):

            repo.get(inserted_entity.id_)  # act
Exemplo n.º 30
0
    def test_repository_can_delete_an_entity(
        self,
        repo: Repository,
        inserted_entities: List[Entity],
    ) -> None:
        """
        Given: a full repository.
        When: an entity is deleted.
        Then: the entity is not longer in the repository.
        """
        entity_to_delete = inserted_entities[1]
        repo.delete(entity_to_delete)

        repo.commit()  # act

        remaining_entities = repo.all(type(entity_to_delete))
        assert entity_to_delete not in remaining_entities