728x90

Confluence에서 페이지를 업데이트할 때 두 가지 옵션이 있습니다. 그냥 [업데이트]와 [업데이트 설정 조정을 통해 버전 댓글을 남겨 업데이트]가 있습니다.
 


버전 댓글을 남기게 된다면 Git에서 Commit 같이 어떤 내용이 변경되었는지 버전 별로 간단하게 메시지를 남길 수 있어 필요한 기능이라고 생각이 되는데 이를 강제할 수 있는 방법은 현재 없는 것으로 보입니다. 



그래서 사용자가 버전 댓글을 남기지 않고 그냥 업데이트 했을 때 관리자에게 메일을 통해 알려서 버전 댓글을 달 수 있게 하는 방법에 대해 소개하려고 합니다.
 
먼저 gmail의 smtp를 이용하기 위해서 Google [앱 비밀번호]를 먼저 발급받아야 합니다. 


발급된 [기기용 앱 비밀번호]를 따로 복사 해둡니다.



[ScriptRunner] > [Script Listeners]에서 [Add Listener]를 눌러줍니다. Called에 간단히 제목이나 설명을 추가하고 [On these events]에 우리는 버전 댓글 없이 업데이트 됐을 때니 [Page updated]를 선택합니다.


그리고 Code to Run에 아래 코드를 입력 후 [Save] 합니다.

import javax.mail.internet.*
import javax.mail.*

def pageID = page["id"]             // 현재 업데이트 된 페이지 ID
def curVerID = page["version"]      // 업데이트 된 페이지 현재 버전

def res = get("/wiki/api/v2/pages/${pageID}/versions").asObject(Map).body
def value = get("/wiki/api/v2/pages/${pageID}?expand=body.storage").asObject(Map).body

def lastVersionRes = res["results"].find { ver ->
    ver["number"] == curVerID
} // 마지막으로 update 된 버전의 정보

if ( lastVersionRes["message"] == "" ) {

    Properties prop = new Properties()
    //Enter the details of your SMTP server
    prop.put("mail.smtp.auth", true)
    prop.put("mail.smtp.host", "smtp.gmail.com")
    prop.put("mail.smtp.port", "587")
    prop.put("mail.smtp.starttls.enable", "true")

    String emailId = "Gmail ID"
    String password = "App Password"

    Session session = Session.getInstance(prop, new Authenticator() {
        @Override
        protected PasswordAuthentication getPasswordAuthentication() {
            new PasswordAuthentication(emailId, password)
        }
    })

    MimeMessage msg = new MimeMessage(session)
    msg.setFrom(new InternetAddress(emailId, "Confluence Alert"))
    msg.setSubject("[Confluence] 페이지 업데이트 버전 댓글이 제대로 기록되지 않았습니다.", "UTF-8")
	
    String htmlBody = """
    <html>
        <body>
            <p><strong> 업데이트 한 ${page["title"]}에 버전 댓글이 입력되지 않았습니다.</strong></p>
            <p><strong> Url = <a href="${page["self"]}">${page["self"]}</a></strong></p>
        </body>
    </html>
    """   
    msg.setContent(htmlBody, "text/html; charset=UTF-8")
    msg.setRecipients(Message.RecipientType.TO, InternetAddress.parse("Admin Email", false))

    Transport.send(msg)
    logger.info("Email Sent Successfully!!")
} // 버전 정보가 비어져 있으면 버전 메시지를 추가해서 다시 업데이트


이제 Listener를 등록해 놨으니 버전 댓글 없이 업데이트 시 아래처럼 메일이 오게 됩니다.



그리고 만약에 버전 댓글을 기록하지 않은 버전을 지우고 싶다면 먼저 버전 댓글이 입력되지 않은 상태에서 업데이트 버튼을 누른 후 글의 내용은 일절 수정하지 않고 버전 기록만 남긴 후 업데이트 합니다.

 
다음으로 [버전 이력]으로 들어가 버전 이력을 남기지 않은 버전을 삭제합니다.(관리자 계정으로만 가능)


 
그렇게 되면 버전 이력이 추가된 버전과 그 이전 버전만 남게 되게 됩니다.


 

728x90
728x90

이전 ScriptRunner 게시글에서 사용했던것처럼 ScriptRunner를 이용하여 Jira의 상태를 자동으로 변경하고 버전 정보를 넣는 간단한 코드입니다.

 

ScriptRunner > Script Console에서 사용하였습니다.

// 이슈 상태 바꾸고 version 픽스하기
def trans = '이슈 다시 열기'     // 이슈 닫기 or 이슈 다시 열기
def type = 'issue'

Issues.search("type = " + type).each { issue->

    if ( issue.getTransitions()['name'].contains(trans) ) {
        logger.warn('Issue = ' + issue.getKey())
        issue.transition(trans)

    }

    if ( issue.getStatus()['name'] == '다시 열림' ) {
        issue.update { 
            setFixVersions {
                add('ver2')
            }
        }
    }
}
728x90
728x90

Confluence 페이지에 특정 상태를 지정해주는 방법입니다.
 
[스페이스 설정]으로 들어간 후 [콘텐츠 상태]로 들어갑니다. 
여기서 콘텐츠 상태를 활성화 하고 상태를 지정해줍니다.(최대 5개)
 


상태를 지정한 후에는 페이지 편집 화면에서 제목 영역부분으로 커서를 올린 후 [상태]를 통해 지정을 해줄 수 있습니다.
 


여기서 만약에 페이지를 만든 후 자동으로 상태를 지정하게 하고 싶다면 [자동화]를 통해 작업이 가능합니다.
 
[자동화] > [규칙 만들기]를 통해 규칙을 생성합니다.
 
여기서 페이지 게시할 때 만약 페이지 상태가 지정되어 있지 않다면 자동으로 기본 상태를 지정하게 됩니다.


 

728x90
728x90

Confluence에 기본적으로 매크로들이 많이 제공되지만 ScriptRunner라는 App을 사용하면 REST API를 통해 Jira의 정보들을 사용할 수 있습니다.
 
Confluence에서 [앱] > [새 앱 찾기]를 들어가 [ScriptRunner for Confluence]를 설치합니다. 
 
설치가 완료됐으면 [Get Started] 옆에 있는 점 세개 버튼을 눌러 [Configure]로 들어갑니다.


초기 화면으로 들어오면 여러 탭이 있지만 일단 여기서는 [Script Console]과 [Macros]를 사용해보려고 합니다.
 
[Script Console]을 통해 Jira의 정보를 가져올 REST API 호출문을 먼저 실행해볼 수 있고 원하는 결과가 나왔으면 이제 그 코드로 [Macro]로 만들어줄 예정입니다.
 
[Script Console]에서 아래 코드 입력 화면에 먼저 특정 이슈에 대한 정보를 가져올 수 있는 코드를 실행해보겠습니다.
여기서 우리가 필요한것은 Jira의 API Token인데 파싱할 Jira 페이지로 이동합니다.
 
Jira 프로필 사진을 누른 후 [계정 관리]로 들어갑니다.
 
상단 메뉴에서 [보안]으로 이동한 후 하단 [API 토큰]에서 [API 토큰 만들기 및 관리]로 들어갑니다.
 


[API 토큰 만들기]를 눌러 만들기 후 생성된 토큰 값을 복사해줍니다.
 


이제 다시 [ScriptRunner for Confluence]의 [Script Console]로 넘어와서 아래 코드 입력 창에 아래와 같이 입력합니다.

def issue = get("https://[도메인].atlassian.net/rest/api/3/issue/[Issue Key]")
            .basicAuth("[아이디]", "[API Token]")
            .header("Accept", "application/json")
            .asObject(Map).body

return issue.key

이렇게 코드를 각자에 맞게 넣어주신 후 [Run]을 누르면 하단에 해당 Issue의 Key가 출력되게 됩니다.
 
이를 이용해서 매크로를 만들어주려 합니다. [Macros] 탭으로 이동합니다. [Create Custom Macro]를 눌러줍니다.


매크로 이름과 설명을 간단히 작성 후 아래 [parameters]로 이동하여 [Add Parameter]를 선택합니다. 그리고 파라미터로 받을 issueID를 생성 후 [Add]를 눌러줍니다.
 


이제 우리는 아까 [Issue Key] 부분을 우리가 전달한 parameter로 대체하게 되는데 [Script to execute]에 아래 코드를 넣어줍니다.

def issue = get("https://[도메인].atlassian.net/rest/api/3/issue/${parameters.issueID}")
            .basicAuth("[아이디]", "[API Token]")
            .header("Accept", "application/json")
            .asObject(Map).body

return issue.key

이제 [Save]로 매크로를 저장해주고 Confluence Page를 하나 생성한 후 "/만든 매크로 이름"을 선택 하고 설정한 파라미터에 원하는 키를 입력하면 됩니다.
 


 
 

728x90
728x90

enum 값을 사용할 때 for 문으로 enum 값을 증가시키면서 사용하는 방식을 사용할 수도 있습니다. 이럴 때 enum을 정의한 후 바로 증가 연산자를 사용할 수 없고 operator++를 재정의 해주어야 합니다.

enum fruit {
	frApple,
    frOrange,
    
    num_of_fruit
};

fruit& operator++(fruit& fr) {
    fr = static_cast<fruit>(static_cast<int>(fr) + 1);
    return fr;
}

fruit operator++(fruit& fr, int) {
    fruit temp = fr;
    fr = static_cast<fruit>(static_cast<int>(fr) + 1);
    if (fr > num_of_fruit) {
        fr = num_of_fruit;  
    }
    return temp;
}

첫 번째 operator++는 전위 증가 연산자를 위한 재정의, 두 번째 operator++는 후위 증가 연산자를 위한 재정의 함수입니다.

 

728x90

'Programming > C++' 카테고리의 다른 글

[C++] boost windows 비동기 처리하기  (2) 2023.10.23
[C++] Template(2)  (0) 2023.09.22
[C++] Template(1)  (2) 2023.09.19
[C++] std::forward_list, std::list  (0) 2023.07.18
[C++] std::array, std::vector  (0) 2023.07.12
728x90

보통 QTreeWidget에서 아이템을 싹 비우려고 할 때 Clear() 함수를 많이 사용합니다. 하지만 이 함수의 경우 QTreeWidget에 가지고 있는 아이템들의 메모리를 해제시키기 때문에 아래와 같이 아이템을 다시 불러오려고 할 때 문제가 생기게 됩니다.

class Test {
    Test(uchar nItemCount) {
    	for ( int i=0; i < nItemCount; i++ ) {
    		QTreeWidgetItem* it = new QTreeWidgetItem;
        	items.append(it);
        }
    }
    QList<QTreeWidgetItem*> getItems() { return Items; }
    
	QList<QTreeWidgetItem*> Items;
};

int main() {
    Test* t1 = new Test(3);
    Test* t2 = new Test(10);
    
    auto items = t1->getItems();
    for ( auto i : items ) {
    	ui->tree->addTopLevelCount(i);
    }
    
    ui->tree->clear();		// t1이 갖고 있는 item들 delete 됨
    
    auto items = t2->getItems();
    for ( auto i : items ) {
    	ui->tree->addTopLevelCount(i);
    }
    
    ui->tree->clear();		// t2이 갖고 있는 item들 delete 됨
    
    auto items = t1->getItems();
    for ( auto i : items ) {
    	ui->tree->addTopLevelCount(i);		// i는 쓰레기 값
    }
    
    ...
}

이럴때는 clear를 사용하는 것이 아닌 takeTopLevelItem을 통해 단순히 트리에서 제거해주면 됩니다.

while (topLevelItemCount() > 0) {
    takeTopLevelItem(0);
} // clear but not delete items
728x90
728x90

엑셀을 잘 사용하다가 갑자기 아래 사진과 같이 파란색 굵은 점선이 보이는 경우가 있을 수 있습니다.


이럴때 [보기] > [페이지 나누기 미리 보기]를 [기본]으로 선택하면 파란 선은 나오지 않습니다.



하지만 이렇게 해도 점선은 보이게 되는데 이 영역은 결국 페이지 미리보기 즉 인쇄됐을 때 한 페이지 당 출력되는 내용을 미리 볼 수 있는 건데 이렇게 작은 단위로 잘게 쪼개져 있다면 용지 크기를 한번 확인해 볼 필요가 있습니다.
 
확인해보니 A4와 같은 일반적인 페이지 크기가 아닌 다른 유형이 있는 것을 볼 수 있습니다.



이 경우는 라벨 프린터기나 다른 프린터 제품을 사용할 때 볼 수 있는데 이럴때는 [파일] > [인쇄]를 클릭해서 프린터를 변경해보시면 됩니다.


 
변경이 완료됐다면 [보기] > [페이지 나누기 미리 보기]를 선택해도 정상적으로 보이게 됩니다.

 

728x90
728x90

예전 포스팅에서 소개했던 QtConcurrent를 사용하면 간단하고 Qt Event에 특화된 비동기 처리가 가능합니다. 이 QtConcorruent에서 제공하는 map-reduce 패턴을 사용할 수 있습니다.

 

map-reduce 패턴을 간단히 설명하자면 수행할 작업을 분산하여 처리하고 하나로 모은다고 생각하시면 됩니다.

QtConcurrent::mappedReuced 함수 원형입니다.

template <typename Sequence, typename MapFunction, typename ReduceFunction>
QFuture<typename QtPrivate::MapResultType<MapFunction, Sequence>::ResultType>
mappedReduced(const Sequence &sequence, MapFunction mapFunction, ReduceFunction reduceFunction, QtConcurrent::ReduceOptions options = QtConcurrent::SequentialReduce);

 

전달되는 파라미터에 대한 설명입니다.

  • sequence : 처리할 데이터 셋(QList, QVector)
  • mapFunction : 데이터가 처리될 함수 전달
  • reduceFunction : 각각 분산되어 처리된 데이터를 하나로 모으는 함수 전달

반환 값은 QFuture 데이터가 반환되며 비동기 상태를 관리하는 객체입니다. waitForResult 함수로 처리가 끝날때까지 대기가 가능하며 result 함수로 처리 결과를 받을 수 있습니다.

 

아래 코드는 QtConcurrent::mappedReduced를 사용하는 간단한 예제입니다. 

먼저 .pro에 concurrent 모듈을 추가합니다.

QT = core concurrent

전달되는 Function을 람다로 처리하였습니다.

#include <QtConcurrent>
#include <QFuture>
#include <QList>
#include <iostream>


int main() {
    QList<int> data;
    for ( int i=0; i<= 100; i++ ) {
        data.append(i);
    }

    auto mapFunction = [&] ( int value ) -> int {
        qDebug() << "Map : " << "value = " << value;
        return value * 2;
    };

    auto reduceFunction = [&] ( int &result, int value ) {
        result += value;
        qDebug() << "Reduce : " << "Origin Value = " << value / 2 << "Value = " << value << " Result = " << result;
    };

    // mappedReduced 호출
    QFuture<int> future = QtConcurrent::mappedReduced(data, mapFunction, reduceFunction, QtConcurrent::SequentialReduce);

    // 작업 완료 대기 및 결과 출력
    future.waitForFinished();
    int result = future.result();
    std::cout << "Result: " << result << std::endl; // 출력 예시: 30

    return 0;
}
// 실행 결과
Map :  value =  0
Map :  value =  1
Map :  value =  2
Map :  value =  3
Map :  value =  4
Map :  value =  5
Map :  value =  7
Map :  value =  6
Map :  value =  8
Map :  value =  10
Map :  value =  9
/* 중략 */
Reduce :  Origin Value =  91 Value =  182  Result =  8372
Reduce :  Origin Value =  92 Value =  184  Result =  8556
Reduce :  Origin Value =  93 Value =  186  Result =  8742
Reduce :  Origin Value =  94 Value =  188  Result =  8930
Reduce :  Origin Value =  95 Value =  190  Result =  9120
Reduce :  Origin Value =  96 Value =  192  Result =  9312
Reduce :  Origin Value =  97 Value =  194  Result =  9506
Reduce :  Origin Value =  98 Value =  196  Result =  9702
Reduce :  Origin Value =  99 Value =  198  Result =  9900
Reduce :  Origin Value =  100 Value =  200  Result =  10100
Result: 10100

 

728x90

+ Recent posts

Buy Me A Coffee