추적용 콘솔 앱 작성.
This commit is contained in:
24
IssueTracker/IssueRow.cs
Normal file
24
IssueTracker/IssueRow.cs
Normal 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; }
|
||||||
|
}
|
||||||
@@ -7,4 +7,8 @@
|
|||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="CsvHelper" Version="33.1.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -1,10 +1,165 @@
|
|||||||
namespace IssueTracker
|
using CsvHelper;
|
||||||
|
using CsvHelper.Configuration;
|
||||||
|
using System.Globalization;
|
||||||
|
|
||||||
|
namespace IssueTracker;
|
||||||
|
|
||||||
|
internal class Program
|
||||||
{
|
{
|
||||||
internal class Program
|
static int Main(string[] args)
|
||||||
{
|
{
|
||||||
static void Main(string[] args)
|
if (args.Length < 2)
|
||||||
{
|
{
|
||||||
Console.WriteLine("Hello, World!");
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user