Beeinflussen Sie die Statistik der Inline-Kommentare

Wie sammle ich Seitenkommentarstatistiken in Confluence?

Ja, und warum könnte das nötig sein?



Warum und warum


In dem Projekt, in dem ich gerade gearbeitet habe, ist der folgende Prozess der Vorbereitung und Genehmigung von Anforderungen entstanden:

  • Confluence wurde für die Dokumentation verwendet.
  • Das Lieferantenteam erstellte Beschreibungen der Geschäftsprozesse und ihrer Schritte in Form von separaten Seiten entsprechend der Vorlage.
  • Einmal pro Woche wurde dem Kunden eine Reihe vorbereiteter Beschreibungen zum Korrekturlesen übergeben.
  • Das Kundenteam hinterließ alle Fragen und Ergänzungen in Form von eingebauten Kommentaren auf der entsprechenden Seite.
  • Das Lieferantenteam ergänzte den Inhalt, beantwortete Kommentare und stellte Fragen für zusätzliche Studien.
  • Wenn die Frage beantwortet und der Inhalt aktualisiert oder die Aufgabe für das Studium festgelegt wurde, sollte das Kundenteam den Kommentar schließen.
  • Fragen zu einem Stapel von Dokumenten aus der aktuellen Woche sollten geschlossen sein, bis der nächste Stapel nächste Woche übertragen wird.

Projektmanager haben jede Woche die Aufgabe zu verstehen, wie viel der gelieferte Stapel von Dokumenten funktioniert hat und was davon als bedingt bereit angesehen werden kann. Und die Teilnehmer des Lieferantenteams müssen regelmäßig den Status der Kommentare zu ihren Dokumenten überprüfen und mit den anderen zielgerichtet zusammenarbeiten. Aber wie findet man sie? Sie können jede Seite öffnen, den ersten Kommentar mit Ihren Augen suchen (oder mit Hilfe eines kleinen Tricks ) und dann auf alle Kommentare klicken, weil Das Kundenteam hat es nicht eilig, sie zu schließen, über sie nachzudenken und nach einer Antwort zu suchen.

Der wöchentliche Stapel enthält 50-100 separate Seiten, und dies mit Ihren Händen zu tun, ist ein erheblicher Arbeitsaufwand. Und wenn Sie immer noch versuchen, Argumente zu sammeln, um die andere Seite zu überzeugen, wird es sehr traurig. Und es gibt auch baumelnde Kommentare, die sich aus einer fehlerhaften Bearbeitung der Seite ergeben, wenn der Quelltext versehentlich gelöscht wird. Ein solcher Kommentar ist in den aufgelösten sichtbar, kann jedoch nicht wieder geöffnet werden (dies ist möglich, wenn Sie eine unsichtbare Markierung im Text der Seite neu erstellen).

Die Suche nach Tools war nicht erfolgreich. Darüber hinaus wird Confluence auf Kundenseite bereitgestellt. Sie können keine Plugins installieren, ganz zu schweigen vom Kauf. Keine Makroentwicklungsexpertise.

Irgendwann erinnerte ich mich an die Confluence- REST-API und an frühere Erfahrungen mit der Verwendung der ähnlichen Jira-API . Suche und Experimente mit Aufruffunktionen aus dem Browser haben gezeigt, dass Sie zu den Kommentaren und deren Eigenschaften gelangen können. Als Nächstes mussten Sie ein Tool für die Automatisierung auswählen und können mit der Lösung beginnen. Ich habe einige Erfahrungen mit der Erstellung von Skripten für Tools, die eher mit Administratoren wie Bash, Perl oder JScript zu tun haben. Ich bin kein Entwickler, ich hatte keine vertrauten oder vertrauten Werkzeuge. Ich wollte etwas üblicheres oder passenderes ausprobieren. Dann entdeckte ich einen Wrapper für die Python-API und beschloss, es damit zu versuchen.

Allgemeines Prinzip


Das Problem wurde wie folgt formuliert. Sie müssen alle Seiten finden, die sich auf eine bestimmte wöchentliche Lieferung beziehen. Sammeln Sie Kommentare dazu in einer Liste: Seite, Link zum Kommentar, Autor und Datum des Kommentars, Quelltext auf der Seite, Kommentar und Antworten, Autor und Datum der letzten Antwort, Status des Kommentars. Sammeln Sie außerdem Statistiken für jede Seite, wie viele Kommentare insgesamt, wie viele Gefahren, wie viele geöffnet sind. Speichern Sie alles auf einer speziellen Statistikseite.

Ich setze Python ein, schaue mir die Grundlagen der Arbeit damit an und lass uns gehen. Erstellen Sie zunächst eine Verbindung:

from atlassian import Confluence UserLogin = 'xxxxxx' # input("Login: ") UserPwd = 'xxxxxx' # input("Password: ") confluenceURL = 'http://wiki.xxxxxx' confluence = Confluence( url=confluenceURL, username=UserLogin, password=UserPwd) 

Um nach Seiten aus einem wöchentlichen Stapel zu suchen, habe ich mich für die Verwendung von Tags entschieden. Wie man sie in großen Mengen liefert, ist eine separate Aufgabe.

 page_label = 'week123' cql = 'space.key={} and label = "{}" and type = page ' 'ORDER BY title '.format('YYY', page_label) pages = confluence.cql(cql, expand=None, start=0, limit=200) 

Also haben wir eine Liste von Seiten, die wir überprüfen müssen. Als nächstes bearbeiten wir jede einzelne Seite. Wir erheben aus ihren Daten Kommentare mit ihren Parametern. Basierend auf diesen Daten erstellen wir Statistiken darüber, wie viele Kommentare auf der Seite vorhanden sind und unter welchen Bedingungen. Als nächstes knocken wir das Ergebnis für alle Seiten aus und beginnen, das Ergebnis zu formatieren. Wir erstellen den Hauptteil der Seite mit dem Ergebnis in Form einer Statistiktabelle und einer detaillierten Liste offener Kommentare.

Seitenlistenverarbeitung
 statistics = [] open_comments = [] #  - ? if pages is not None: #    ? if pages['size'] > 0: for page in pages['results']: print(page['title']) #       , #  page_comments = page_comments_data(page['content']['id']) #     statistics.append(page_statistics(page_comments)) #        #    . for comment in page_comments: if comment['Result'] not in ['resolved', 'nocomment']: if not (comment['Result'] == 'dangling' and comment['Author'] in excludeNames): open_comments.append(comment) #       . statistics.append(total_statistics(statistics)) #     page_id = confluence.get_page_id(space='YYY', title=page_title) #     ,    . page_body = ('<p><ac:structured-macro ac:name="toc" ac:schema-version="1"' '/></p>' '<h1>Comments Statistics</h1>{}' '<h1>Open Comments List</h1>{}' ).format(create_table(statistics), create_table(open_comments)) #  if page_id is not None: status = confluence.update_page( page_id=page_id, title=page_title, body=page_body, representation='storage' ) 

API-Funktionen


Lassen Sie uns nun herausfinden, wie die Seite verarbeitet wird. Standardmäßig gibt die API nur grundlegende Informationen zurück, z. B. einen Bezeichner oder einen Seitennamen. Alle zusätzlichen Eigenschaften müssen explizit angegeben werden. Sie können angezeigt werden, indem Sie das Ergebnis des Anrufs analysieren. Zusätzliche Daten finden Sie in Abschnitten oder Unterabschnitten von _expandable. Wir fügen den gewünschten Artikel zum Erweitern hinzu und schauen weiter, bis wir die erforderlichen Daten finden.

Issue-Beispiel
 http://wiki.xxxxxx/rest/api/content/101743895?expand=body,children.comment { "id": "97517865", "type": "page", "status": "current", "title": "w2019-47 comments status", "children": { "comment": { "results": [], "start": 0, "limit": 25, "size": 25, "_links": {} }, "_links": {}, "_expandable": { "attachment": "/rest/api/content/97517865/child/attachment", "page": "/rest/api/content/97517865/child/page" } }, "body": { "_expandable": { "editor": "", "view": "", "export_view": "", "styled_view": "", "storage": "", "anonymous_export_view": "" } }, "extensions": { "position": "none" }, "_links": {}, "_expandable": { "metadata": "", "operations": "", "restrictions": "/rest/api/content/97517865/restriction/byOperation", "history": "/rest/api/content/97517865/history", "ancestors": "", "version": "", "descendants": "/rest/api/content/97517865/descendant", } } 

Und es gibt auch eine Beschränkung der Anzahl der ausgegebenen Ergebnisse, die Paginierung. Es ist serverseitig konfiguriert (API?) Und in unserem Fall ist es 25. Bei einigen Anforderungen kann es durch explizite Angabe geändert werden, funktioniert jedoch nur für die oberste Ebene. Und wenn wir Kommentare zu der Seite veröffentlichen, bekommen wir trotzdem nur 25, während die Größe auch falsch ist. Im Beispiel waren es 29 in der Realität. Wir haben es geschafft, die Paginierung von Kommentaren mithilfe einer separaten Funktion im Confluence-Modul zu umgehen - get_page_comments mit der Möglichkeit, die Seitengröße anzugeben.

 #    import re #      XML def replace_chars2(in_text): text = re.sub(r'&', '&', in_text) text = re.sub(r'\'', ''', text) text = re.sub(r'<', '<', text) text = re.sub(r'>', '>', text) text = re.sub(r'"', '"', text) return text 

Die nächste Gefahr lag in der Erhaltung und Herausgabe von Sonderzeichen. Der Hauptteil einer Seite oder eines Kommentars kann in mehreren Ansichten abgerufen werden: Interner XML-Speicher, Zwischen-HTML ohne Makroausgabe-Ansicht und HTML mit Makroausgabe-export_view. Der Titel des Seitentitels und der ursprünglich kommentierte Text originalSelection werden jedoch immer in einer zum Lesen geeigneten Form ausgegeben. Weil Zukünftig werden diese Daten mit Statistiken in den Hauptteil der Seite geschrieben, und einige Zeichen führen zu Konvertierungsfehlern. Ich musste ein Ersatzverfahren oben schreiben.

Seitenkommentare


Nun zum Parsen der Seite. Dies ist eine Prozedur, die eine Seite lädt, ihre Daten sammelt, dann eine Liste von Kommentaren mit Antworten herausholt und sie in Korrespondenz sammelt. Das Ergebnis ist eine Liste von Wörterbüchern, wobei jedes Element der Liste einem Kommentar mit Antworten entspricht. Und alle Attribute dieses Kommentars liegen in den entsprechenden Feldern des Wörterbuchs.

Einzelseitenverarbeitung
 def page_comments_data(page_identifier): #,      ,  , #   , #  ,    ,    #   (). expand_text = ('body.storage,extensions.inlineProperties' ',extensions.resolution,version,children.comment' ',children.comment.version,children.comment.body.storage' ) #    .     . conf_page = confluence.get_page_by_id(page_identifier, expand='body.storage') page_title = replace_chars2(conf_page['title']) #   . link_base = conf_page['_links']['base'] page_link = link_base + conf_page['_links']['webui'] page_code = '<a href="{}">{}</a>'.format(page_link, page_title) #   page_comments = confluence.get_page_comments(content_id=page_identifier, start=0, limit=1000, comments = [] #    for comment in page_comments['results']: #      - . if comment['extensions']['location'] == 'footer': continue #  comment_text = comment['body']['storage']['value'] comment_result = comment['extensions']['resolution']['status'] comment_link = '<a href="{}">{}</a>'\ .format(link_base + comment['_links']['webui'], 'link') #  comment_link = re.sub(r'&focusedCommentId=', '&focusedCommentId=', comment_link) #  created_when = re.sub(r'\.000\+', ' GMT+', re.sub(r'T', ' ', comment['version']['when'])) created_by = comment['version']['by']['displayName'] orig_text = replace_chars2(comment['extensions'] ['inlineProperties']['originalSelection']) #    /. thread = '<b>To text: </b>{}<br/><b>At: </b>{}<br/><b>By: ' '</b>{}<br/>{}'.format(orig_text, created_when, created_by, comment_text) last_by = '' last_when = '' #  answers = comment['children']['comment']['size'] if answers > 0: for message in comment['children']['comment']['results']: #  last_when = re.sub(r'\.000\+', ' GMT+', re.sub(r'T', ' ', message['version']['when'])) last_by = message['version']['by']['displayName'] #  thread += ('<br/>===next===<br/><b>At: </b>{}<br/><b>By: ' '</b>{}<br/>{}'.format(last_when, last_by, message['body']['storage']['value']) ) #  . row_comm = {"Page": page_code, "Comment": comment_link, "Thread": thread, "Result": comment_result, "Answers count": answers, "Creation Date": created_when, "Author": created_by, "Last Date": last_when, "Last Author": last_by} comments.append(row_comm) #   ,   , #     . if len(comments) == 0: row_comm = {"Page": page_code, "Comment": 'nolink', "Thread": 'nocomment', "Result": 'nocomment', "Answers count": 0, "Creation Date": 'never', "Author": 'nobody', "Last Date": 'never', "Last Author": 'nobody'} comments.append(row_comm) return comments 

Statistiken und Tabellen


Zur Klarheit der Datenpräsentation sammeln wir Statistiken über diese und ordnen sie in Form von Tabellen an.

Wir verarbeiten Statistiken
 def page_statistics(comments_data): open_count = 0 dang_count = 0 comment_count = len(comments_data) if comment_count > 0: for comment in comments_data: #     if comment['Result'] not in ['resolved', 'nocomment']: if comment['Result'] in ['open', 'reopened']: open_count += 1 if comment['Result'] == 'dangling' and comment['Author'] not in excludeNames: dang_count += 1 res_dict = {'Page': comments_data[0]['Page'], 'Total': comment_count, 'Resolved': comment_count - open_count - dang_count, 'Dangling': dang_count, 'Open': open_count} return res_dict #   def total_statistics(stat_data): total_comment = 0 total_resolved = 0 total_open = 0 total_dangling = 0 for statRow in stat_data: total_comment += statRow['Total'] total_resolved += statRow['Resolved'] total_open += statRow['Open'] total_dangling += statRow['Dangling'] res_dict = {'Page': 'All Pages Total', 'Type': '', 'Jira': '', 'Status': '', 'Total': total_comment, 'Resolved': total_resolved, 'Dangling': total_dangling, 'Open': total_open} return res_dict 

Um die Daten in Form von Tabellen zu erstellen, führen wir eine weitere Prozedur aus. Es bildet den HTML-Code der Tabelle aus der Liste der Wörterbücher, fügt als Überschriften eine Zeile aus den Namen der Wörterbuchtasten und eine Spalte mit Zeilennummern hinzu.

 def create_table(tab_data): tab_start = '<table style="width: 100.00%;"><colgroup><col />' '<col /></colgroup><tbody>' tab_end = '</tbody></table>' tab_code = tab_start + '<tr>' row_num = 1 if len(tab_data) > 0: tab_code += '<th>Num</th>' for key in tab_data[0].keys(): tab_code += '<th>{}</th>'.format(key) tab_code += '</tr>' for row in tab_data: tab_code += '<tr><td>{}</td>'.format(row_num) row_num += 1 for field in row.values(): tab_code += '<td>{}</td>'.format(field) tab_code += '</tr>' tab_code += tab_end + '\n' return tab_code 

Jetzt ist alles fertig. Nach dem Beschriften und Ausführen des Skripts erhalten wir eine Seite, die ungefähr so ​​aussieht:



PS

Das ist natürlich nicht alles, was sich am Ende herausgestellt hat. Es wurde ein Seiten-Parsing durchgeführt und nach einem Makro mit der Aufgabennummer in Jira gesucht. In Jira gab es eine automatische Kennzeichnung nach Aufgabennummern und Verknüpfungen von diesen zu Confluence. Es wurde ein Vergleich und eine Überprüfung der wöchentlichen Lieferlisten durchgeführt. In Excel wurden Kommentare gespeichert und gemeinsame Daten aus mehreren wöchentlichen Excel-Dateien gesammelt. Und kürzlich wurde das Parsen von Kommentaren aus Word hinzugefügt.

Source: https://habr.com/ru/post/de485652/


All Articles