| <ItemGroup> | <ItemGroup> | ||||
| <Using Include="System.Threading.ExecutionContext" Alias="ExecutionContext" /> | <Using Include="System.Threading.ExecutionContext" Alias="ExecutionContext" /> | ||||
| </ItemGroup> | </ItemGroup> | ||||
| <ItemGroup> | |||||
| <Folder Include="Helpers\" /> | |||||
| </ItemGroup> | |||||
| </Project> | </Project> |
| using BlackRockReportFunction.Models; | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using System.Threading.Tasks; | |||||
| using System.Net; | |||||
| using System.Net.Http; | |||||
| using System.Net.Http.Headers; | |||||
| using GemBox.Spreadsheet; | |||||
| using Microsoft.AspNetCore.Mvc; | |||||
| using Microsoft.AspNetCore.Http; | |||||
| using Microsoft.Build.Tasks.Deployment.Bootstrapper; | |||||
| namespace BlackRockReportFunction.Bussines | |||||
| { | |||||
| public class ClockifyReports | |||||
| { | |||||
| public static string? clockifyApiKey = Environment.GetEnvironmentVariable("ClockifyApiKey"); | |||||
| static HttpClient client = new HttpClient(); | |||||
| public static async Task InitializeClockifyIntegration() | |||||
| { | |||||
| client.DefaultRequestHeaders.Add("X-API-Key", clockifyApiKey); | |||||
| client.BaseAddress = new Uri("https://reports.api.clockify.me/v1"); | |||||
| client.DefaultRequestHeaders.Accept.Clear(); | |||||
| client.DefaultRequestHeaders.Accept | |||||
| .Add(new MediaTypeWithQualityHeaderValue("application/json")); | |||||
| } | |||||
| public static async Task<Uri> CreateClockifyReport(ClockifyReport clockifyReport) | |||||
| { | |||||
| HttpResponseMessage httpResponseMessage = await client | |||||
| .PostAsJsonAsync("api/clockifyreport", clockifyReport); | |||||
| httpResponseMessage.EnsureSuccessStatusCode(); | |||||
| return httpResponseMessage.Headers.Location; | |||||
| } | |||||
| public static async Task<ClockifyReport> GetClockifyReportAsync(string path) | |||||
| { | |||||
| ClockifyReport clockifyReport = null; | |||||
| HttpResponseMessage httpResponseMessage= await client.GetAsync(path); | |||||
| if (httpResponseMessage.IsSuccessStatusCode) | |||||
| { | |||||
| clockifyReport = await httpResponseMessage.Content.ReadAsAsync<ClockifyReport>(); | |||||
| } | |||||
| return clockifyReport; | |||||
| } | |||||
| } | |||||
| } |
| using GemBox.Spreadsheet; | using GemBox.Spreadsheet; | ||||
| using BlackRockReportFunction.Models; | using BlackRockReportFunction.Models; | ||||
| using BlackRockReportFunction.Helpers; | |||||
| namespace BlackRockReportFunction.Bussines | namespace BlackRockReportFunction.Bussines | ||||
| { | { | ||||
| internal class ReportGenerator | internal class ReportGenerator | ||||
| { | { | ||||
| public const string DefaultFontName = "Calibri"; | |||||
| public const string DefaultFontName = "DejaVu Sans"; | |||||
| public const int DefaultFontSize = 20; | public const int DefaultFontSize = 20; | ||||
| public static ExcelFile GenerateReportContent(ClockifyReport reportObject) | |||||
| public static ExcelFile GenerateReportContent(Root reportObject) | |||||
| { | { | ||||
| string licenseKey = Environment.GetEnvironmentVariable("GemBoxLicenseKey"); | string licenseKey = Environment.GetEnvironmentVariable("GemBoxLicenseKey"); | ||||
| SpreadsheetInfo.SetLicense(licenseKey); | SpreadsheetInfo.SetLicense(licenseKey); | ||||
| { | { | ||||
| Name = DefaultFontName, | Name = DefaultFontName, | ||||
| Weight = ExcelFont.NormalWeight, | Weight = ExcelFont.NormalWeight, | ||||
| Size = DefaultFontSize * 10, | |||||
| Size = DefaultFontSize * 11, | |||||
| }, | }, | ||||
| VerticalAlignment = VerticalAlignmentStyle.Center, | VerticalAlignment = VerticalAlignmentStyle.Center, | ||||
| }; | }; | ||||
| return excelFile; | return excelFile; | ||||
| } | } | ||||
| public static void AddReportItems(ClockifyReport reportObject, ExcelWorksheet ws, CellStyle sectionStyle, CellStyle mainDetailsStyle, CellStyle normalDetailsStyle) | |||||
| public static void AddReportItems(Root reportObject, ExcelWorksheet ws, CellStyle sectionStyle, CellStyle mainDetailsStyle, CellStyle normalDetailsStyle) | |||||
| { | { | ||||
| TimeSpan t, time,time1; | |||||
| int row = 0; | int row = 0; | ||||
| string[] sectionNames = { "User ", "Description ", "Time (h) ", "Time (decimal) ", "Amount (USD) " }; | |||||
| string[] sectionNames = { "User ", "Description ", "Time (h) ", "Time (decimal) "}; | |||||
| decimal totalAmountSum = 0; | decimal totalAmountSum = 0; | ||||
| List<string> usersDecimalHours = new List<string>(); | List<string> usersDecimalHours = new List<string>(); | ||||
| row++; | row++; | ||||
| foreach (var reportPerson in reportObject.reportPeople) | |||||
| foreach (var reportPerson in reportObject.groupOne) | |||||
| { | { | ||||
| ws.Cells[row, 0].Style = mainDetailsStyle; | ws.Cells[row, 0].Style = mainDetailsStyle; | ||||
| ws.Cells[row, 0].Style.Borders[IndividualBorder.Right].LineStyle = LineStyle.Thin; | ws.Cells[row, 0].Style.Borders[IndividualBorder.Right].LineStyle = LineStyle.Thin; | ||||
| ws.Cells[row, 0].Value = reportPerson.fullName; | |||||
| ws.Cells[row, 0].Value = reportPerson.name; | |||||
| ws.Cells[row, 1].Style.Borders[IndividualBorder.Right].LineStyle = LineStyle.Thin; | ws.Cells[row, 1].Style.Borders[IndividualBorder.Right].LineStyle = LineStyle.Thin; | ||||
| var sumOfRecordHours = Formaters.getSumOfRecordTimes(reportPerson.records.Select(record => record.recordTime).ToList()); | |||||
| time = TimeSpan.FromSeconds(reportPerson.duration); | |||||
| var sumOfRecordHours = string.Format("{0:00}:{1:D2}:{2:D2}", Math.Floor(time.TotalHours), time.Minutes, time.Seconds); | |||||
| ws.Cells[row, 2].Style = mainDetailsStyle; | ws.Cells[row, 2].Style = mainDetailsStyle; | ||||
| ws.Cells[row, 2].Style.HorizontalAlignment = HorizontalAlignmentStyle.Right; | ws.Cells[row, 2].Style.HorizontalAlignment = HorizontalAlignmentStyle.Right; | ||||
| ws.Cells[row, 2].Style.Borders[IndividualBorder.Right].LineStyle = LineStyle.Thin; | ws.Cells[row, 2].Style.Borders[IndividualBorder.Right].LineStyle = LineStyle.Thin; | ||||
| ws.Cells[row, 3].Style = mainDetailsStyle; | ws.Cells[row, 3].Style = mainDetailsStyle; | ||||
| ws.Cells[row, 3].Style.HorizontalAlignment = HorizontalAlignmentStyle.Right; | ws.Cells[row, 3].Style.HorizontalAlignment = HorizontalAlignmentStyle.Right; | ||||
| ws.Cells[row, 3].Style.Borders[IndividualBorder.Right].LineStyle = LineStyle.Thin; | ws.Cells[row, 3].Style.Borders[IndividualBorder.Right].LineStyle = LineStyle.Thin; | ||||
| var userDecimalHours = Formaters.getDecimalHours(sumOfRecordHours); | |||||
| var userDecimalHours = Convert.ToDecimal(TimeSpan.FromSeconds(reportPerson.duration).TotalHours).ToString("0.00"); ; | |||||
| ws.Cells[row, 3].Value = userDecimalHours; | ws.Cells[row, 3].Value = userDecimalHours; | ||||
| usersDecimalHours.Add(userDecimalHours); | usersDecimalHours.Add(userDecimalHours); | ||||
| ws.Cells[row, 4].Style = mainDetailsStyle; | |||||
| ws.Cells[row, 4].Style.HorizontalAlignment = HorizontalAlignmentStyle.Right; | |||||
| ws.Cells[row, 4].Style.Borders[IndividualBorder.Right].LineStyle = LineStyle.Thin; | |||||
| ws.Cells[row, 4].Value = string.Format("{0} USD", reportPerson.records.Sum(record => record.amount).ToString("0.00")); | |||||
| totalAmountSum += reportPerson.records.Sum(record => record.amount); | |||||
| row++; | row++; | ||||
| foreach (var personRecord in reportPerson.records) | |||||
| foreach (var personRecord in reportPerson.children) | |||||
| { | { | ||||
| ws.Cells[row, 0].Style.Borders[IndividualBorder.Right].LineStyle = LineStyle.Thin; | ws.Cells[row, 0].Style.Borders[IndividualBorder.Right].LineStyle = LineStyle.Thin; | ||||
| ws.Cells[row, 1].Style = normalDetailsStyle; | ws.Cells[row, 1].Style = normalDetailsStyle; | ||||
| ws.Cells[row, 1].Style.Borders[IndividualBorder.Right].LineStyle = LineStyle.Thin; | ws.Cells[row, 1].Style.Borders[IndividualBorder.Right].LineStyle = LineStyle.Thin; | ||||
| ws.Cells[row, 1].Value = personRecord.recordDescription; | |||||
| ws.Cells[row, 1].Value = personRecord.name; | |||||
| ws.Cells[row, 2].Style = normalDetailsStyle; | ws.Cells[row, 2].Style = normalDetailsStyle; | ||||
| ws.Cells[row, 2].Style.HorizontalAlignment = HorizontalAlignmentStyle.Right; | ws.Cells[row, 2].Style.HorizontalAlignment = HorizontalAlignmentStyle.Right; | ||||
| ws.Cells[row, 2].Style.Borders[IndividualBorder.Right].LineStyle = LineStyle.Thin; | ws.Cells[row, 2].Style.Borders[IndividualBorder.Right].LineStyle = LineStyle.Thin; | ||||
| ws.Cells[row, 2].Value = Formaters.getRecordTime(personRecord.recordTime); | |||||
| t = TimeSpan.FromSeconds(personRecord.duration); | |||||
| ws.Cells[row, 2].Value = string.Format("{0:00}:{1:D2}:{2:D2}", Math.Floor(t.TotalHours), t.Minutes,t.Seconds) ; | |||||
| ws.Cells[row, 3].Style = normalDetailsStyle; | ws.Cells[row, 3].Style = normalDetailsStyle; | ||||
| ws.Cells[row, 3].Style.HorizontalAlignment = HorizontalAlignmentStyle.Right; | ws.Cells[row, 3].Style.HorizontalAlignment = HorizontalAlignmentStyle.Right; | ||||
| ws.Cells[row, 3].Style.Borders[IndividualBorder.Right].LineStyle = LineStyle.Thin; | ws.Cells[row, 3].Style.Borders[IndividualBorder.Right].LineStyle = LineStyle.Thin; | ||||
| ws.Cells[row, 3].Value = Formaters.getDecimalHours(personRecord.recordTime); | |||||
| ws.Cells[row, 3].Value = Convert.ToDecimal(TimeSpan.FromSeconds(personRecord.duration).TotalHours).ToString("0.00"); | |||||
| ws.Cells[row, 4].Style = normalDetailsStyle; | |||||
| ws.Cells[row, 4].Style.HorizontalAlignment = HorizontalAlignmentStyle.Right; | |||||
| ws.Cells[row, 4].Style.Borders[IndividualBorder.Right].LineStyle = LineStyle.Thin; | |||||
| ws.Cells[row, 4].Value = string.Format("{0} USD", personRecord.amount.ToString("0.00")); | |||||
| row++; | row++; | ||||
| } | } | ||||
| } | } | ||||
| ws.Cells.GetSubrangeAbsolute(row, 0, row, 1).Merged = true; | |||||
| ws.Cells.GetSubrangeAbsolute(row, 0, row, 1).Style = mainDetailsStyle; | |||||
| ws.Cells.GetSubrangeAbsolute(row, 0, row, 1).Style.Borders[IndividualBorder.Right].LineStyle = LineStyle.Thin; | |||||
| ws.Cells.GetSubrangeAbsolute(row, 0, row, 1).Value = reportObject.reportDescription; | |||||
| var totalSum = Formaters.getTotalSum( | |||||
| reportObject.reportPeople.Select( | |||||
| person => Formaters.getSumOfRecordTimes( | |||||
| person.records.Select(record => record.recordTime).ToList() | |||||
| ) | |||||
| ).ToList() | |||||
| ); | |||||
| var totalSum = reportObject.totals.Select(total => total.totalTime).FirstOrDefault(); | |||||
| ws.Cells[row, 0].Style = mainDetailsStyle; | |||||
| ws.Cells[row, 0].Style.Borders[IndividualBorder.Right].LineStyle = LineStyle.Thin; | |||||
| ws.Cells[row, 0].Value = "Total"; | |||||
| ws.Cells[row, 2].Style = mainDetailsStyle; | ws.Cells[row, 2].Style = mainDetailsStyle; | ||||
| ws.Cells[row, 2].Style.HorizontalAlignment = HorizontalAlignmentStyle.Right; | ws.Cells[row, 2].Style.HorizontalAlignment = HorizontalAlignmentStyle.Right; | ||||
| ws.Cells[row, 2].Style.Borders[IndividualBorder.Right].LineStyle = LineStyle.Thin; | ws.Cells[row, 2].Style.Borders[IndividualBorder.Right].LineStyle = LineStyle.Thin; | ||||
| ws.Cells[row, 2].Value = totalSum; | |||||
| time1= t = TimeSpan.FromSeconds(totalSum); | |||||
| ws.Cells[row, 2].Value = string.Format("{0:00}:{1:D2}:{2:D2}", Math.Floor(time1.TotalHours), time1.Minutes, time1.Seconds); | |||||
| ws.Cells[row, 3].Style = mainDetailsStyle; | ws.Cells[row, 3].Style = mainDetailsStyle; | ||||
| ws.Cells[row, 3].Style.HorizontalAlignment = HorizontalAlignmentStyle.Right; | ws.Cells[row, 3].Style.HorizontalAlignment = HorizontalAlignmentStyle.Right; | ||||
| ws.Cells[row, 3].Style.Borders[IndividualBorder.Right].LineStyle = LineStyle.Thin; | ws.Cells[row, 3].Style.Borders[IndividualBorder.Right].LineStyle = LineStyle.Thin; | ||||
| ws.Cells[row, 3].Value = Formaters.getTotalDecimalHours(usersDecimalHours); | |||||
| ws.Cells[row, 3].Value = Convert.ToDecimal(TimeSpan.FromSeconds(totalSum).TotalHours).ToString("0.00"); ; | |||||
| ws.Cells[row, 4].Style = mainDetailsStyle; | |||||
| ws.Cells[row, 4].Style.HorizontalAlignment = HorizontalAlignmentStyle.Right; | |||||
| ws.Cells[row, 4].Style.Borders[IndividualBorder.Right].LineStyle = LineStyle.Thin; | |||||
| ws.Cells[row, 4].Value = string.Format("{0} USD", totalAmountSum.ToString("0.00")); | |||||
| } | } | ||||
| public static void AutoFitReport(ExcelFile excelFile) | public static void AutoFitReport(ExcelFile excelFile) |
| using Microsoft.Extensions.Logging; | using Microsoft.Extensions.Logging; | ||||
| using BlackRockReportFunction.Models; | using BlackRockReportFunction.Models; | ||||
| using Newtonsoft.Json; | using Newtonsoft.Json; | ||||
| using System.Net.Http; | |||||
| using System.Net.Http.Headers; | |||||
| using System.Text; | |||||
| namespace BlackRockReportFunction | namespace BlackRockReportFunction | ||||
| { | { | ||||
| public class ClockifyApiIntegrationFunction | public class ClockifyApiIntegrationFunction | ||||
| { | { | ||||
| private readonly ILogger _logger; | private readonly ILogger _logger; | ||||
| public static string? clockifyApiKey = Environment.GetEnvironmentVariable("ClockifyApiKey"); | |||||
| static HttpClient client = new HttpClient(); | |||||
| public static async Task InitializeClockifyIntegration() | |||||
| { | |||||
| //client.DefaultRequestHeaders.Add("X-API-Key", clockifyApiKey); /// | |||||
| client.DefaultRequestHeaders.Add("X-API-Key", "MmU2ZTA2MGItMTM1ZS00ZTg1LTkwMjAtMDkzYThiZmNmYmIy"); | |||||
| client.BaseAddress = new Uri("https://reports.api.clockify.me/v1"); | |||||
| client.DefaultRequestHeaders.Accept.Clear(); | |||||
| client.DefaultRequestHeaders.Accept | |||||
| .Add(new MediaTypeWithQualityHeaderValue("application/json")); | |||||
| } | |||||
| public ClockifyApiIntegrationFunction(ILoggerFactory loggerFactory) | public ClockifyApiIntegrationFunction(ILoggerFactory loggerFactory) | ||||
| { | { | ||||
| _logger = loggerFactory.CreateLogger<ClockifyApiIntegrationFunction>(); | _logger = loggerFactory.CreateLogger<ClockifyApiIntegrationFunction>(); | ||||
| [Function("ClockifyApiIntegrationFunction")] | [Function("ClockifyApiIntegrationFunction")] | ||||
| [QueueOutput("queue1")] | [QueueOutput("queue1")] | ||||
| public string Run([TimerTrigger("*/15 * * * * *")] MyInfo myTimer) | |||||
| public string Run([TimerTrigger("*/15 * * * * *" )] MyInfo myTimer) //TODO: Set on Friday at 20 o'clock | |||||
| { | { | ||||
| ClockifyReports.InitializeClockifyIntegration(); | |||||
| InitializeClockifyIntegration(); // Clockify API Integration | |||||
| var json = "{\"dateRangeStart\":\"2022-05-30T00:00:00.000\",\"dateRangeEnd\":\"2022-06-05T23:59:59.000\",\"summaryFilter\":{\"groups\":[\"USER\",\"TIMEENTRY\"]},\"clients\":{\"ids\":[\"61488f8d9eb0753d0e40d761\"]},\"projects\":{\"ids\":[\"6242f015f6fe850b94cd0c64\"]},\"amountShown\":\"HIDE_AMOUNT\"}"; | |||||
| HttpContent httpContent = new StringContent(json, Encoding.UTF8, "application/json"); | |||||
| var testObject = new ClockifyReport | |||||
| HttpResponseMessage response = client.PostAsync(client.BaseAddress + "/workspaces/5eb44340ef0f6c66fc88732a/reports/summary", httpContent).Result; | |||||
| //TO DO: Clear code!!! | |||||
| if (response.IsSuccessStatusCode) | |||||
| { | { | ||||
| reportName = "BlackRockReport_20220615", | |||||
| reportDescription = "Total (13/06/2022 - 15/06/2022)", | |||||
| reportPeople = new List<Person> | |||||
| { | |||||
| new Person | |||||
| { | |||||
| fullName = "Nikola Jovanovic", | |||||
| records = new List<ClockifyRecord> | |||||
| { | |||||
| new ClockifyRecord | |||||
| { | |||||
| recordDescription = "massa placerat duis ultricies lacus sed turpis tincidunt id aliquet risus feugiat in ante metus dictum at tempor commodo ullamcorper", | |||||
| recordTime = new TimeOnly(3,15,44), | |||||
| amount = 200 | |||||
| }, | |||||
| new ClockifyRecord | |||||
| { | |||||
| recordDescription = "et tortor at risus viverra adipiscing at in tellus integer feugiat scelerisque varius morbi enim nunc faucibus a pellentesque sit", | |||||
| recordTime = new TimeOnly(3,15,44), | |||||
| amount = 150 | |||||
| } | |||||
| } | |||||
| }, | |||||
| new Person | |||||
| { | |||||
| fullName = "Boris Stevanovic", | |||||
| records = new List<ClockifyRecord> | |||||
| { | |||||
| new ClockifyRecord | |||||
| { | |||||
| recordDescription = "iaculis urna id volutpat lacus laoreet non curabitur gravida arcu ac tortor dignissim convallis aenean et tortor at risus viverra", | |||||
| recordTime = new TimeOnly(3,15,44), | |||||
| amount = 300 | |||||
| }, | |||||
| new ClockifyRecord | |||||
| { | |||||
| recordDescription = "gravida arcu ac tortor dignissim convallis aenean et tortor at risus viverra adipiscing at in tellus integer feugiat scelerisque varius", | |||||
| recordTime = new TimeOnly(3,15,44), | |||||
| amount = 100 | |||||
| }, | |||||
| new ClockifyRecord | |||||
| { | |||||
| recordDescription = "sit amet massa vitae tortor condimentum lacinia quis vel eros donec ac odio tempor orci dapibus ultrices in iaculis nunc", | |||||
| recordTime = new TimeOnly(3,15,44), | |||||
| amount = 120 | |||||
| } | |||||
| } | |||||
| }, | |||||
| new Person | |||||
| { | |||||
| fullName = "Dunja Stevanovic", | |||||
| records = new List<ClockifyRecord> | |||||
| { | |||||
| new ClockifyRecord | |||||
| { | |||||
| recordDescription = "vulputate mi sit amet mauris commodo quis imperdiet massa tincidunt nunc pulvinar sapien et ligula ullamcorper malesuada proin libero nunc", | |||||
| recordTime = new TimeOnly(3,15,44), | |||||
| amount = 50 | |||||
| }, | |||||
| new ClockifyRecord | |||||
| { | |||||
| recordDescription = "senectus et netus et malesuada fames ac turpis egestas maecenas pharetra convallis posuere morbi leo urna molestie at elementum eu", | |||||
| recordTime = new TimeOnly(3,15,44), | |||||
| amount = 500 | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| }; | |||||
| _logger.LogInformation($"Data collection successfull!"); | |||||
| return JsonConvert.SerializeObject(testObject); | |||||
| _logger.LogInformation($"Data collection successfull!"); | |||||
| return JsonConvert.SerializeObject(JsonConvert.DeserializeObject<object>(response.Content.ReadAsStringAsync().Result), Formatting.Indented); | |||||
| //var responseContent = JsonConvert.DeserializeObject<object>(response.Content.ReadAsStringAsync().Result); // TO DO: Convert JSON to csv | |||||
| } | |||||
| else | |||||
| { | |||||
| return JsonConvert.SerializeObject(JsonConvert.DeserializeObject<object>(response.Content.ReadAsStringAsync().Result), Formatting.Indented); | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| //using System; | |||||
| //using Microsoft.Azure.Functions.Worker; | |||||
| //using Microsoft.Extensions.Logging; | |||||
| //namespace BlackRockReportFunction | |||||
| //{ | |||||
| // public class Function1 | |||||
| // { | |||||
| // [Function("Function1")] | |||||
| // public void Run([TimerTrigger("0 */5 * * * *")]TimerInfo myTimer, ILogger log) | |||||
| // { | |||||
| // log.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}"); | |||||
| // } | |||||
| // } | |||||
| //} |
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using System.Threading.Tasks; | |||||
| using Microsoft.Extensions.Logging; | |||||
| namespace BlackRockReportFunction.Helpers | |||||
| { | |||||
| public static class Formaters | |||||
| { | |||||
| public static string getSumOfRecordTimes(List<TimeOnly> recordTimes) | |||||
| { | |||||
| int totalHours = 0; | |||||
| int totalMinutes = 0; | |||||
| int totalSeconds = 0; | |||||
| // Calculate Seconds | |||||
| int totalRecordsSeconds = recordTimes.Sum(record => record.Second); | |||||
| totalSeconds = totalRecordsSeconds % 60; | |||||
| totalMinutes += totalRecordsSeconds / 60; | |||||
| // Calculate Minutes | |||||
| int totalRecordsMinutes = recordTimes.Sum(record => record.Minute); | |||||
| totalMinutes = totalMinutes + (totalRecordsMinutes % 60); | |||||
| totalHours += totalRecordsMinutes / 60; | |||||
| // Calculate Hours | |||||
| int totalRecordHours = recordTimes.Sum(record => record.Hour); | |||||
| totalHours += totalRecordHours; | |||||
| return string.Format("{0}:{1}:{2}", | |||||
| totalHours > 9 ? totalHours : string.Format("0{0}", totalHours), | |||||
| totalMinutes > 9 ? totalMinutes : string.Format("0{0}", totalMinutes), | |||||
| totalSeconds > 9 ? totalSeconds : string.Format("0{0}", totalSeconds)); | |||||
| } | |||||
| public static string getDecimalHours(string recordHoursSum) | |||||
| { | |||||
| var components = recordHoursSum.Split(':').ToList(); | |||||
| var hours = (components[0].Length == 2 && components[0].First() == '0') | |||||
| ? components[0].ToCharArray()[1].ToString() | |||||
| : components[0]; | |||||
| var totalMinutes = Convert.ToDecimal(components[1]) + (Convert.ToDecimal(components[2]) / 60); | |||||
| var minutesPercent = Convert.ToInt32(Math.Floor(totalMinutes * 100 / 60)).ToString(); | |||||
| if (minutesPercent.Length == 1) | |||||
| { | |||||
| minutesPercent = string.Format("0{0}", minutesPercent); | |||||
| } | |||||
| return string.Format("{0}:{1}", hours, minutesPercent); | |||||
| } | |||||
| public static string getDecimalHours(TimeOnly recordTime) | |||||
| { | |||||
| return getDecimalHours(string.Format("{0}:{1}:{2}", recordTime.Hour, recordTime.Minute, recordTime.Second)); | |||||
| } | |||||
| public static string getRecordTime(TimeOnly recordTime) | |||||
| { | |||||
| return string.Format("{0}:{1}:{2}", recordTime.Hour, recordTime.Minute, recordTime.Second); | |||||
| } | |||||
| public static string getTotalSum(List<string> personsSums) | |||||
| { | |||||
| var totalHours = 0; | |||||
| var totalMinutes = 0; | |||||
| var totalSeconds = 0; | |||||
| foreach(var personSum in personsSums) | |||||
| { | |||||
| var components = personSum.Split(':').Select(Int32.Parse).ToList(); | |||||
| //add new person time | |||||
| totalSeconds += components[2] % 60; | |||||
| totalMinutes += components[2] / 60; | |||||
| //clean up new sum | |||||
| totalMinutes += totalSeconds / 60; | |||||
| totalSeconds = totalSeconds % 60; | |||||
| //add new person time | |||||
| totalMinutes += components[1] % 60; | |||||
| totalHours += components[1] / 60; | |||||
| //clean up new time | |||||
| totalHours += totalMinutes / 60; | |||||
| totalMinutes = totalMinutes % 60; | |||||
| //add new person time | |||||
| totalHours += components[0]; | |||||
| } | |||||
| return string.Format("{0}:{1}:{2}", | |||||
| totalHours > 9 ? totalHours : string.Format("0{0}", totalHours), | |||||
| totalMinutes > 9 ? totalMinutes : string.Format("0{0}", totalMinutes), | |||||
| totalSeconds > 9 ? totalSeconds : string.Format("0{0}", totalSeconds)); | |||||
| } | |||||
| public static string getTotalDecimalHours(List<string> usersDecimalHours) | |||||
| { | |||||
| var minutesPercent = 0; | |||||
| var hours = 0; | |||||
| foreach(var userDecimalHours in usersDecimalHours) | |||||
| { | |||||
| var components = userDecimalHours.Split(':'); | |||||
| minutesPercent += Convert.ToInt32(components[1]); | |||||
| hours += Convert.ToInt32(components[0]); | |||||
| } | |||||
| hours += minutesPercent / 100; | |||||
| minutesPercent = minutesPercent % 100; | |||||
| return string.Format("{0}:{1}", hours, minutesPercent); | |||||
| } | |||||
| } | |||||
| } |
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Globalization; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using System.Threading.Tasks; | |||||
| using Newtonsoft.Json; | |||||
| namespace BlackRockReportFunction.Helpers | |||||
| { | |||||
| public class TimeOnlyConverter : JsonConverter<TimeOnly> | |||||
| { | |||||
| private const string Format = "hh:mm:ss"; | |||||
| public override TimeOnly ReadJson(JsonReader reader, | |||||
| Type objectType, | |||||
| TimeOnly existingValue, | |||||
| bool hasExistingValue, | |||||
| JsonSerializer serializer) => | |||||
| TimeOnly.ParseExact((string)reader.Value, Format, CultureInfo.InvariantCulture); | |||||
| public override void WriteJson(JsonWriter writer, TimeOnly value, JsonSerializer serializer) => | |||||
| writer.WriteValue(value.ToString(Format, CultureInfo.InvariantCulture)); | |||||
| } | |||||
| } |
| var email = new MimeMessage(); | var email = new MimeMessage(); | ||||
| email.From.Add(MailboxAddress.Parse("nikola.jovanovic@dilig.net")); | email.From.Add(MailboxAddress.Parse("nikola.jovanovic@dilig.net")); | ||||
| email.To.Add(MailboxAddress.Parse("shaniya.blick76@ethereal.email")); | |||||
| email.To.Add(MailboxAddress.Parse("greta.bartoletti@ethereal.email")); | |||||
| email.Subject = "BlackRock Report"; | email.Subject = "BlackRock Report"; | ||||
| var body = new TextPart(TextFormat.Html) { Text = string.Format("Here is yours report for last week. {0}", fileName) }; | var body = new TextPart(TextFormat.Html) { Text = string.Format("Here is yours report for last week. {0}", fileName) }; | ||||
| using var smtp = new SmtpClient(); | using var smtp = new SmtpClient(); | ||||
| smtp.Connect("smtp.ethereal.email", 587, SecureSocketOptions.StartTls); | smtp.Connect("smtp.ethereal.email", 587, SecureSocketOptions.StartTls); | ||||
| smtp.Authenticate("shaniya.blick76@ethereal.email", "8pPsjCbwCFMrEeKNef"); | |||||
| smtp.Authenticate("greta.bartoletti@ethereal.email", "ecsdGZxWHk4yfjZpD5"); //"shaniya.blick76@ethereal.email", "8pPsjCbwCFMrEeKNef" | |||||
| smtp.Send(email); | smtp.Send(email); | ||||
| smtp.Disconnect(true); | smtp.Disconnect(true); | ||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using System.Text.Json.Serialization; | |||||
| using System.Threading.Tasks; | |||||
| namespace BlackRockReportFunction.Models | |||||
| { | |||||
| internal class Child | |||||
| { | |||||
| public int duration { get; set; } | |||||
| public string _id { get; set; } | |||||
| public string name { get; set; } //description | |||||
| public List<object> amounts { get; set; } | |||||
| } | |||||
| } |
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using System.Threading.Tasks; | |||||
| using Newtonsoft.Json; | |||||
| using BlackRockReportFunction.Helpers; | |||||
| namespace BlackRockReportFunction.Models | |||||
| { | |||||
| public class ClockifyRecord | |||||
| { | |||||
| public string recordDescription { get; set; } | |||||
| [JsonConverter(typeof(TimeOnlyConverter))] | |||||
| public TimeOnly recordTime { get; set; } | |||||
| public decimal amount { get; set; } | |||||
| } | |||||
| } |
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using System.Threading.Tasks; | |||||
| namespace BlackRockReportFunction.Models | |||||
| { | |||||
| public class ClockifyReport | |||||
| { | |||||
| public string reportName { get; set; } | |||||
| public string reportDescription { get; set; } | |||||
| public List<Person> reportPeople { get; set; } | |||||
| } | |||||
| } |
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using System.Threading.Tasks; | |||||
| namespace BlackRockReportFunction.Models | |||||
| { | |||||
| internal class GroupOne | |||||
| { | |||||
| public int duration { get; set; } | |||||
| public string _id { get; set; } | |||||
| public string name { get; set; } | |||||
| public string nameLowerCase { get; set; } | |||||
| public List<Child> children { get; set; } | |||||
| public List<object> amounts { get; set; } | |||||
| } | |||||
| } |
| namespace BlackRockReportFunction.Models | namespace BlackRockReportFunction.Models | ||||
| { | { | ||||
| public class Person | |||||
| internal class Root | |||||
| { | { | ||||
| public string fullName { get; set; } | |||||
| public List<ClockifyRecord> records { get; set; } | |||||
| public List<Total> totals { get; set; } | |||||
| public List<GroupOne> groupOne { get; set; } | |||||
| } | } | ||||
| } | } |
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using System.Threading.Tasks; | |||||
| namespace BlackRockReportFunction.Models | |||||
| { | |||||
| internal class Total | |||||
| { | |||||
| public string _id { get; set; } | |||||
| public int totalTime { get; set; } | |||||
| public int entriesCount { get; set; } | |||||
| public object totalAmount { get; set; } | |||||
| public List<object> amounts { get; set; } | |||||
| } | |||||
| } |
| using Microsoft.Azure.WebJobs; | using Microsoft.Azure.WebJobs; | ||||
| using GemBox.Spreadsheet; | using GemBox.Spreadsheet; | ||||
| using BlackRockReportFunction.Models; | using BlackRockReportFunction.Models; | ||||
| using BlackRockReportFunction.Helpers; | |||||
| using Azure.Storage.Blobs; | using Azure.Storage.Blobs; | ||||
| using BlackRockReportFunction.Bussines; | using BlackRockReportFunction.Bussines; | ||||
| using MailKit; | using MailKit; | ||||
| [BlobOutput("report-container/BlackRock_Report.xslx", Connection = "AzureWebJobsStorage")] | [BlobOutput("report-container/BlackRock_Report.xslx", Connection = "AzureWebJobsStorage")] | ||||
| public async Task Run([QueueTrigger("queue1")] string myQueueItem) | public async Task Run([QueueTrigger("queue1")] string myQueueItem) | ||||
| { | { | ||||
| var reportObject = JsonConvert.DeserializeObject<ClockifyReport>(myQueueItem); | |||||
| //Console.WriteLine("Run function debugging"); | |||||
| //var reportObject = JsonConvert.DeserializeObject<ClockifyReport>(myQueueItem); | |||||
| var reportObject = JsonConvert.DeserializeObject<Root>(myQueueItem); | |||||
| var reportFile = ReportGenerator.GenerateReportContent(reportObject); | var reportFile = ReportGenerator.GenerateReportContent(reportObject); | ||||
| string connection = Environment.GetEnvironmentVariable("AzureWebJobsStorage"); | string connection = Environment.GetEnvironmentVariable("AzureWebJobsStorage"); | ||||
| { | { | ||||
| Type = XlsxType.Xlsx | Type = XlsxType.Xlsx | ||||
| }); | }); | ||||
| memoryStream.Position = 0; | memoryStream.Position = 0; | ||||
| await blob.UploadAsync(memoryStream); | await blob.UploadAsync(memoryStream); | ||||