From 6120dfc2338465ab6cbca41c5c59c7f16092a4f2 Mon Sep 17 00:00:00 2001 From: OndraRehounek <ondra.rehounek@seznam.cz> Date: Fri, 15 Apr 2022 16:00:38 +0200 Subject: [PATCH] district & region: Unify template for program blocks, finish management command --- README.md | 1 + district/blocks.py | 18 ++++- ..._program_block.html => program_block.html} | 6 +- .../blocks/redmine_program_block.html | 81 ------------------- .../commands/update_redmine_issues.py | 54 ++++++++++--- redmine_utils/models.py | 5 -- redmine_utils/utils.py | 37 ++++++--- region/blocks.py | 17 +++- ..._program_block.html => program_block.html} | 0 .../region/blocks/redmine_program_block.html | 81 ------------------- 10 files changed, 104 insertions(+), 196 deletions(-) rename district/templates/district/blocks/{static_program_block.html => program_block.html} (89%) delete mode 100644 district/templates/district/blocks/redmine_program_block.html delete mode 100644 redmine_utils/models.py rename region/templates/region/blocks/{static_program_block.html => program_block.html} (100%) delete mode 100644 region/templates/region/blocks/redmine_program_block.html diff --git a/README.md b/README.md index d2583839..d055038b 100644 --- a/README.md +++ b/README.md @@ -174,6 +174,7 @@ Přes CRON je třeba na pozadí spouštět Django `manage.py` commandy: * `clearsessions` - maže expirované sessions (denně až týdně) * `publish_scheduled_pages` - publikuje naplánované stránky (každou hodinu) * `update_callendars` - stáhne a aktualizuje kalendáře (několikrát denně) +* `update_redmine_issues` - aktualizuje programované body MS a KS stránek napojených na Redmine (několikrát denně) ### Fulltextové vyhledávání v češtině diff --git a/district/blocks.py b/district/blocks.py index f4849df7..693f061c 100644 --- a/district/blocks.py +++ b/district/blocks.py @@ -223,10 +223,10 @@ class StaticProgramBlock(StructBlock): perex = TextBlock(label="Krátký text pod nadpisem", required=True) person = PageChooserBlock(label="Garant", page_type=["district.DistrictPersonPage"]) completion_percentage = IntegerBlock(label="Procento dokončení", required=True) - program_items = ListBlock(ProgramItemBlock()) + program_items = ListBlock(ProgramItemBlock(), label="Seznam bodů") class Meta: - template = "district/blocks/static_program_block.html" + template = "district/blocks/program_block.html" icon = "list-ul" label = "Blok programu" @@ -236,9 +236,19 @@ class RedmineProgramBlock(StructBlock): perex = TextBlock(label="Krátký text pod nadpisem", required=True) person = PageChooserBlock(label="Garant", page_type=["district.DistrictPersonPage"]) redmine_issue = IntegerBlock(label="Číslo Redmine issue", required=True) - completion_percentage = IntegerBlock(label="Procento dokončení", required=False) + completion_percentage = IntegerBlock( + label="Procento dokončení - bude doplněno automaticky", + required=False, + help_text="Hodnota se automaticky načte s Redmine", + ) + program_items = ListBlock( + ProgramItemBlock(), + label="Seznam bodů - bude doplněno automaticky", + help_text="Hodnota se automaticky načte s Redmine", + required=False, + ) class Meta: - template = "district/blocks/redmine_program_block.html" + template = "district/blocks/program_block.html" icon = "site" label = "Blok programu stahovaný z Redmine" diff --git a/district/templates/district/blocks/static_program_block.html b/district/templates/district/blocks/program_block.html similarity index 89% rename from district/templates/district/blocks/static_program_block.html rename to district/templates/district/blocks/program_block.html index d205bb68..caf0108a 100644 --- a/district/templates/district/blocks/static_program_block.html +++ b/district/templates/district/blocks/program_block.html @@ -20,7 +20,7 @@ <div> <a href="https://redmine.pirati.cz/issues/28177" class="contact-line icon-link content-block--nostyle"> <i class="ico--info"></i> - <span>Plnění programu: {{ self.completion_percentage }}%</span> + <span>Plnění programu: {{ self.completion_percentage | default_if_none:'' }}%</span> </a> </div> </div> @@ -43,10 +43,10 @@ <tr> <td> <a href="{{ item.issue_link }}" target="_blank"> - {{ item.title }} + {{ item.title | default_if_none:'' }} </a> </td> - <td>{{ item.completion_percentage }} %</td> + <td>{{ item.completion_percentage | default_if_none:'' }} %</td> </tr> {% endfor %} </tbody> diff --git a/district/templates/district/blocks/redmine_program_block.html b/district/templates/district/blocks/redmine_program_block.html deleted file mode 100644 index 94240293..00000000 --- a/district/templates/district/blocks/redmine_program_block.html +++ /dev/null @@ -1,81 +0,0 @@ -<article class="mt-8"> - <div class="lg:flex lg:space-x-16"> - <div class="lg:w-3/5"> - <h2 class="head-heavy-sm mb-2 lg:mb-4"> - {{ self.headline }} - </h2> - <div itemprop="description" class="w-full content-block"> - <p> - {{ self.perex }} - </p> - </div> - </div> - <div class="pt-8 lg:w-2/5 md:pt-0"> - <div class="card"> - <div class="card__body"> - {% include "shared/person_badge_snippet.html" with person_page=self.person %} - - <div class="content-block"> - <div class="space-y-4 mt-8"> - <div> - <a href="https://redmine.pirati.cz/issues/{{ self.redmine_issue }}" class="contact-line icon-link content-block--nostyle"> - <i class="ico--info"></i> - <span> - Plnění programu: - <span data-redmine-issue-id="{{ self.redmine_issue }}">10</span>% - </span> - </a> - </div> - </div> - </div> - </div> - </div> - </div> - </div> - - <div class="mt-4"> - <table class="table table--striped table--bordered w-full"> - <thead> - <tr> - <td>Název</td> - <td>Stav plnění</td> - </tr> - </thead> - <tbody data-redmine-table-id="{{ self.redmine_issue }}"> - </tbody> - </table> - </div> -</article> - -<script> - (function () { - const redmineIssueId = '{{ self.redmine_issue }}' - - fetch(`https://redmine.pirati.cz/issues/${redmineIssueId}.json`) - .then(response => response.json()) - .then(data => handleOverallData(data.issue || {})) - - fetch(`https://redmine.pirati.cz/issues.json?parent_id=${redmineIssueId}&sort=id:as`) - .then(response => response.json()) - .then(data => handleIssueList(data.issues || [])) - - function handleOverallData(issue) { - document.querySelector('[data-redmine-issue-id="{{ self.redmine_issue }}"]').innerText = issue.done_ratio || 0 - } - - function handleIssueList(issueList) { - const table = document.querySelector('[data-redmine-table-id="{{ self.redmine_issue }}"]') - for (const issue of issueList) { - table.innerHTML += - `<tr> - <td> - <a href="https://redmine.pirati.cz/issues/${issue.id}" target="_blank"> - ${issue.subject || ''} - </a> - </td> - <td>${issue.done_ratio || 0} %</td> - </tr>` - } - } - })() -</script> diff --git a/redmine_utils/management/commands/update_redmine_issues.py b/redmine_utils/management/commands/update_redmine_issues.py index b42ddb11..cc0ae3a5 100644 --- a/redmine_utils/management/commands/update_redmine_issues.py +++ b/redmine_utils/management/commands/update_redmine_issues.py @@ -1,9 +1,11 @@ from django.core.management.base import BaseCommand +from wagtail.core.blocks import StructValue +from district.blocks import ProgramItemBlock from district.models import DistrictProgramPage from region.models import RegionProgramPage -from ...utils import get_issue_list, get_issue_overall +from ...utils import get_issue_list, get_issue_overall_percentage class Command(BaseCommand): @@ -17,14 +19,48 @@ class Command(BaseCommand): self.stdout.write("Updating Redmine issues...") for model in updated_models: - program_pages = model.objects.all() - for page_content in model.objects.all().values_list("content", flat=True): - for program_block in page_content: + for page in model.objects.all(): + for program_block in page.content: if program_block.block_type == "redmine_program_block": - # block_content_dict = program_block.value - redmine_issue = program_block.value["redmine_issue"] - program_block.value["headline"] = "test" - get_issue_overall(redmine_issue) - get_issue_list(redmine_issue) + fill_data_from_redmine(program_block.value) + + page.save() self.stdout.write("\nUpdating Redmine issues finished") + + +def fill_data_from_redmine(program_block_value: dict): + """ + Naplní hondnotu completion_percentage a program_items pro RedmineProgramBlock + """ + redmine_issue_id = program_block_value["redmine_issue"] + fill_overall_percentage_from_redmine(program_block_value, redmine_issue_id) + fill_program_items_from_redmine(program_block_value, redmine_issue_id) + + +def fill_overall_percentage_from_redmine(program_block_value: dict, issue_id: int): + """ + Naplní hondnotu completion_percentage pro RedmineProgramBlock hodnoutou z Redmine. + """ + program_block_value["completion_percentage"] = get_issue_overall_percentage( + issue_id + ) + + +def fill_program_items_from_redmine(program_block_value: dict, issue_id: int): + """ + Naplní hondnotu program_items pro RedmineProgramBlock. Nejdříve hodnotu (list) + promaže a pak z getných issues naplní pomocí StructValue + """ + issue_list = get_issue_list(issue_id) + program_block_value["program_items"].clear() + + for issue in issue_list: + sv = StructValue(ProgramItemBlock) + sv.update( + { + "completion_percentage": issue.get("done_ratio", None), + "title": issue.get("subject", ""), + } + ) + program_block_value["program_items"].append(sv) diff --git a/redmine_utils/models.py b/redmine_utils/models.py deleted file mode 100644 index d07ff1c1..00000000 --- a/redmine_utils/models.py +++ /dev/null @@ -1,5 +0,0 @@ -from django.db import models - - -class RedmineIssue(models.Model): - pass diff --git a/redmine_utils/utils.py b/redmine_utils/utils.py index e06990d3..4d55d071 100644 --- a/redmine_utils/utils.py +++ b/redmine_utils/utils.py @@ -1,24 +1,41 @@ import json +import logging +from typing import Dict, List import requests +logger = logging.getLogger(__name__) -def get_issue_overall(issue_id): + +def get_issue_overall_percentage(issue_id: int) -> int or None: + """ + Getne stav issue na redmine a vrátí její procento dokončení (nebo None) + """ response = requests.get("https://redmine.pirati.cz/issues/{}.json".format(issue_id)) - print(response.text) + if response.status_code != 200 or not response.text: + logger.error( + "Nepodařilo se stáhnout info o Redmine issue", + extra={"redmine_issue": issue_id}, + ) + return None + + return json.loads(response.text).get("issue", {}).get("done_ratio", None) -def get_issue_list(issue_id): + +def get_issue_list(issue_id: int) -> List[Dict]: + """ + Getne list dceřiných issues a vrátí je jako list dictionaries. + """ response = requests.get( "https://redmine.pirati.cz/issues.json?parent_id={}&sort=id:as".format(issue_id) ) if response.status_code != 200 or not response.text: - # TODO log - return - - data = json.loads(response.text) - - print(response) + logger.error( + "Nepodařilo se stáhnout dílčí issues pro Redmine issue", + extra={"redmine_issue": issue_id}, + ) + return [] - return data["issues"] + return json.loads(response.text).get("issues", []) diff --git a/region/blocks.py b/region/blocks.py index d54020cb..e173f963 100644 --- a/region/blocks.py +++ b/region/blocks.py @@ -223,10 +223,10 @@ class StaticProgramBlock(StructBlock): perex = TextBlock(label="Krátký text pod nadpisem", required=True) person = PageChooserBlock(label="Garant", page_type=["region.RegionPersonPage"]) completion_percentage = IntegerBlock(label="Procento dokončení", required=True) - program_items = ListBlock(ProgramItemBlock()) + program_items = ListBlock(ProgramItemBlock(), label="Seznam bodů") class Meta: - template = "region/blocks/static_program_block.html" + template = "region/blocks/program_block.html" icon = "list-ul" label = "Blok programu" @@ -236,8 +236,19 @@ class RedmineProgramBlock(StructBlock): perex = TextBlock(label="Krátký text pod nadpisem", required=True) person = PageChooserBlock(label="Garant", page_type=["region.RegionPersonPage"]) redmine_issue = IntegerBlock(label="Číslo Redmine issue", required=True) + completion_percentage = IntegerBlock( + label="Procento dokončení - bude doplněno automaticky", + required=False, + help_text="Hodnota se automaticky načte s Redmine", + ) + program_items = ListBlock( + ProgramItemBlock(), + label="Seznam bodů - bude doplněno automaticky", + help_text="Hodnota se automaticky načte s Redmine", + required=False, + ) class Meta: - template = "region/blocks/redmine_program_block.html" + template = "region/blocks/program_block.html" icon = "site" label = "Blok programu stahovaný z Redmine" diff --git a/region/templates/region/blocks/static_program_block.html b/region/templates/region/blocks/program_block.html similarity index 100% rename from region/templates/region/blocks/static_program_block.html rename to region/templates/region/blocks/program_block.html diff --git a/region/templates/region/blocks/redmine_program_block.html b/region/templates/region/blocks/redmine_program_block.html deleted file mode 100644 index 94240293..00000000 --- a/region/templates/region/blocks/redmine_program_block.html +++ /dev/null @@ -1,81 +0,0 @@ -<article class="mt-8"> - <div class="lg:flex lg:space-x-16"> - <div class="lg:w-3/5"> - <h2 class="head-heavy-sm mb-2 lg:mb-4"> - {{ self.headline }} - </h2> - <div itemprop="description" class="w-full content-block"> - <p> - {{ self.perex }} - </p> - </div> - </div> - <div class="pt-8 lg:w-2/5 md:pt-0"> - <div class="card"> - <div class="card__body"> - {% include "shared/person_badge_snippet.html" with person_page=self.person %} - - <div class="content-block"> - <div class="space-y-4 mt-8"> - <div> - <a href="https://redmine.pirati.cz/issues/{{ self.redmine_issue }}" class="contact-line icon-link content-block--nostyle"> - <i class="ico--info"></i> - <span> - Plnění programu: - <span data-redmine-issue-id="{{ self.redmine_issue }}">10</span>% - </span> - </a> - </div> - </div> - </div> - </div> - </div> - </div> - </div> - - <div class="mt-4"> - <table class="table table--striped table--bordered w-full"> - <thead> - <tr> - <td>Název</td> - <td>Stav plnění</td> - </tr> - </thead> - <tbody data-redmine-table-id="{{ self.redmine_issue }}"> - </tbody> - </table> - </div> -</article> - -<script> - (function () { - const redmineIssueId = '{{ self.redmine_issue }}' - - fetch(`https://redmine.pirati.cz/issues/${redmineIssueId}.json`) - .then(response => response.json()) - .then(data => handleOverallData(data.issue || {})) - - fetch(`https://redmine.pirati.cz/issues.json?parent_id=${redmineIssueId}&sort=id:as`) - .then(response => response.json()) - .then(data => handleIssueList(data.issues || [])) - - function handleOverallData(issue) { - document.querySelector('[data-redmine-issue-id="{{ self.redmine_issue }}"]').innerText = issue.done_ratio || 0 - } - - function handleIssueList(issueList) { - const table = document.querySelector('[data-redmine-table-id="{{ self.redmine_issue }}"]') - for (const issue of issueList) { - table.innerHTML += - `<tr> - <td> - <a href="https://redmine.pirati.cz/issues/${issue.id}" target="_blank"> - ${issue.subject || ''} - </a> - </td> - <td>${issue.done_ratio || 0} %</td> - </tr>` - } - } - })() -</script> -- GitLab