Commit 3582366c authored by Jakob Moser's avatar Jakob Moser
Browse files

Merge branch 'change-agenda-status' into 'master'

Allow changing agenda item statuses directly in Portal

See merge request !41
parents 6370989a fe49222f
Loading
Loading
Loading
Loading
Loading

.python-version

0 → 100644
+1 −0
Changes for .python-version: 1 added line, 0 removed lines.
Original line number Diff line number Diff line
3.12
 No newline at end of file
+39 −12
Changes for portal/api/agenda.py: 39 added lines, 12 removed lines.
Original line number Diff line number Diff line
from flask import request

from portal.model import TaigaUserStoryProvider

from .blueprint import bp
from ..model import AgendaItem, Key, TaigaUserStoryReference
from .auth import require_valid
from ..model import Key
from .blueprint import bp


@bp.get("/agendas/-/items")
@require_valid(Key)
def the_agenda_items() -> list[dict[str, str]]:
    tasks = sorted(
def the_agenda_items() -> list[AgendaItem]:
    user_stories = sorted(
        TaigaUserStoryProvider.get_the_one().fetch_user_stories(),
        key=lambda task: task["kanban_order"],
        key=lambda story: story["kanban_order"],
    )

    item_names = [
        "Tagesordnung",
        "Gremienberichte",
        "Posteingang und Kummerkasten",
        *(task["subject"] for task in tasks),
        "Sonstiges",
    return [
        AgendaItem(name="Tagesordnung"),
        AgendaItem(name="Gremienberichte"),
        AgendaItem(name="Posteingang und Kummerkasten"),
        *(
            AgendaItem(
                name=story["subject"],
                taiga=TaigaUserStoryReference(
                    id=story["id"], status=story["status"], version=story["version"]
                ),
            )
            for story in user_stories
        ),
        AgendaItem(name="Sonstiges"),
    ]

    return [{"name": name} for name in item_names]

@bp.patch("/agendas/-/items")
@require_valid(Key)
def patch_the_agenda_items() -> list[AgendaItem]:
    relevant_items = [item for item in request.json if "taiga" in item]

    taiga = TaigaUserStoryProvider.get_the_one()

    # TODO Parallelize this
    for item in relevant_items:
        taiga.update_user_story(
            item["taiga"]["id"],
            item["taiga"]["version"],
            status=item["taiga"]["status"],
        )
        # TODO Add a comment

    return the_agenda_items()
+2 −1
Changes for portal/api/auth.py: 2 added lines, 1 removed line.
Original line number Diff line number Diff line
# Login code adapted from the flaskr tutorial (see LICENSE-3RD-PARTY.md for details)
# See https://flask.palletsprojects.com/en/2.0.x/tutorial/views/
import functools
from typing import Any, Callable
from collections.abc import Callable
from typing import Any

from flask import g, request

+8 −0
Changes for portal/model/AgendaItem.py: 8 added lines, 0 removed lines.
Original line number Diff line number Diff line
from typing import NotRequired, TypedDict

from .TaigaUserStoryReference import TaigaUserStoryReference


class AgendaItem(TypedDict):
    name: str
    taiga: NotRequired[TaigaUserStoryReference]
+16 −6
Changes for portal/model/TaigaUserStoryProvider.py: 16 added lines, 6 removed lines.
Original line number Diff line number Diff line
from sqlalchemy.orm import Mapped
from typing import Any, Self

import requests

from sqlalchemy.orm import Mapped

from portal.model.Base import Base


class TaigaUserStoryProvider(Base):
    """
    Fetches a list of "user stories" (i.e., tasks) from the project management tool Taiga.
    Fetches and changes "user stories" (i.e., tasks) from the project management tool Taiga.
    """

    url: Mapped[str]
    authorization: Mapped[str]

    def fetch_user_stories(self) -> list[dict[str, Any]]:
        return requests.get(
            self.url, headers={"Authorization": self.authorization}
        ).json()
        return requests.get(self.url, headers=self.headers).json()

    def update_user_story(self, id: int, version: int, **kwargs: Any) -> None:
        # TODO Maybe build this in a smarter way
        user_story_url = f"{self.url.split('?')[0]}/{id}"

        # TODO Maybe return something
        requests.patch(
            user_story_url, headers=self.headers, json=kwargs | {"version": version}
        )

    @property
    def headers(self) -> dict[str, str]:
        return {"Authorization": self.authorization}

    @classmethod
    def get_the_one(cls) -> Self:
Loading