추적용 콘솔 앱 작성.

This commit is contained in:
2026-01-05 18:30:33 +09:00
committed by seungmuk.oh
parent 73cfe9c033
commit 9d04de1a20
3 changed files with 187 additions and 4 deletions

24
IssueTracker/IssueRow.cs Normal file
View File

@@ -0,0 +1,24 @@
using CsvHelper.Configuration.Attributes;
namespace IssueTracker;
public class IssueRow
{
[Name("Issue Type")]
public string IssueType { get; set; } = "";
[Name("Issue key")]
public string IssueKey { get; set; } = "";
[Name("Issue id")]
public string IssueId { get; set; } = "";
public string Summary { get; set; } = "";
public string Assignee { get; set; } = "";
public string Manager { get; set; } = "";
public string Status { get; set; } = "";
public string Created { get; set; } = "";
public string Updated { get; set; } = "";
public int WorkingDate { get; set; }
}

View File

@@ -7,4 +7,8 @@
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="CsvHelper" Version="33.1.0" />
</ItemGroup>
</Project>

View File

@@ -1,10 +1,165 @@
namespace IssueTracker
{
using CsvHelper;
using CsvHelper.Configuration;
using System.Globalization;
namespace IssueTracker;
internal class Program
{
static void Main(string[] args)
static int Main(string[] args)
{
Console.WriteLine("Hello, World!");
if (args.Length < 2)
{
Console.WriteLine("Usage: IssueTracker <inputCsv> <outputCsv>");
return -1;
}
var inputCsv = args[0];
var outputCsv = args[1];
if (!File.Exists(inputCsv))
{
Console.WriteLine($"Input CSV not found: {inputCsv}");
return -2;
}
Console.WriteLine("========================================");
Console.WriteLine(" Issue Tracker started");
Console.WriteLine($" Input : {inputCsv}");
Console.WriteLine($" Output: {outputCsv}");
Console.WriteLine("========================================");
var csvConfig = new CsvConfiguration(CultureInfo.InvariantCulture)
{
HasHeaderRecord = true,
IgnoreBlankLines = true,
BadDataFound = null, // 깨진 데이터 무시
MissingFieldFound = null, // 컬럼 누락 무시
HeaderValidated = null, // 헤더 검증 비활성화
PrepareHeaderForMatch = args => args.Header.Trim() // 헤더 공백 제거
};
// 1. 최초 실행: output CSV가 없는 경우
if (!File.Exists(outputCsv))
{
Console.WriteLine("Tracking CSV not found. Creating new file.");
var firstIssues = LoadCsv(inputCsv, csvConfig);
Console.WriteLine($"[Initial] Loaded {firstIssues.Count} issues from input CSV");
SaveCsv(outputCsv, firstIssues, csvConfig);
Console.WriteLine("Tracking CSV created successfully.");
return 0;
}
// 2. 기존 Tracking CSV 로드
var previousIssues = LoadCsv(outputCsv, csvConfig)
.ToDictionary(x => x.IssueKey);
Console.WriteLine($"[Previous] Loaded {previousIssues.Count} issues from tracking CSV");
// 3. 오늘 CSV 로드
var todayIssues = LoadCsv(inputCsv, csvConfig)
.ToDictionary(x => x.IssueKey);
Console.WriteLine($"[Today] Loaded {todayIssues.Count} issues from input CSV");
var newIssues = new List<IssueRow>();
var updatedIssues = new List<IssueRow>();
var retainedIssues = new List<IssueRow>();
int updatedCount = 0;
int retainedCount = 0;
int newCount = 0;
// 4. 기존 모든 이슈 처리 (입력에 없어도 유지)
foreach (var (key, old) in previousIssues)
{
if (todayIssues.TryGetValue(key, out var issue))
{
// 입력에 존재하는 이슈 -> 업데이트
if (string.Equals(old.Status, issue.Status, StringComparison.OrdinalIgnoreCase))
{
// 상태 유지
issue.WorkingDate = old.WorkingDate + 1;
}
else
{
// 상태 변경
if (string.Equals(issue.Status, "Resolved", StringComparison.OrdinalIgnoreCase))
{
// Resolved -> 값 유지 + 추적 중지
issue.WorkingDate = old.WorkingDate;
}
else
{
// 다른 상태 변경 -> 초기화
issue.WorkingDate = 0;
}
}
updatedIssues.Add(issue);
updatedCount++;
}
else
{
// 입력에 없는 이슈 -> 기존 데이터 그대로 유지 (아래쪽 배치)
retainedIssues.Add(old);
retainedCount++;
//Console.WriteLine($"[Retained] Issue {key} not in input CSV, keeping in output");
}
}
// 5. 입력에는 있지만 기존 추적에 없던 신규 이슈 추가 (위쪽 배치)
foreach (var (key, issue) in todayIssues)
{
if (!previousIssues.ContainsKey(key))
{
// 신규 Issue
issue.WorkingDate = 0;
newIssues.Add(issue);
newCount++;
Console.WriteLine($"[New] Issue {key} added to tracking");
}
}
Console.WriteLine("----------------------------------------");
Console.WriteLine($"Updated: {updatedCount}, Retained: {retainedCount}, New: {newCount}");
Console.WriteLine("----------------------------------------");
// 6. 정렬: 신규 -> 업데이트 -> 추적 중단
var result = new List<IssueRow>();
result.AddRange(newIssues);
result.AddRange(updatedIssues);
result.AddRange(retainedIssues);
Console.WriteLine($"Total output rows: {result.Count}");
Console.WriteLine("----------------------------------------");
// 7. 결과 저장
SaveCsv(outputCsv, result, csvConfig);
Console.WriteLine("Tracking CSV updated successfully.");
return 0;
}
private static List<IssueRow> LoadCsv(string path, CsvConfiguration config)
{
using var reader = new StreamReader(path);
using var csv = new CsvReader(reader, config);
return csv.GetRecords<IssueRow>().ToList();
}
private static void SaveCsv(
string path,
List<IssueRow> data,
CsvConfiguration config)
{
using var writer = new StreamWriter(path, false, System.Text.Encoding.UTF8);
using var csv = new CsvWriter(writer, config);
csv.WriteHeader<IssueRow>();
csv.NextRecord();
csv.WriteRecords(data);
}
}