From 7b322ef7fbedebd96147ca167676ae972c715679 Mon Sep 17 00:00:00 2001 From: femsci Date: Fri, 17 Nov 2023 22:46:30 +0100 Subject: [PATCH 01/12] absolute utter filth yet it's working --- src/Nyanlabs.Umogen.Core/Models/ApiChoice.cs | 15 +++++ src/Nyanlabs.Umogen.Core/Models/ApiRequest.cs | 3 + .../Models/ApiResponse.cs | 16 ++++++ .../Models/ApiResponseFormat.cs | 6 ++ src/Nyanlabs.Umogen.Core/Models/GptMessage.cs | 7 +++ .../Models/UmoDocument.cs | 2 +- src/Nyanlabs.Umogen.Core/Models/UsageStats.cs | 17 ++++++ src/Nyanlabs.Umogen.Core/UmoEngine.cs | 2 +- src/Nyanlabs.Umogen.Core/UmoProcess.cs | 57 ++++++++++++++++++- src/Nyanlabs.Umogen.Core/Umogen.cs | 8 ++- .../GenerationTests.cs | 40 +++++++++++++ 11 files changed, 169 insertions(+), 4 deletions(-) create mode 100644 src/Nyanlabs.Umogen.Core/Models/ApiChoice.cs create mode 100644 src/Nyanlabs.Umogen.Core/Models/ApiResponse.cs create mode 100644 src/Nyanlabs.Umogen.Core/Models/ApiResponseFormat.cs create mode 100644 src/Nyanlabs.Umogen.Core/Models/GptMessage.cs create mode 100644 src/Nyanlabs.Umogen.Core/Models/UsageStats.cs create mode 100644 test/Nyanlabs.Umogen.WebTests/GenerationTests.cs diff --git a/src/Nyanlabs.Umogen.Core/Models/ApiChoice.cs b/src/Nyanlabs.Umogen.Core/Models/ApiChoice.cs new file mode 100644 index 0000000..68462c9 --- /dev/null +++ b/src/Nyanlabs.Umogen.Core/Models/ApiChoice.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Nyanlabs.Umogen.Core.Models; + +public class ApiChoice +{ + public string FinishReason { get; set; } = default!; + public int Index { get; set; } + public GptMessage? Message { get; set; } + public GptMessage? Delta { get; set; } + +} diff --git a/src/Nyanlabs.Umogen.Core/Models/ApiRequest.cs b/src/Nyanlabs.Umogen.Core/Models/ApiRequest.cs index 06b7c37..4646f18 100644 --- a/src/Nyanlabs.Umogen.Core/Models/ApiRequest.cs +++ b/src/Nyanlabs.Umogen.Core/Models/ApiRequest.cs @@ -4,4 +4,7 @@ public record ApiRequest { public string Model { get; set; } = "gpt-3.5-turbo"; public float Temperature { get; set; } = 0.7f; + public ICollection Messages { get; set; } = new List(); + public bool Stream { get; set; } + //public ApiResponseFormat ResponseFormat { get; set; } = new(); } diff --git a/src/Nyanlabs.Umogen.Core/Models/ApiResponse.cs b/src/Nyanlabs.Umogen.Core/Models/ApiResponse.cs new file mode 100644 index 0000000..8421f3b --- /dev/null +++ b/src/Nyanlabs.Umogen.Core/Models/ApiResponse.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Nyanlabs.Umogen.Core.Models; + +public class ApiResponse +{ + public string Id { get; set; } = default!; + public string Model { get; set; } = default!; + public string Object { get; set; } = default!; + public UsageStats Usage { get; set; } = default!; + public ICollection Choices { get; set; } = new List(); + public long Created { get; set; } +} diff --git a/src/Nyanlabs.Umogen.Core/Models/ApiResponseFormat.cs b/src/Nyanlabs.Umogen.Core/Models/ApiResponseFormat.cs new file mode 100644 index 0000000..61616b6 --- /dev/null +++ b/src/Nyanlabs.Umogen.Core/Models/ApiResponseFormat.cs @@ -0,0 +1,6 @@ +namespace Nyanlabs.Umogen.Core.Models; + +public class ApiResponseFormat +{ + public string Type { get; set; } = "json_object"; +} diff --git a/src/Nyanlabs.Umogen.Core/Models/GptMessage.cs b/src/Nyanlabs.Umogen.Core/Models/GptMessage.cs new file mode 100644 index 0000000..d85d550 --- /dev/null +++ b/src/Nyanlabs.Umogen.Core/Models/GptMessage.cs @@ -0,0 +1,7 @@ +namespace Nyanlabs.Umogen.Core.Models; + +public record GptMessage(string Role, string Content) +{ + public string Role { get; } = Role; + public string Content { get; } = Content; +} diff --git a/src/Nyanlabs.Umogen.Core/Models/UmoDocument.cs b/src/Nyanlabs.Umogen.Core/Models/UmoDocument.cs index 4f6d715..291b648 100644 --- a/src/Nyanlabs.Umogen.Core/Models/UmoDocument.cs +++ b/src/Nyanlabs.Umogen.Core/Models/UmoDocument.cs @@ -45,6 +45,6 @@ public class UmoDocument : INLSerializable public ValidTime? ValidTime { get; set; } public string NLSerialize() { - return $"{Doctype.Name().Item1} for {Employee.NLSerialize()} working for {Employer.NLSerialize()}; valid {ValidTime?.NLSerialize() ?? "for unspecified time"}."; + return $"{Doctype.Name().Item2} for {Employee.NLSerialize()} working for {Employer.NLSerialize()}; valid {ValidTime?.NLSerialize() ?? "for unspecified time"}."; } } diff --git a/src/Nyanlabs.Umogen.Core/Models/UsageStats.cs b/src/Nyanlabs.Umogen.Core/Models/UsageStats.cs new file mode 100644 index 0000000..45077e6 --- /dev/null +++ b/src/Nyanlabs.Umogen.Core/Models/UsageStats.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json.Serialization; +using System.Threading.Tasks; + +namespace Nyanlabs.Umogen.Core.Models; + +public class UsageStats +{ + [JsonPropertyName("completion_tokens")] + public int CompletionTokens { get; set; } + [JsonPropertyName("prompt_tokens")] + public int PromptTokens { get; set; } + [JsonPropertyName("total_tokens")] + public int TotalTokens { get; set; } +} diff --git a/src/Nyanlabs.Umogen.Core/UmoEngine.cs b/src/Nyanlabs.Umogen.Core/UmoEngine.cs index 87771a3..67d21e5 100644 --- a/src/Nyanlabs.Umogen.Core/UmoEngine.cs +++ b/src/Nyanlabs.Umogen.Core/UmoEngine.cs @@ -38,7 +38,7 @@ public class UmoEngine : IDisposable _http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", apiKey); } - private readonly HttpClient _http; + internal readonly HttpClient _http; public async Task ValidateKey() { diff --git a/src/Nyanlabs.Umogen.Core/UmoProcess.cs b/src/Nyanlabs.Umogen.Core/UmoProcess.cs index 439423b..9257e31 100644 --- a/src/Nyanlabs.Umogen.Core/UmoProcess.cs +++ b/src/Nyanlabs.Umogen.Core/UmoProcess.cs @@ -1,3 +1,10 @@ +using System.Data; +using System.Net.Http.Json; +using System.Text; +using System.Text.Json; + +using Nyanlabs.Umogen.Core.Models; + namespace Nyanlabs.Umogen.Core; public class UmoProcess @@ -9,8 +16,56 @@ public class UmoProcess private readonly UmoEngine _eng; - public async Task Ask() + public async IAsyncEnumerable Ask(string content) { + ApiRequest req = new() + { + Messages = new List() + { + new("system", Umogen.PROMPT), + new("user", content) + }, + Stream = true + }; + HttpRequestMessage msg = new(HttpMethod.Post, "chat/completions"); + msg.Content = new StringContent(JsonSerializer.Serialize(req, options: Umogen.JSON_OPTS), Encoding.UTF8, "application/json"); + var resp = await _eng._http.SendAsync(msg, HttpCompletionOption.ResponseHeadersRead); + + if (!resp.IsSuccessStatusCode) + { + Console.WriteLine(await resp.Content.ReadAsStringAsync()); + throw new DataException($"Cannot process request: {resp.StatusCode}."); + } + + var apiResp = await resp.Content.ReadAsStreamAsync(); + + var reader = new StreamReader(apiResp); + + string? line; + while ((line = await reader.ReadLineAsync()) != null) + { + if (line.StartsWith("data:")) + { + line = line[5..].Trim(); + } + + if (string.IsNullOrWhiteSpace(line)) + { + continue; + } + + File.AppendAllText("/tmp/raw.md", line + "\n"); + + ApiResponse respChunk = JsonSerializer.Deserialize(line, options: Umogen.JSON_OPTS)!; + ApiChoice choice = respChunk.Choices.First(); + + if (choice.FinishReason == "stop") + { + break; + } + + yield return choice.Delta?.Content ?? ":<"; + } } } diff --git a/src/Nyanlabs.Umogen.Core/Umogen.cs b/src/Nyanlabs.Umogen.Core/Umogen.cs index 116dbde..b9781e6 100644 --- a/src/Nyanlabs.Umogen.Core/Umogen.cs +++ b/src/Nyanlabs.Umogen.Core/Umogen.cs @@ -1,7 +1,13 @@ +using System.Text.Json; + namespace Nyanlabs.Umogen.Core; public class Umogen { public const string DEFAULT_API_KEY_FILE = "umogenkey.secret"; - public const string PROMPT = "You write Polish legal document in markdown format."; + public const string PROMPT = "You write Polish legal contracts in Polish language in markdown format."; + public static readonly JsonSerializerOptions JSON_OPTS = new(JsonSerializerDefaults.Web) + { + PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower + }; } diff --git a/test/Nyanlabs.Umogen.WebTests/GenerationTests.cs b/test/Nyanlabs.Umogen.WebTests/GenerationTests.cs new file mode 100644 index 0000000..72fe9d8 --- /dev/null +++ b/test/Nyanlabs.Umogen.WebTests/GenerationTests.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +using Nyanlabs.Umogen.Core; +using Nyanlabs.Umogen.Core.Models; + +namespace Nyanlabs.Umogen.WebTests; + +public class GenerationTests +{ + private Person ModelEmployer => new("Kotek", "Miauczyński", "CBS4327563", "04281308999", new DateTime(2004, 08, 13)); + + private Person ModelEmployee => new("Miau", "Kotczyński", "CBS69696969", "76011694336", new DateTime(1976, 1, 16)); + + [Fact] + public async Task Meow4Me() + { + // Given + var rootDir = Environment.CurrentDirectory.Replace("test/Nyanlabs.Umogen.WebTests/bin/Debug/net8.0", ""); + + UmoEngine eng = new(Path.Combine(rootDir, Core.Umogen.DEFAULT_API_KEY_FILE)); + + // When + UmoDocument doc = new(UmoDoctype.CONTR_EMPLOYMENT, ModelEmployer, ModelEmployee); + + UmoProcess proc = new(eng); + var enu = proc.Ask(doc.NLSerialize()); + + await foreach (var str in enu) + { + Console.Write(str); + File.AppendAllText("/tmp/doc.md", str); + } + + Console.WriteLine("~"); + + } +} From b9c218111a54e8d2e505778bb73d4f89ab652f3f Mon Sep 17 00:00:00 2001 From: femsci Date: Fri, 17 Nov 2023 23:37:35 +0100 Subject: [PATCH 02/12] Add legal entity --- src/Nyanlabs.Umogen.Core/Models/IEntity.cs | 7 ++++++ .../Models/LegalEntity.cs | 24 +++++++++++++++++++ src/Nyanlabs.Umogen.Core/Models/Person.cs | 12 +++++++++- .../Models/UmoDocument.cs | 4 ++-- .../NLSerializationTests.cs | 19 +++++++++++++-- 5 files changed, 61 insertions(+), 5 deletions(-) create mode 100644 src/Nyanlabs.Umogen.Core/Models/IEntity.cs create mode 100644 src/Nyanlabs.Umogen.Core/Models/LegalEntity.cs diff --git a/src/Nyanlabs.Umogen.Core/Models/IEntity.cs b/src/Nyanlabs.Umogen.Core/Models/IEntity.cs new file mode 100644 index 0000000..0c11c94 --- /dev/null +++ b/src/Nyanlabs.Umogen.Core/Models/IEntity.cs @@ -0,0 +1,7 @@ +namespace Nyanlabs.Umogen.Core.Models; + +public interface IEntity : INLSerializable +{ + public string GetName(); + public string? IdCode(); +} diff --git a/src/Nyanlabs.Umogen.Core/Models/LegalEntity.cs b/src/Nyanlabs.Umogen.Core/Models/LegalEntity.cs new file mode 100644 index 0000000..027c4ea --- /dev/null +++ b/src/Nyanlabs.Umogen.Core/Models/LegalEntity.cs @@ -0,0 +1,24 @@ +namespace Nyanlabs.Umogen.Core.Models; + +public class LegalEntity(string Name, string? Nip = null, string? Headquarters = null, Person? Representative = null) : IEntity, INLSerializable +{ + public string Name { get; set; } = Name; + public string? Nip { get; set; } = Nip; + public string? Headquarters { get; set; } = Headquarters; + public Person? Representative { get; set; } = Representative; + + public string GetName() + { + return Name; + } + + public string? IdCode() + { + return Nip; + } + + public string NLSerialize() + { + return $"{Name}; with NIP {Nip ?? "unknown"}{(Headquarters != null ? $"; located in {Headquarters}" : "")}{(Representative != null ? $"; represented by {Representative.GetName()}" : "")}"; + } +} diff --git a/src/Nyanlabs.Umogen.Core/Models/Person.cs b/src/Nyanlabs.Umogen.Core/Models/Person.cs index 1befbe4..343d09d 100644 --- a/src/Nyanlabs.Umogen.Core/Models/Person.cs +++ b/src/Nyanlabs.Umogen.Core/Models/Person.cs @@ -1,6 +1,6 @@ namespace Nyanlabs.Umogen.Core.Models; -public record Person(string Name, string Surname, string? Id = null, string? Pesel = null, DateTime? DoB = null) : INLSerializable +public record Person(string Name, string Surname, string? Id = null, string? Pesel = null, DateTime? DoB = null) : IEntity, INLSerializable { public string Name { get; set; } = Name; public string Surname { get; set; } = Surname; @@ -8,6 +8,16 @@ public record Person(string Name, string Surname, string? Id = null, string? Pes public string? Pesel { get; set; } = Pesel; public DateTime? DoB { get; set; } = DoB; + public string GetName() + { + return $"{Name} {Surname}"; + } + + public string? IdCode() + { + return IDCode!; + } + public string NLSerialize() { return $"{Name} {Surname}; {(Pesel != null ? $"PESEL {Pesel}" : "unknown PESEL")}; {(IDCode != null ? $"ID series {IDCode}" : "unknown ID series")}; {(DoB != null ? $"born {DoB.Value:dd MMM yyyy}" : "unknown date of birth")}"; diff --git a/src/Nyanlabs.Umogen.Core/Models/UmoDocument.cs b/src/Nyanlabs.Umogen.Core/Models/UmoDocument.cs index 291b648..0ad4733 100644 --- a/src/Nyanlabs.Umogen.Core/Models/UmoDocument.cs +++ b/src/Nyanlabs.Umogen.Core/Models/UmoDocument.cs @@ -30,7 +30,7 @@ public static class UmoDoctypeBindings public class UmoDocument : INLSerializable { - public UmoDocument(UmoDoctype type, Person employer, Person employee, ValidTime? validTime = null) + public UmoDocument(UmoDoctype type, IEntity employer, Person employee, ValidTime? validTime = null) { Doctype = type; Employer = employer; @@ -39,7 +39,7 @@ public class UmoDocument : INLSerializable } public UmoDoctype Doctype { get; set; } - public Person Employer { get; set; } + public IEntity Employer { get; set; } public Person Employee { get; set; } //Null is 'unspecified time' public ValidTime? ValidTime { get; set; } diff --git a/test/Nyanlabs.Umogen.CoreTests/NLSerializationTests.cs b/test/Nyanlabs.Umogen.CoreTests/NLSerializationTests.cs index 2e60fd8..46d23f8 100644 --- a/test/Nyanlabs.Umogen.CoreTests/NLSerializationTests.cs +++ b/test/Nyanlabs.Umogen.CoreTests/NLSerializationTests.cs @@ -18,7 +18,7 @@ public class NLSerializationTests var query = doc.NLSerialize(); // Then - Assert.Equal($"Contract of Employment for {ModelEmployee.NLSerialize()} working for {ModelEmployer.NLSerialize()}; valid for unspecified time.", query); + Assert.Equal($"Umowa o Pracę for {ModelEmployee.NLSerialize()} working for {ModelEmployer.NLSerialize()}; valid for unspecified time.", query); } [Fact] @@ -31,7 +31,7 @@ public class NLSerializationTests var query = doc.NLSerialize(); // Then - Assert.Equal($"Contract of Employment for {ModelEmployee.NLSerialize()} working for {ModelEmployer.NLSerialize()}; valid from 17 Nov 2023 until 18 Nov 2023.", query); + Assert.Equal($"Umowa o Pracę for {ModelEmployee.NLSerialize()} working for {ModelEmployer.NLSerialize()}; valid from 17 Nov 2023 until 18 Nov 2023.", query); } [Fact] @@ -64,4 +64,19 @@ public class NLSerializationTests // Then Assert.Equal("Kotek Miauczyński; unknown PESEL; unknown ID series; unknown date of birth", query); } + + private LegalEntity ModelCompany => new("Nyanbyte P.S.A.", "1313131313", "Miaumiaśna 13, Miauczki", ModelEmployer); + + [Fact] + public void CompanySerializationTest() + { + // Given + LegalEntity company = ModelCompany; + + // When + var query = company.NLSerialize(); + + // Then + Assert.Equal("Nyanbyte P.S.A.; with NIP 1313131313; located in Miaumiaśna 13, Miauczki; represented by Kotek Miauczyński", query); + } } From 067a5365d28b4733375943c5d4f2a9d6ff2e0835 Mon Sep 17 00:00:00 2001 From: femsci Date: Fri, 17 Nov 2023 23:38:35 +0100 Subject: [PATCH 03/12] Refactor Server Sent Events parsing --- src/Nyanlabs.Umogen.Core/Models/ApiChoice.cs | 5 --- .../Models/ApiResponse.cs | 5 --- src/Nyanlabs.Umogen.Core/Models/UsageStats.cs | 4 -- src/Nyanlabs.Umogen.Core/SseConsumer.cs | 45 +++++++++++++++++++ src/Nyanlabs.Umogen.Core/UmoProcess.cs | 27 +++-------- src/Nyanlabs.Umogen.Core/Umogen.cs | 2 +- 6 files changed, 51 insertions(+), 37 deletions(-) create mode 100644 src/Nyanlabs.Umogen.Core/SseConsumer.cs diff --git a/src/Nyanlabs.Umogen.Core/Models/ApiChoice.cs b/src/Nyanlabs.Umogen.Core/Models/ApiChoice.cs index 68462c9..6fe57f6 100644 --- a/src/Nyanlabs.Umogen.Core/Models/ApiChoice.cs +++ b/src/Nyanlabs.Umogen.Core/Models/ApiChoice.cs @@ -1,8 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - namespace Nyanlabs.Umogen.Core.Models; public class ApiChoice diff --git a/src/Nyanlabs.Umogen.Core/Models/ApiResponse.cs b/src/Nyanlabs.Umogen.Core/Models/ApiResponse.cs index 8421f3b..fca01a0 100644 --- a/src/Nyanlabs.Umogen.Core/Models/ApiResponse.cs +++ b/src/Nyanlabs.Umogen.Core/Models/ApiResponse.cs @@ -1,8 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - namespace Nyanlabs.Umogen.Core.Models; public class ApiResponse diff --git a/src/Nyanlabs.Umogen.Core/Models/UsageStats.cs b/src/Nyanlabs.Umogen.Core/Models/UsageStats.cs index 45077e6..2ab1b60 100644 --- a/src/Nyanlabs.Umogen.Core/Models/UsageStats.cs +++ b/src/Nyanlabs.Umogen.Core/Models/UsageStats.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; using System.Text.Json.Serialization; -using System.Threading.Tasks; namespace Nyanlabs.Umogen.Core.Models; diff --git a/src/Nyanlabs.Umogen.Core/SseConsumer.cs b/src/Nyanlabs.Umogen.Core/SseConsumer.cs new file mode 100644 index 0000000..80c7558 --- /dev/null +++ b/src/Nyanlabs.Umogen.Core/SseConsumer.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Text.Json; +using System.Threading.Tasks; + +namespace Nyanlabs.Umogen.Core; + +public class SseConsumer : IDisposable, IAsyncDisposable +{ + public SseConsumer(Stream stream) + { + _stream = stream; + } + + private readonly Stream _stream; + + public async IAsyncEnumerable ReadEvents() + { + var reader = new StreamReader(_stream); + + while (!reader.EndOfStream) + { + string line = (await reader.ReadLineAsync())!; + //Second \n + await reader.ReadLineAsync(); + + //Remove 'data: ' + line = line[6..].Trim(); + + yield return JsonSerializer.Deserialize(line, Umogen.JSON_OPTS) ?? throw new DataException("Invalid data..."); + } + } + + public void Dispose() + { + _stream.Dispose(); + } + + public ValueTask DisposeAsync() + { + return _stream.DisposeAsync(); + } +} diff --git a/src/Nyanlabs.Umogen.Core/UmoProcess.cs b/src/Nyanlabs.Umogen.Core/UmoProcess.cs index 9257e31..875a61a 100644 --- a/src/Nyanlabs.Umogen.Core/UmoProcess.cs +++ b/src/Nyanlabs.Umogen.Core/UmoProcess.cs @@ -1,5 +1,4 @@ using System.Data; -using System.Net.Http.Json; using System.Text; using System.Text.Json; @@ -38,34 +37,18 @@ public class UmoProcess throw new DataException($"Cannot process request: {resp.StatusCode}."); } - var apiResp = await resp.Content.ReadAsStreamAsync(); + var apiStream = await resp.Content.ReadAsStreamAsync(); - var reader = new StreamReader(apiResp); - - string? line; - while ((line = await reader.ReadLineAsync()) != null) + using var sseConsumer = new SseConsumer(apiStream); + await foreach (var chunk in sseConsumer.ReadEvents()) { - if (line.StartsWith("data:")) - { - line = line[5..].Trim(); - } - - if (string.IsNullOrWhiteSpace(line)) - { - continue; - } - - File.AppendAllText("/tmp/raw.md", line + "\n"); - - ApiResponse respChunk = JsonSerializer.Deserialize(line, options: Umogen.JSON_OPTS)!; - ApiChoice choice = respChunk.Choices.First(); - + ApiChoice choice = chunk.Choices.First(); if (choice.FinishReason == "stop") { break; } - yield return choice.Delta?.Content ?? ":<"; + yield return choice.Delta?.Content ?? throw new DataException("Message delta is null"); } } } diff --git a/src/Nyanlabs.Umogen.Core/Umogen.cs b/src/Nyanlabs.Umogen.Core/Umogen.cs index b9781e6..6331e4f 100644 --- a/src/Nyanlabs.Umogen.Core/Umogen.cs +++ b/src/Nyanlabs.Umogen.Core/Umogen.cs @@ -5,7 +5,7 @@ namespace Nyanlabs.Umogen.Core; public class Umogen { public const string DEFAULT_API_KEY_FILE = "umogenkey.secret"; - public const string PROMPT = "You write Polish legal contracts in Polish language in markdown format."; + public const string PROMPT = "You write Polish legal contracts in Polish language in markdown format. Fill unknown fields with signature placeholder (underscore) with enough space to sign it with pen."; public static readonly JsonSerializerOptions JSON_OPTS = new(JsonSerializerDefaults.Web) { PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower From b255abc5c703062231caeb2d66bfc727c6c1ad77 Mon Sep 17 00:00:00 2001 From: femsci Date: Sat, 18 Nov 2023 01:20:31 +0100 Subject: [PATCH 04/12] =?UTF-8?q?Add=20our=20beloved=20Middleware=20Papies?= =?UTF-8?q?kie=20=F0=9F=92=9B=F0=9F=92=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Nyanlabs.Umogen.Server/App.razor | 2 +- .../Data/WeatherForecast.cs | 12 ------------ .../Data/WeatherForecastService.cs | 19 ------------------- .../Nyanlabs.Umogen.Server.csproj | 4 ++++ src/Nyanlabs.Umogen.Server/Program.cs | 6 ++++-- 5 files changed, 9 insertions(+), 34 deletions(-) delete mode 100644 src/Nyanlabs.Umogen.Server/Data/WeatherForecast.cs delete mode 100644 src/Nyanlabs.Umogen.Server/Data/WeatherForecastService.cs diff --git a/src/Nyanlabs.Umogen.Server/App.razor b/src/Nyanlabs.Umogen.Server/App.razor index 6fd3ed1..3e9cdb6 100644 --- a/src/Nyanlabs.Umogen.Server/App.razor +++ b/src/Nyanlabs.Umogen.Server/App.razor @@ -6,7 +6,7 @@ Not found -

Sorry, there's nothing at this address.

+

Sorri but we slipped and forgor the location 💀

diff --git a/src/Nyanlabs.Umogen.Server/Data/WeatherForecast.cs b/src/Nyanlabs.Umogen.Server/Data/WeatherForecast.cs deleted file mode 100644 index 0a45384..0000000 --- a/src/Nyanlabs.Umogen.Server/Data/WeatherForecast.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Nyanlabs.Umogen.Server.Data; - -public class WeatherForecast -{ - public DateOnly Date { get; set; } - - public int TemperatureC { get; set; } - - public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); - - public string? Summary { get; set; } -} diff --git a/src/Nyanlabs.Umogen.Server/Data/WeatherForecastService.cs b/src/Nyanlabs.Umogen.Server/Data/WeatherForecastService.cs deleted file mode 100644 index eea977f..0000000 --- a/src/Nyanlabs.Umogen.Server/Data/WeatherForecastService.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Nyanlabs.Umogen.Server.Data; - -public class WeatherForecastService -{ - private static readonly string[] Summaries = new[] - { - "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" - }; - - public Task GetForecastAsync(DateOnly startDate) - { - return Task.FromResult(Enumerable.Range(1, 5).Select(index => new WeatherForecast - { - Date = startDate.AddDays(index), - TemperatureC = Random.Shared.Next(-20, 55), - Summary = Summaries[Random.Shared.Next(Summaries.Length)] - }).ToArray()); - } -} diff --git a/src/Nyanlabs.Umogen.Server/Nyanlabs.Umogen.Server.csproj b/src/Nyanlabs.Umogen.Server/Nyanlabs.Umogen.Server.csproj index 7295c13..1770d66 100644 --- a/src/Nyanlabs.Umogen.Server/Nyanlabs.Umogen.Server.csproj +++ b/src/Nyanlabs.Umogen.Server/Nyanlabs.Umogen.Server.csproj @@ -4,6 +4,10 @@ + + + + net8.0 enable diff --git a/src/Nyanlabs.Umogen.Server/Program.cs b/src/Nyanlabs.Umogen.Server/Program.cs index 7cd2c8b..11fa1b3 100644 --- a/src/Nyanlabs.Umogen.Server/Program.cs +++ b/src/Nyanlabs.Umogen.Server/Program.cs @@ -1,11 +1,11 @@ -using Nyanlabs.Umogen.Server.Data; +using Femsci.AspapajNet; var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddRazorPages(); builder.Services.AddServerSideBlazor(); -builder.Services.AddSingleton(); +builder.Services.AddPapiez(); var app = builder.Build(); @@ -18,6 +18,8 @@ if (!app.Environment.IsDevelopment()) app.UseHttpsRedirection(); +app.UsePapiez(); + app.UseStaticFiles(); app.UseRouting(); From 962f4c15a14dc74233d1ab3c7a0d5bbc77271e55 Mon Sep 17 00:00:00 2001 From: femsci Date: Sat, 18 Nov 2023 01:38:23 +0100 Subject: [PATCH 05/12] =?UTF-8?q?Actual=20state=20of=20Rust=E2=84=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/umogen-cli/src/main.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/umogen-cli/src/main.rs b/src/umogen-cli/src/main.rs index df2d312..3a631fe 100644 --- a/src/umogen-cli/src/main.rs +++ b/src/umogen-cli/src/main.rs @@ -1,3 +1,4 @@ fn main() { println!("Hello, meowmeow~"); + unimplemented!("soon™"); } From 60c896acd1e8acb6949565ac868c2b866eccf2fc Mon Sep 17 00:00:00 2001 From: femsci Date: Sat, 18 Nov 2023 04:50:31 +0100 Subject: [PATCH 06/12] current state of !stuff! --- .../Models/DocSerializable.cs | 6 +++ src/Nyanlabs.Umogen.Core/Models/IEntity.cs | 2 +- .../Models/LegalEntity.cs | 7 ++- .../Models/NLSerializable.cs | 2 +- src/Nyanlabs.Umogen.Core/Models/Person.cs | 7 ++- .../Models/UmoDocument.cs | 17 ++++++- .../Models/UmoDocumentResult.cs | 49 +++++++++++++++++++ src/Nyanlabs.Umogen.Core/Models/ValidTime.cs | 14 +++++- src/Nyanlabs.Umogen.Core/Pdf/PdfRenderer.cs | 6 +++ src/Nyanlabs.Umogen.Core/SseConsumer.cs | 4 -- src/Nyanlabs.Umogen.Core/Umogen.cs | 2 +- .../Shared/MainLayout.razor | 3 ++ .../Shared/MainLayout.razor.css | 19 ++++++- src/Nyanlabs.Umogen.Server/Shared/Navi.razor | 3 ++ .../Shared/Navi.razor.css | 3 ++ .../wwwroot/css/site.css | 19 +++++++ .../NLSerializationTests.cs | 14 +++--- .../GenerationTests.cs | 8 +-- 18 files changed, 156 insertions(+), 29 deletions(-) create mode 100644 src/Nyanlabs.Umogen.Core/Models/DocSerializable.cs create mode 100644 src/Nyanlabs.Umogen.Core/Models/UmoDocumentResult.cs create mode 100644 src/Nyanlabs.Umogen.Core/Pdf/PdfRenderer.cs create mode 100644 src/Nyanlabs.Umogen.Server/Shared/Navi.razor create mode 100644 src/Nyanlabs.Umogen.Server/Shared/Navi.razor.css diff --git a/src/Nyanlabs.Umogen.Core/Models/DocSerializable.cs b/src/Nyanlabs.Umogen.Core/Models/DocSerializable.cs new file mode 100644 index 0000000..0cf8b43 --- /dev/null +++ b/src/Nyanlabs.Umogen.Core/Models/DocSerializable.cs @@ -0,0 +1,6 @@ +namespace Nyanlabs.Umogen.Core.Models; + +public interface IDocSerializable +{ + public string SerializeToDocument(); +} diff --git a/src/Nyanlabs.Umogen.Core/Models/IEntity.cs b/src/Nyanlabs.Umogen.Core/Models/IEntity.cs index 0c11c94..c38bdcf 100644 --- a/src/Nyanlabs.Umogen.Core/Models/IEntity.cs +++ b/src/Nyanlabs.Umogen.Core/Models/IEntity.cs @@ -1,6 +1,6 @@ namespace Nyanlabs.Umogen.Core.Models; -public interface IEntity : INLSerializable +public interface IEntity : INLSerializable, IDocSerializable { public string GetName(); public string? IdCode(); diff --git a/src/Nyanlabs.Umogen.Core/Models/LegalEntity.cs b/src/Nyanlabs.Umogen.Core/Models/LegalEntity.cs index 027c4ea..10180f2 100644 --- a/src/Nyanlabs.Umogen.Core/Models/LegalEntity.cs +++ b/src/Nyanlabs.Umogen.Core/Models/LegalEntity.cs @@ -17,8 +17,13 @@ public class LegalEntity(string Name, string? Nip = null, string? Headquarters = return Nip; } - public string NLSerialize() + public string NLQuerySerialize() { return $"{Name}; with NIP {Nip ?? "unknown"}{(Headquarters != null ? $"; located in {Headquarters}" : "")}{(Representative != null ? $"; represented by {Representative.GetName()}" : "")}"; } + + public string SerializeToDocument() + { + return $"{Name}, reprezentowaną przez {Representative.GetName()}"; + } } diff --git a/src/Nyanlabs.Umogen.Core/Models/NLSerializable.cs b/src/Nyanlabs.Umogen.Core/Models/NLSerializable.cs index 92adccc..6967c40 100644 --- a/src/Nyanlabs.Umogen.Core/Models/NLSerializable.cs +++ b/src/Nyanlabs.Umogen.Core/Models/NLSerializable.cs @@ -2,5 +2,5 @@ namespace Nyanlabs.Umogen.Core.Models; public interface INLSerializable { - public string NLSerialize(); + public string NLQuerySerialize(); } diff --git a/src/Nyanlabs.Umogen.Core/Models/Person.cs b/src/Nyanlabs.Umogen.Core/Models/Person.cs index 343d09d..4804b1f 100644 --- a/src/Nyanlabs.Umogen.Core/Models/Person.cs +++ b/src/Nyanlabs.Umogen.Core/Models/Person.cs @@ -18,8 +18,13 @@ public record Person(string Name, string Surname, string? Id = null, string? Pes return IDCode!; } - public string NLSerialize() + public string NLQuerySerialize() { return $"{Name} {Surname}; {(Pesel != null ? $"PESEL {Pesel}" : "unknown PESEL")}; {(IDCode != null ? $"ID series {IDCode}" : "unknown ID series")}; {(DoB != null ? $"born {DoB.Value:dd MMM yyyy}" : "unknown date of birth")}"; } + + public string SerializeToDocument() + { + return $"{Name} {Surname}{(Pesel != null ? $" PESEL {Pesel}" : "")}{(IDCode != null ? $" legitymującą się dowodem {IDCode}" : "")}{(DoB != null ? $" ur. {DoB.Value:dd MMM yyyy}" : "")}"; + } } diff --git a/src/Nyanlabs.Umogen.Core/Models/UmoDocument.cs b/src/Nyanlabs.Umogen.Core/Models/UmoDocument.cs index 0ad4733..8ff2884 100644 --- a/src/Nyanlabs.Umogen.Core/Models/UmoDocument.cs +++ b/src/Nyanlabs.Umogen.Core/Models/UmoDocument.cs @@ -1,3 +1,5 @@ +using System.Data; + namespace Nyanlabs.Umogen.Core.Models; public enum UmoDoctype @@ -16,6 +18,17 @@ public static class UmoDoctypeBindings { UmoDoctype.CONTR_ASS, ("Contract for Assignment", "Umowa o Dzieło") } }; + public static string TemplateDoc(this UmoDoctype t) + { + return t switch + { + UmoDoctype.CONTR_EMPLOYMENT => "uop.fodt", + UmoDoctype.CONTR_SERVICE => "uz.fodt", + UmoDoctype.CONTR_ASS => "ud.fodt", + _ => throw new DataException("invalid"), + }; + } + public static (string, string) Name(this UmoDoctype t) { return s_umoass[t]; @@ -43,8 +56,8 @@ public class UmoDocument : INLSerializable public Person Employee { get; set; } //Null is 'unspecified time' public ValidTime? ValidTime { get; set; } - public string NLSerialize() + public string NLQuerySerialize() { - return $"{Doctype.Name().Item2} for {Employee.NLSerialize()} working for {Employer.NLSerialize()}; valid {ValidTime?.NLSerialize() ?? "for unspecified time"}."; + return $"{Doctype.Name().Item2} for {Employee.NLQuerySerialize()} working for {Employer.NLQuerySerialize()}; valid {ValidTime?.NLQuerySerialize() ?? "for unspecified time"}."; } } diff --git a/src/Nyanlabs.Umogen.Core/Models/UmoDocumentResult.cs b/src/Nyanlabs.Umogen.Core/Models/UmoDocumentResult.cs new file mode 100644 index 0000000..0cab7e1 --- /dev/null +++ b/src/Nyanlabs.Umogen.Core/Models/UmoDocumentResult.cs @@ -0,0 +1,49 @@ +using System.Data; +using System.Diagnostics; + +namespace Nyanlabs.Umogen.Core.Models; + +public class UmoDocumentResult(UmoDoctype type, Person employee, IEntity employer, ValidTime? validity, string? content) +{ + public UmoDoctype DocType { get; } = type; + public Person Employee { get; } = employee; + public IEntity Employer { get; } = employer; + public ValidTime? Validity { get; } = validity; + public string? Content { get; } = content; + + const string ISSUE_DATE = "$DATETIME_ISSUE", + ENACT_DATE = "$DATETIME", + EMPLOYER = "$EMPLOYER", + EMPLOYEE = "$EMPLOYEE", + VALID_TIME = "$VALIDITY_TIME", + WORK_TYPE = "$WORK_TYPE", + DYNAMIC_DATA = "$WORK_DYNAMIC_DATA", + START_DATE = "$WORK_START_DATE"; + + public async Task ProcessPdf() + { + string xml = await File.ReadAllTextAsync(DocType.TemplateDoc()); + xml = xml.Replace(EMPLOYER, Employer.SerializeToDocument()) + .Replace(EMPLOYEE, Employee.SerializeToDocument()) + .Replace(VALID_TIME, (Validity ?? ValidTime.Invalid).SerializeToDocument()) + .Replace(ENACT_DATE, DateTime.Now.ToString("dd.MM.yyyy")) + .Replace(ISSUE_DATE, DateTime.Now.ToString("dd.MM.yyyy")) + .Replace(DYNAMIC_DATA, Content); + string tmp = Path.GetTempFileName(); + await File.WriteAllTextAsync(tmp, xml); + + await Task.Run(async () => + { + var proc = Process.Start("soffice", $"--headless --convert-to pdf {tmp}"); + await proc.WaitForExitAsync(); + if (proc.ExitCode != 0) + { + throw new DataException("invaliddddd"); + } + }); + + byte[] data = await File.ReadAllBytesAsync(Path.ChangeExtension(tmp, "pdf")); + + return data; + } +} diff --git a/src/Nyanlabs.Umogen.Core/Models/ValidTime.cs b/src/Nyanlabs.Umogen.Core/Models/ValidTime.cs index b43974b..044b961 100644 --- a/src/Nyanlabs.Umogen.Core/Models/ValidTime.cs +++ b/src/Nyanlabs.Umogen.Core/Models/ValidTime.cs @@ -1,6 +1,6 @@ namespace Nyanlabs.Umogen.Core.Models; -public readonly struct ValidTime : INLSerializable +public readonly struct ValidTime : INLSerializable, IDocSerializable { public ValidTime(DateTime start, DateTime end) { @@ -14,9 +14,19 @@ public readonly struct ValidTime : INLSerializable End = start + duration; } + public static readonly ValidTime Invalid = new(DateTime.MaxValue, DateTime.MinValue); + public readonly DateTime Start { get; } public readonly DateTime End { get; } public readonly TimeSpan Duration => End - Start; - public string NLSerialize() => $"from {Start:dd MMM yyyy} until {End:dd MMM yyyy}"; + public string NLQuerySerialize() + { + return this.Start != DateTime.MaxValue ? $"from {Start:dd MMM yyyy} until {End:dd MMM yyyy}" : "for unspecified time"; + } + + public string SerializeToDocument() + { + return this.Start != DateTime.MaxValue ? $"na czas od {Start:dd MMM yyyy} do {End:dd MMM yyyy}" : "na czas nieokreślony"; + } } diff --git a/src/Nyanlabs.Umogen.Core/Pdf/PdfRenderer.cs b/src/Nyanlabs.Umogen.Core/Pdf/PdfRenderer.cs new file mode 100644 index 0000000..d4220ed --- /dev/null +++ b/src/Nyanlabs.Umogen.Core/Pdf/PdfRenderer.cs @@ -0,0 +1,6 @@ +namespace Nyanlabs.Umogen.Core.Pdf; + +public class PdfRenderer +{ + +} diff --git a/src/Nyanlabs.Umogen.Core/SseConsumer.cs b/src/Nyanlabs.Umogen.Core/SseConsumer.cs index 80c7558..a07ba05 100644 --- a/src/Nyanlabs.Umogen.Core/SseConsumer.cs +++ b/src/Nyanlabs.Umogen.Core/SseConsumer.cs @@ -1,9 +1,5 @@ -using System; -using System.Collections.Generic; using System.Data; -using System.Linq; using System.Text.Json; -using System.Threading.Tasks; namespace Nyanlabs.Umogen.Core; diff --git a/src/Nyanlabs.Umogen.Core/Umogen.cs b/src/Nyanlabs.Umogen.Core/Umogen.cs index 6331e4f..b2ec03f 100644 --- a/src/Nyanlabs.Umogen.Core/Umogen.cs +++ b/src/Nyanlabs.Umogen.Core/Umogen.cs @@ -5,7 +5,7 @@ namespace Nyanlabs.Umogen.Core; public class Umogen { public const string DEFAULT_API_KEY_FILE = "umogenkey.secret"; - public const string PROMPT = "You write Polish legal contracts in Polish language in markdown format. Fill unknown fields with signature placeholder (underscore) with enough space to sign it with pen."; + public const string PROMPT = "You analyze employment prompt requests and return document type (one of these 'Umowa o Pracę', 'Umowa Zlecenie', 'Umowa o Dzieło') and elaborate description of job details as a list separated by hyphens and newlines. You write them in format: '\n'"; public static readonly JsonSerializerOptions JSON_OPTS = new(JsonSerializerDefaults.Web) { PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower diff --git a/src/Nyanlabs.Umogen.Server/Shared/MainLayout.razor b/src/Nyanlabs.Umogen.Server/Shared/MainLayout.razor index af0239c..0b0e4ab 100644 --- a/src/Nyanlabs.Umogen.Server/Shared/MainLayout.razor +++ b/src/Nyanlabs.Umogen.Server/Shared/MainLayout.razor @@ -2,6 +2,9 @@ umogen +
diff --git a/src/Nyanlabs.Umogen.Server/Shared/MainLayout.razor.css b/src/Nyanlabs.Umogen.Server/Shared/MainLayout.razor.css index f539b54..627a238 100644 --- a/src/Nyanlabs.Umogen.Server/Shared/MainLayout.razor.css +++ b/src/Nyanlabs.Umogen.Server/Shared/MainLayout.razor.css @@ -8,8 +8,23 @@ main { flex: 1; } -.sidebar { - background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%); +.nav-side { + color: #fefefe; + display: flex; + position: sticky; + top: 0; + left: 0; + margin: 0; + height: 100%; + width: 12rem; + background-image: linear-gradient( + 180deg, + rgb(10, 140, 173) 0%, + #198342 25%, + #fe00fe 50%, + #198342 75%, + rgb(10, 140, 173) 100% + ); } .top-row { diff --git a/src/Nyanlabs.Umogen.Server/Shared/Navi.razor b/src/Nyanlabs.Umogen.Server/Shared/Navi.razor new file mode 100644 index 0000000..ba94b6d --- /dev/null +++ b/src/Nyanlabs.Umogen.Server/Shared/Navi.razor @@ -0,0 +1,3 @@ +
+

umogen

+
diff --git a/src/Nyanlabs.Umogen.Server/Shared/Navi.razor.css b/src/Nyanlabs.Umogen.Server/Shared/Navi.razor.css new file mode 100644 index 0000000..9b01f19 --- /dev/null +++ b/src/Nyanlabs.Umogen.Server/Shared/Navi.razor.css @@ -0,0 +1,3 @@ +h2 { + text-align: center; +} diff --git a/src/Nyanlabs.Umogen.Server/wwwroot/css/site.css b/src/Nyanlabs.Umogen.Server/wwwroot/css/site.css index 78abcbd..5f9929a 100644 --- a/src/Nyanlabs.Umogen.Server/wwwroot/css/site.css +++ b/src/Nyanlabs.Umogen.Server/wwwroot/css/site.css @@ -1,6 +1,19 @@ html, body { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + position: relative; + width: 100%; + height: 100%; + margin: 0; +} + +footer, +header, +hgroup, +menu, +nav, +section { + display: block; } h1:focus { @@ -30,6 +43,12 @@ a, padding-top: 1.1rem; } +.container { + position: relative; + width: 100%; + height: 100%; +} + .valid.modified:not([type="checkbox"]) { outline: 1px solid #26b050; } diff --git a/test/Nyanlabs.Umogen.CoreTests/NLSerializationTests.cs b/test/Nyanlabs.Umogen.CoreTests/NLSerializationTests.cs index 46d23f8..43da2cd 100644 --- a/test/Nyanlabs.Umogen.CoreTests/NLSerializationTests.cs +++ b/test/Nyanlabs.Umogen.CoreTests/NLSerializationTests.cs @@ -15,10 +15,10 @@ public class NLSerializationTests UmoDocument doc = new(UmoDoctype.CONTR_EMPLOYMENT, ModelEmployer, ModelEmployee); // When - var query = doc.NLSerialize(); + var query = doc.NLQuerySerialize(); // Then - Assert.Equal($"Umowa o Pracę for {ModelEmployee.NLSerialize()} working for {ModelEmployer.NLSerialize()}; valid for unspecified time.", query); + Assert.Equal($"Umowa o Pracę for {ModelEmployee.NLQuerySerialize()} working for {ModelEmployer.NLQuerySerialize()}; valid for unspecified time.", query); } [Fact] @@ -28,10 +28,10 @@ public class NLSerializationTests UmoDocument doc = new(UmoDoctype.CONTR_EMPLOYMENT, ModelEmployer, ModelEmployee, new(new DateTime(2023, 11, 17), TimeSpan.FromDays(1))); // When - var query = doc.NLSerialize(); + var query = doc.NLQuerySerialize(); // Then - Assert.Equal($"Umowa o Pracę for {ModelEmployee.NLSerialize()} working for {ModelEmployer.NLSerialize()}; valid from 17 Nov 2023 until 18 Nov 2023.", query); + Assert.Equal($"Umowa o Pracę for {ModelEmployee.NLQuerySerialize()} working for {ModelEmployer.NLQuerySerialize()}; valid from 17 Nov 2023 until 18 Nov 2023.", query); } [Fact] @@ -41,7 +41,7 @@ public class NLSerializationTests Person p = ModelEmployer; // When - var query = p.NLSerialize(); + var query = p.NLQuerySerialize(); // Then Assert.Equal("Kotek Miauczyński; PESEL 04281308999; ID series CBS4327563; born 13 Aug 2004", query); @@ -59,7 +59,7 @@ public class NLSerializationTests }; // When - var query = p.NLSerialize(); + var query = p.NLQuerySerialize(); // Then Assert.Equal("Kotek Miauczyński; unknown PESEL; unknown ID series; unknown date of birth", query); @@ -74,7 +74,7 @@ public class NLSerializationTests LegalEntity company = ModelCompany; // When - var query = company.NLSerialize(); + var query = company.NLQuerySerialize(); // Then Assert.Equal("Nyanbyte P.S.A.; with NIP 1313131313; located in Miaumiaśna 13, Miauczki; represented by Kotek Miauczyński", query); diff --git a/test/Nyanlabs.Umogen.WebTests/GenerationTests.cs b/test/Nyanlabs.Umogen.WebTests/GenerationTests.cs index 72fe9d8..3448585 100644 --- a/test/Nyanlabs.Umogen.WebTests/GenerationTests.cs +++ b/test/Nyanlabs.Umogen.WebTests/GenerationTests.cs @@ -1,8 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - using Nyanlabs.Umogen.Core; using Nyanlabs.Umogen.Core.Models; @@ -26,12 +21,11 @@ public class GenerationTests UmoDocument doc = new(UmoDoctype.CONTR_EMPLOYMENT, ModelEmployer, ModelEmployee); UmoProcess proc = new(eng); - var enu = proc.Ask(doc.NLSerialize()); + var enu = proc.Ask("I would like to employ Jan Kowalski ID NO CBS3727348 for part time job as a shopkeeper."); await foreach (var str in enu) { Console.Write(str); - File.AppendAllText("/tmp/doc.md", str); } Console.WriteLine("~"); From 24ba0f63147ac4b3b5798e464f7bfc2fb7f7a62b Mon Sep 17 00:00:00 2001 From: femsci Date: Sat, 18 Nov 2023 05:59:32 +0100 Subject: [PATCH 07/12] absolutely horrendous --- src/Nyanlabs.Umogen.Core/Models/Person.cs | 2 +- .../Models/UmoAnalModel.cs | 21 +++++++++ .../Models/UmoDocumentResult.cs | 25 ++++++---- src/Nyanlabs.Umogen.Core/Pdf/PdfRenderer.cs | 6 --- src/Nyanlabs.Umogen.Core/Umogen.cs | 2 +- .../GenerationTests.cs | 13 ++--- test/Nyanlabs.Umogen.WebTests/PdfTest.cs | 47 +++++++++++++++++++ 7 files changed, 92 insertions(+), 24 deletions(-) create mode 100644 src/Nyanlabs.Umogen.Core/Models/UmoAnalModel.cs delete mode 100644 src/Nyanlabs.Umogen.Core/Pdf/PdfRenderer.cs create mode 100644 test/Nyanlabs.Umogen.WebTests/PdfTest.cs diff --git a/src/Nyanlabs.Umogen.Core/Models/Person.cs b/src/Nyanlabs.Umogen.Core/Models/Person.cs index 4804b1f..b833009 100644 --- a/src/Nyanlabs.Umogen.Core/Models/Person.cs +++ b/src/Nyanlabs.Umogen.Core/Models/Person.cs @@ -25,6 +25,6 @@ public record Person(string Name, string Surname, string? Id = null, string? Pes public string SerializeToDocument() { - return $"{Name} {Surname}{(Pesel != null ? $" PESEL {Pesel}" : "")}{(IDCode != null ? $" legitymującą się dowodem {IDCode}" : "")}{(DoB != null ? $" ur. {DoB.Value:dd MMM yyyy}" : "")}"; + return $"{Name} {Surname}{(Pesel != null ? $" PESEL {Pesel}" : "")}{(IDCode != null ? $" legitymując{(Pesel == null ? 'e' : (((int.Parse(Pesel[^2].ToString()) & 0x1) == 0x1) ? 'y' : 'a'))} się dowodem {IDCode}" : "")}"; } } diff --git a/src/Nyanlabs.Umogen.Core/Models/UmoAnalModel.cs b/src/Nyanlabs.Umogen.Core/Models/UmoAnalModel.cs new file mode 100644 index 0000000..49ce09c --- /dev/null +++ b/src/Nyanlabs.Umogen.Core/Models/UmoAnalModel.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json.Serialization; +using System.Threading.Tasks; + +namespace Nyanlabs.Umogen.Core.Models; + +public class UmoAnalModel +{ + [JsonPropertyName("c_type")] + public string ContractType { get; set; } + [JsonPropertyName("j_pos")] + public string JobPosition { get; set; } + [JsonPropertyName("k_lit")] + public string ValueLiteral { get; set; } + [JsonPropertyName("conds")] + public ICollection Conditions { get; set; } + + public UmoDoctype Doctype => UmoDoctypeBindings.GetFromName(ContractType).Value; +} diff --git a/src/Nyanlabs.Umogen.Core/Models/UmoDocumentResult.cs b/src/Nyanlabs.Umogen.Core/Models/UmoDocumentResult.cs index 0cab7e1..0def101 100644 --- a/src/Nyanlabs.Umogen.Core/Models/UmoDocumentResult.cs +++ b/src/Nyanlabs.Umogen.Core/Models/UmoDocumentResult.cs @@ -3,13 +3,14 @@ using System.Diagnostics; namespace Nyanlabs.Umogen.Core.Models; -public class UmoDocumentResult(UmoDoctype type, Person employee, IEntity employer, ValidTime? validity, string? content) +public class UmoDocumentResult(UmoDoctype? type, Person employee, IEntity employer, ValidTime? validity, decimal? payment, UmoAnalModel anal) { - public UmoDoctype DocType { get; } = type; + public UmoDoctype DocType { get; set; } = type ?? anal.Doctype; public Person Employee { get; } = employee; public IEntity Employer { get; } = employer; public ValidTime? Validity { get; } = validity; - public string? Content { get; } = content; + public decimal? Payment { get; } = payment; + public UmoAnalModel Anal { get; } = anal; const string ISSUE_DATE = "$DATETIME_ISSUE", ENACT_DATE = "$DATETIME", @@ -17,24 +18,29 @@ public class UmoDocumentResult(UmoDoctype type, Person employee, IEntity employe EMPLOYEE = "$EMPLOYEE", VALID_TIME = "$VALIDITY_TIME", WORK_TYPE = "$WORK_TYPE", - DYNAMIC_DATA = "$WORK_DYNAMIC_DATA", + DYNAMIC_DATA = "$$WORK_DYNAMIC_DATA$$", + PAYMENT = "$PAYMENT", + PAYMENT_LIT = "$PAYMENT_LIT", START_DATE = "$WORK_START_DATE"; - public async Task ProcessPdf() + public async Task ProcessPdf(string? template = null) { - string xml = await File.ReadAllTextAsync(DocType.TemplateDoc()); + string xml = await File.ReadAllTextAsync(template ?? DocType.TemplateDoc()); xml = xml.Replace(EMPLOYER, Employer.SerializeToDocument()) .Replace(EMPLOYEE, Employee.SerializeToDocument()) .Replace(VALID_TIME, (Validity ?? ValidTime.Invalid).SerializeToDocument()) .Replace(ENACT_DATE, DateTime.Now.ToString("dd.MM.yyyy")) .Replace(ISSUE_DATE, DateTime.Now.ToString("dd.MM.yyyy")) - .Replace(DYNAMIC_DATA, Content); + .Replace(PAYMENT_LIT, Anal.ValueLiteral) + .Replace(PAYMENT, Payment.Value.ToString()) + .Replace(WORK_TYPE, Anal.JobPosition) + .Replace(DYNAMIC_DATA, string.Join('\n', Anal.Conditions.Select(a => $"- {a}"))); string tmp = Path.GetTempFileName(); await File.WriteAllTextAsync(tmp, xml); await Task.Run(async () => { - var proc = Process.Start("soffice", $"--headless --convert-to pdf {tmp}"); + var proc = Process.Start("soffice", $"--headless --convert-to pdf --outdir /tmp/ {tmp}"); await proc.WaitForExitAsync(); if (proc.ExitCode != 0) { @@ -44,6 +50,9 @@ public class UmoDocumentResult(UmoDoctype type, Person employee, IEntity employe byte[] data = await File.ReadAllBytesAsync(Path.ChangeExtension(tmp, "pdf")); + File.Delete(tmp); + File.Delete(Path.ChangeExtension(tmp, "pdf")); + return data; } } diff --git a/src/Nyanlabs.Umogen.Core/Pdf/PdfRenderer.cs b/src/Nyanlabs.Umogen.Core/Pdf/PdfRenderer.cs deleted file mode 100644 index d4220ed..0000000 --- a/src/Nyanlabs.Umogen.Core/Pdf/PdfRenderer.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Nyanlabs.Umogen.Core.Pdf; - -public class PdfRenderer -{ - -} diff --git a/src/Nyanlabs.Umogen.Core/Umogen.cs b/src/Nyanlabs.Umogen.Core/Umogen.cs index b2ec03f..f8471c6 100644 --- a/src/Nyanlabs.Umogen.Core/Umogen.cs +++ b/src/Nyanlabs.Umogen.Core/Umogen.cs @@ -5,7 +5,7 @@ namespace Nyanlabs.Umogen.Core; public class Umogen { public const string DEFAULT_API_KEY_FILE = "umogenkey.secret"; - public const string PROMPT = "You analyze employment prompt requests and return document type (one of these 'Umowa o Pracę', 'Umowa Zlecenie', 'Umowa o Dzieło') and elaborate description of job details as a list separated by hyphens and newlines. You write them in format: '\n'"; + public const string PROMPT = "You analyze employment prompt requests and return document type (one of these 'Umowa o Pracę', 'Umowa Zlecenie', 'Umowa o Dzieło') and elaborate description of job details as a list separated by hyphens and newlines. Also give a very generalized job position. You write them in format: '{ \"c_type\": \"\", \"j_pos\": \"\", \"k_lit\": \"\", \"conds\": [ ]'"; public static readonly JsonSerializerOptions JSON_OPTS = new(JsonSerializerDefaults.Web) { PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower diff --git a/test/Nyanlabs.Umogen.WebTests/GenerationTests.cs b/test/Nyanlabs.Umogen.WebTests/GenerationTests.cs index 3448585..eed01b6 100644 --- a/test/Nyanlabs.Umogen.WebTests/GenerationTests.cs +++ b/test/Nyanlabs.Umogen.WebTests/GenerationTests.cs @@ -21,14 +21,11 @@ public class GenerationTests UmoDocument doc = new(UmoDoctype.CONTR_EMPLOYMENT, ModelEmployer, ModelEmployee); UmoProcess proc = new(eng); - var enu = proc.Ask("I would like to employ Jan Kowalski ID NO CBS3727348 for part time job as a shopkeeper."); - - await foreach (var str in enu) - { - Console.Write(str); - } - - Console.WriteLine("~"); + // var enu = proc.Ask("I would like to employ Jan Kowalski ID NO CBS3727348 for part time job as a shopkeeper and pay 1400zł."); + // await foreach (var str in enu) + // { + // Console.Write(str); + // } } } diff --git a/test/Nyanlabs.Umogen.WebTests/PdfTest.cs b/test/Nyanlabs.Umogen.WebTests/PdfTest.cs new file mode 100644 index 0000000..7987c31 --- /dev/null +++ b/test/Nyanlabs.Umogen.WebTests/PdfTest.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; + +using Nyanlabs.Umogen.Core; +using Nyanlabs.Umogen.Core.Models; + +namespace Nyanlabs.Umogen.CoreTests; + +public class PdfTest +{ + + private Person ModelEmployer => new("Kotek", "Miauczyński", "CBS4327563", "04281308999", new DateTime(2004, 08, 13)); + + private Person ModelEmployee => new("Miau", "Kotczyński", "CBS69696969", "76011694336", new DateTime(1976, 1, 16)); + + [Fact] + public async void RenderBoi() + { + // Given + string root = Environment.CurrentDirectory.Replace("test/Nyanlabs.Umogen.WebTests/bin/Debug/net8.0", ""); + UmoEngine eng = new(Path.Combine(root, Core.Umogen.DEFAULT_API_KEY_FILE)); + + UmoProcess p = new(eng); + + StringBuilder sb = new(); + + await foreach (var str in p.Ask($"Zatrudnij {ModelEmployee.NLQuerySerialize()} jako programistę z wynagrodzeniem 12137zł.")) + { + Console.Write(str); + sb.Append(str); + } + + var anal = JsonSerializer.Deserialize(sb.ToString(), Core.Umogen.JSON_OPTS); + UmoDocumentResult r = new(null, ModelEmployer, ModelEmployee, ValidTime.Invalid, 2137, anal); + + // When + byte[] b = await r.ProcessPdf(Path.Combine(root, $"templates/{r.DocType.TemplateDoc()}")); + // Then + + await File.WriteAllBytesAsync(Path.Combine(root, "out.pdf"), b); + + } +} From 01c1476b02596b9afb23691800d258a8897a1fe8 Mon Sep 17 00:00:00 2001 From: femsci Date: Sat, 18 Nov 2023 07:20:02 +0100 Subject: [PATCH 08/12] templates --- templates/ud.fodt | 399 +++++++++++++++++++++++++++++++++ templates/uop.fodt | 484 ++++++++++++++++++++++++++++++++++++++++ templates/uz.fodt | 542 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1425 insertions(+) create mode 100644 templates/ud.fodt create mode 100644 templates/uop.fodt create mode 100644 templates/uz.fodt diff --git a/templates/ud.fodt b/templates/ud.fodt new file mode 100644 index 0000000..10ca920 --- /dev/null +++ b/templates/ud.fodt @@ -0,0 +1,399 @@ + + + + 2023-11-18T05:26:04.042661348PT8M1LibreOffice/7.6.2.1$Linux_X86_64 LibreOffice_project/60$Build-1 + + + 1411 + 0 + 28577 + 12129 + true + false + + + view2 + 6435 + 20433 + 0 + 1411 + 28575 + 13538 + 0 + 1 + false + 120 + false + false + false + false + false + false + + + + + true + false + true + false + true + false + false + 0 + true + false + true + true + false + false + true + true + false + true + + true + true + true + true + false + true + true + false + false + true + true + true + true + true + false + false + 2046085 + false + + true + false + false + 2046085 + true + false + false + false + 1 + true + true + false + false + true + false + false + true + false + true + false + true + 0 + + false + true + true + true + + 0 + false + false + false + false + high-resolution + true + true + true + true + true + false + false + false + false + true + + true + true + true + true + + false + false + true + true + true + true + true + true + true + false + false + false + false + false + 0 + true + false + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + UMOWA O DZIEŁO + + Umowa zawarta w dniu $DATETIME pomiędzy: + $EMPLOYER + zwanym dalej Zamawiającym, + + a + $EMPLOYEE + zwanym dalej Wykonawcą + + o następującej treści: + + § 1 + Zamawiający powierza wykonanie, a Wykonawca zobowiązuje się wykonać dzieło polegające na: + $$WORK_DYNAMIC_DATA$$ + + + § 2 + Wykonawcy przysługuje wynagrodzenie za wykonanie dzieła w wysokości $PAYMENT zł (słownie: $PAYMENT_LIT złotych). Zamawiający wypłaci Wykonawcy wynagrodzenie w formie przelewu/gotówki w dniu odbioru wykonanego dzieła, na podstawie wystawionego przez Wykonawcę rachunku. + + § 3 + Zmiany umowy wymagają formy pisemnej pod rygorem nieważności. + + § 4 + W sprawach nieuregulowanych niniejszą umową będą miały zastosowanie przepisy kodeksu cywilnego. + + § 5 + Umowę sporządzono w 2 jednobrzmiących egzemplarzach, po 1 dla każdej ze stron. + + + ............................................ …………………………… + Zamawiający Wykonawca + + + + \ No newline at end of file diff --git a/templates/uop.fodt b/templates/uop.fodt new file mode 100644 index 0000000..f93f65a --- /dev/null +++ b/templates/uop.fodt @@ -0,0 +1,484 @@ + + + + 2019-07-12T13:34:33pl-PL2023-11-18T05:58:58.12297407711PT1H8SLibreOffice/7.6.2.1$Linux_X86_64 LibreOffice_project/60$Build-1 + + + 11906 + 0 + 21433 + 8749 + true + false + + + view2 + 7722 + 12328 + 0 + 11906 + 21431 + 20653 + 0 + 1 + false + 160 + false + false + false + false + false + false + + + + + true + false + true + false + true + false + false + 0 + true + false + true + true + false + false + true + true + false + true + + true + true + true + true + true + true + true + false + false + true + true + true + true + true + false + false + 284906 + false + + true + true + false + 374185 + true + false + false + false + 1 + true + true + false + false + true + false + false + true + false + true + false + true + 0 + + false + true + true + true + + 0 + false + false + false + false + high-resolution + true + true + true + true + true + true + false + false + false + true + + true + true + true + true + + false + false + true + true + true + true + true + true + false + false + false + false + false + false + 0 + true + false + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $DATETIME + + UMOWA O PRACĘ + + + zawarta w dniu $DATETIME + + między + $EMPLOYER + + a + + $EMPLOYEE + + na $VALIDITY_TIME. + + + 1. Strony ustalają następujące warunki zatrudnienia: + a) rodzaj pracy $WORK_TYPE + b) stawkę wynagrodzenia miesięcznego wynoszącą $PAYMENT zł (słownie $PAYMENT_LIT złotych) + c) $$WORK_DYNAMIC_DATA$$ + + + + + …………………………………. .....………………………………………. + (data i podpis pracownika) (podpis pracodawcy lub osoby reprezentującej + pracodawcę albo osoby upoważnionej do składania + oświadczeń w imieniu pracodawcy) + + + + + + + \ No newline at end of file diff --git a/templates/uz.fodt b/templates/uz.fodt new file mode 100644 index 0000000..173684f --- /dev/null +++ b/templates/uz.fodt @@ -0,0 +1,542 @@ + + + + Umowa zlecenie wzór.docxdom2013-08-22T09:15:002023-11-18T05:17:47.4649548364PT15M30SLibreOffice/7.6.2.1$Linux_X86_64 LibreOffice_project/60$Build-1 + + + 3969 + 0 + 28577 + 12129 + true + false + + + view2 + 16910 + 9197 + 0 + 3969 + 28575 + 16095 + 0 + 1 + false + 120 + false + false + false + false + false + false + + + + + true + false + true + false + true + false + false + 0 + true + false + true + true + false + true + false + true + false + true + + true + false + true + false + true + true + true + false + false + true + true + false + true + true + false + false + 1322142 + false + + true + true + + + ja + JP + + !%),.:;?]}¢°’”‰′″℃、。々〉》」』】〕゛゜ゝゞ・ヽヾ!%),.:;?]}。」、・゙゚¢ + $([\{£¥‘“〈《「『【〔$([{「£¥ + + + false + 1322142 + true + false + false + false + 1 + true + true + false + false + true + false + false + true + false + true + false + true + 0 + + false + true + true + true + + 0 + false + false + false + true + high-resolution + true + true + true + false + true + false + false + false + false + true + + true + false + true + true + + false + false + true + true + true + false + true + true + false + false + false + false + false + false + 0 + true + false + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Umowa zlecenia + + + Zawarta w dniu $DATETIME pomiędzy + + + + + $EMPLOYER zwanym dalej Zleceniodawcą + + + + a + + + $EMPLOYEE zwanym dalej Zleceniobiorcą. + + + + § 1 + + Na podstawie niniejszej umowy Zleceniodawca zleca a Zleceniobiorca zobowiązuje się do wykonania następujących czynności: $$WORK_DYNAMIC_DATA$$. + + § 2 + + 1. Za wykonanie prac określonych w § 1 Zleceniobiorca otrzyma po ich wykonaniu wynagrodzenie w wysokości $PAYMENT zł brutto, (słownie: $PAYMENT_LIT) + + 2. Wynagrodzenie płatne jest na podstawie rachunku przedstawionego przez Zleceniobiorcę. + + § 3 + + Zleceniobiorca nie może powierzyć prac wymienionych w § 1 innym osobom bez zgody Zleceniodawcy. + + + § 4 + Umowa została zawarta $VALIDITY_TIME. + § 5 + W sprawach nieuregulowanych niniejszą umową mają zastosowanie przepisy Kodeksu Cywilnego. + + § 6 + Umowa została sporządzona w 2 jednobrzmiących egzemplarzach – po 1 dla każdej ze stron. + + + + . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + (Zleceniodawca) (Zleceniobiorca) + + + + + + + \ No newline at end of file From 17ed4cdb6f78d865e4d599296c4d0d9e137ba8ec Mon Sep 17 00:00:00 2001 From: femsci Date: Sat, 18 Nov 2023 07:20:30 +0100 Subject: [PATCH 09/12] web turd --- .../Pages/Contracts.razor | 54 +++++++++++++++++++ src/Nyanlabs.Umogen.Server/Pages/People.razor | 20 +++++++ src/Nyanlabs.Umogen.Server/Pages/_Host.cshtml | 3 ++ .../Shared/MainLayout.razor | 4 +- src/Nyanlabs.Umogen.Server/Shared/Navi.razor | 25 +++++++-- .../Shared/Navi.razor.css | 2 +- .../Shared/PersonAddForm.razor | 38 +++++++++++++ src/Nyanlabs.Umogen.Server/wwwroot/penis.js | 20 +++++++ 8 files changed, 159 insertions(+), 7 deletions(-) create mode 100644 src/Nyanlabs.Umogen.Server/Pages/Contracts.razor create mode 100644 src/Nyanlabs.Umogen.Server/Pages/People.razor create mode 100644 src/Nyanlabs.Umogen.Server/Shared/PersonAddForm.razor create mode 100644 src/Nyanlabs.Umogen.Server/wwwroot/penis.js diff --git a/src/Nyanlabs.Umogen.Server/Pages/Contracts.razor b/src/Nyanlabs.Umogen.Server/Pages/Contracts.razor new file mode 100644 index 0000000..0a94e73 --- /dev/null +++ b/src/Nyanlabs.Umogen.Server/Pages/Contracts.razor @@ -0,0 +1,54 @@ +@page "/contracts" +@using Umogen.Core; +@using Umogen.Core.Models; +@inject IJSRuntime js + +Umowy + +

Umowy

+ +
+ +
+
+ +
+
+ +
+ + +
+
OUT: @output
+
+ + + +@code { + private string _prompt, _person, _value; + private string output = ""; + + private async Task Process() + { + output = string.Empty; + using UmoEngine eng = new("/home/nya/Dev/csharp/umogen/umogenkey.secret"); + UmoProcess proc = new(eng); + Thread.Yield(); + await foreach (var str in proc.Ask(_prompt)) + { + output += str; + Console.Write(str); + this.StateHasChanged(); + } + + var anal = System.Text.Json.JsonSerializer.Deserialize(output, Umogen.JSON_OPTS); + UmoDocumentResult res = new(null, new Person("Kotek", "Miauczyński", "CBS4327563", "04281308999", new DateTime(2004, 08, + 13)), + new Person("Kotek", "Miauczyński", "CBS4327563", "04281308999", new DateTime(2004, 08, 13)), ValidTime.Invalid, 2137, + anal); + + var bytes = await res.ProcessPdf(Path.Combine("/home/nya/Dev/csharp/umogen/", + $"templates/{anal.Doctype.TemplateDoc()}")); + await js.InvokeVoidAsync("blobby", bytes); + } +} diff --git a/src/Nyanlabs.Umogen.Server/Pages/People.razor b/src/Nyanlabs.Umogen.Server/Pages/People.razor new file mode 100644 index 0000000..acfb104 --- /dev/null +++ b/src/Nyanlabs.Umogen.Server/Pages/People.razor @@ -0,0 +1,20 @@ +@page "/people" + +Osoby + +

Osoby

+ + +@if (addVis) +{ + +} + +@code { + private bool addVis = false; + + private void Toggle() + { + addVis = !addVis; + } +} diff --git a/src/Nyanlabs.Umogen.Server/Pages/_Host.cshtml b/src/Nyanlabs.Umogen.Server/Pages/_Host.cshtml index a90635d..fa06e99 100644 --- a/src/Nyanlabs.Umogen.Server/Pages/_Host.cshtml +++ b/src/Nyanlabs.Umogen.Server/Pages/_Host.cshtml @@ -10,9 +10,12 @@ + + diff --git a/src/Nyanlabs.Umogen.Server/Shared/MainLayout.razor b/src/Nyanlabs.Umogen.Server/Shared/MainLayout.razor index 0b0e4ab..0cecf7b 100644 --- a/src/Nyanlabs.Umogen.Server/Shared/MainLayout.razor +++ b/src/Nyanlabs.Umogen.Server/Shared/MainLayout.razor @@ -2,9 +2,7 @@ umogen - +
diff --git a/src/Nyanlabs.Umogen.Server/Shared/Navi.razor b/src/Nyanlabs.Umogen.Server/Shared/Navi.razor index ba94b6d..5003a2e 100644 --- a/src/Nyanlabs.Umogen.Server/Shared/Navi.razor +++ b/src/Nyanlabs.Umogen.Server/Shared/Navi.razor @@ -1,3 +1,22 @@ -
-

umogen

-
+ diff --git a/src/Nyanlabs.Umogen.Server/Shared/Navi.razor.css b/src/Nyanlabs.Umogen.Server/Shared/Navi.razor.css index 9b01f19..c4739b4 100644 --- a/src/Nyanlabs.Umogen.Server/Shared/Navi.razor.css +++ b/src/Nyanlabs.Umogen.Server/Shared/Navi.razor.css @@ -1,3 +1,3 @@ -h2 { +h1 { text-align: center; } diff --git a/src/Nyanlabs.Umogen.Server/Shared/PersonAddForm.razor b/src/Nyanlabs.Umogen.Server/Shared/PersonAddForm.razor new file mode 100644 index 0000000..d486438 --- /dev/null +++ b/src/Nyanlabs.Umogen.Server/Shared/PersonAddForm.razor @@ -0,0 +1,38 @@ +

Dodaj osobę

+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+
+ +@code { + [Parameter] + public required Action OnSubmit { get; set; } + + private string _name = string.Empty, + _surname = string.Empty, + _pesel = string.Empty, + _id = string.Empty; + + private void Submit() + { + Core.Models.Person p = new(_name, _surname, _id, _pesel); + OnSubmit.Invoke(); + } +} diff --git a/src/Nyanlabs.Umogen.Server/wwwroot/penis.js b/src/Nyanlabs.Umogen.Server/wwwroot/penis.js new file mode 100644 index 0000000..3c7598d --- /dev/null +++ b/src/Nyanlabs.Umogen.Server/wwwroot/penis.js @@ -0,0 +1,20 @@ +//I lost ;w; + +function blobby(arr) { + const url = window.URL.createObjectURL( + new Blob([arr], { type: "application/pdf" }) + ); + + var downloadLink = document.createElement("a"); + downloadLink.target = "_blank"; + downloadLink.download = "umo.pdf"; + + // set object URL as the anchor's href + downloadLink.href = url; + + // append the anchor to document body + document.body.append(downloadLink); + + // fire a click event on the anchor + downloadLink.click(); +} From c22d5c6d82f5f23917e790994a5f8bb6023e280c Mon Sep 17 00:00:00 2001 From: femsci Date: Sat, 18 Nov 2023 11:01:11 +0100 Subject: [PATCH 10/12] the absolute state of S T U F F --- .gitignore | 2 + out.pdf | Bin 0 -> 30998 bytes .../Models/{ => Api}/ApiChoice.cs | 0 .../Models/{ => Api}/ApiRequest.cs | 0 .../Models/{ => Api}/ApiResponse.cs | 0 .../Models/{ => Api}/ApiResponseFormat.cs | 0 .../Models/{ => Api}/GptMessage.cs | 0 .../Models/LegalEntity.cs | 19 +- src/Nyanlabs.Umogen.Core/Models/Person.cs | 16 +- .../Models/UmoAnalModel.cs | 4 - src/Nyanlabs.Umogen.Core/Models/ValidTime.cs | 2 +- src/Nyanlabs.Umogen.Core/SseConsumer.cs | 1 + src/Nyanlabs.Umogen.Server/DataContext.cs | 21 + .../Nyanlabs.Umogen.Server.csproj | 2 + .../Pages/Contracts.razor | 105 +- .../Pages/Entities.razor | 97 + src/Nyanlabs.Umogen.Server/Pages/Index.razor | 12 +- src/Nyanlabs.Umogen.Server/Pages/People.razor | 20 - src/Nyanlabs.Umogen.Server/Pages/_Host.cshtml | 3 +- src/Nyanlabs.Umogen.Server/Program.cs | 10 + .../Shared/CompanyAddForm.razor | 56 + src/Nyanlabs.Umogen.Server/Shared/Navi.razor | 2 +- .../Shared/PersonAddForm.razor | 46 +- src/Nyanlabs.Umogen.Server/wwwroot/css/bs.css | 11751 ++++++++++++++++ .../wwwroot/css/site.css | 4 + src/Nyanlabs.Umogen.Server/wwwroot/penis.js | 4 - .../NLSerializationTests.cs | 24 +- test/Nyanlabs.Umogen.WebTests/PdfTest.cs | 4 - 28 files changed, 12110 insertions(+), 95 deletions(-) create mode 100644 out.pdf rename src/Nyanlabs.Umogen.Core/Models/{ => Api}/ApiChoice.cs (100%) rename src/Nyanlabs.Umogen.Core/Models/{ => Api}/ApiRequest.cs (100%) rename src/Nyanlabs.Umogen.Core/Models/{ => Api}/ApiResponse.cs (100%) rename src/Nyanlabs.Umogen.Core/Models/{ => Api}/ApiResponseFormat.cs (100%) rename src/Nyanlabs.Umogen.Core/Models/{ => Api}/GptMessage.cs (100%) create mode 100644 src/Nyanlabs.Umogen.Server/DataContext.cs create mode 100644 src/Nyanlabs.Umogen.Server/Pages/Entities.razor delete mode 100644 src/Nyanlabs.Umogen.Server/Pages/People.razor create mode 100644 src/Nyanlabs.Umogen.Server/Shared/CompanyAddForm.razor create mode 100644 src/Nyanlabs.Umogen.Server/wwwroot/css/bs.css diff --git a/.gitignore b/.gitignore index 32ab2a2..7e52515 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ **/target/ /build /umogenkey.secret +**.db +**.db-* diff --git a/out.pdf b/out.pdf new file mode 100644 index 0000000000000000000000000000000000000000..cfb3252c4a8c66f1d5bddf2d7c9fc8f4700e496f GIT binary patch literal 30998 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
&k=>Zl4lv{YrnXNeq;TfcUWW9 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 d%2+fQ)E%8n$WF8ni6kH}XyzR|l(kft{}0Jy4( Companies { get; set; } + public string GetName() { return $"{Name} {Surname}"; diff --git a/src/Nyanlabs.Umogen.Core/Models/UmoAnalModel.cs b/src/Nyanlabs.Umogen.Core/Models/UmoAnalModel.cs index 49ce09c..5240185 100644 --- a/src/Nyanlabs.Umogen.Core/Models/UmoAnalModel.cs +++ b/src/Nyanlabs.Umogen.Core/Models/UmoAnalModel.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; using System.Text.Json.Serialization; -using System.Threading.Tasks; namespace Nyanlabs.Umogen.Core.Models; diff --git a/src/Nyanlabs.Umogen.Core/Models/ValidTime.cs b/src/Nyanlabs.Umogen.Core/Models/ValidTime.cs index 044b961..81f6595 100644 --- a/src/Nyanlabs.Umogen.Core/Models/ValidTime.cs +++ b/src/Nyanlabs.Umogen.Core/Models/ValidTime.cs @@ -27,6 +27,6 @@ public readonly struct ValidTime : INLSerializable, IDocSerializable public string SerializeToDocument() { - return this.Start != DateTime.MaxValue ? $"na czas od {Start:dd MMM yyyy} do {End:dd MMM yyyy}" : "na czas nieokreślony"; + return this.Start != DateTime.MaxValue ? $"na czas od {Start:dd.MM.yyyy} do {End:dd.MM.yyyy}" : "na czas nieokreślony"; } } diff --git a/src/Nyanlabs.Umogen.Core/SseConsumer.cs b/src/Nyanlabs.Umogen.Core/SseConsumer.cs index a07ba05..079d0e4 100644 --- a/src/Nyanlabs.Umogen.Core/SseConsumer.cs +++ b/src/Nyanlabs.Umogen.Core/SseConsumer.cs @@ -27,6 +27,7 @@ public class SseConsumer : IDisposable, IAsyncDisposable yield return JsonSerializer.Deserialize(line, Umogen.JSON_OPTS) ?? throw new DataException("Invalid data..."); } + reader.Close(); } public void Dispose() diff --git a/src/Nyanlabs.Umogen.Server/DataContext.cs b/src/Nyanlabs.Umogen.Server/DataContext.cs new file mode 100644 index 0000000..c0f6c6c --- /dev/null +++ b/src/Nyanlabs.Umogen.Server/DataContext.cs @@ -0,0 +1,21 @@ +using Microsoft.EntityFrameworkCore; + +using Nyanlabs.Umogen.Core.Models; + +namespace Nyanlabs.Umogen.Server; + +public class DataContext : DbContext +{ + public DbSet Persons => Set(); + public DbSet Companies => Set(); + + protected override void OnConfiguring(DbContextOptionsBuilder o) + { + o.UseSqlite("Data Source=data.db;"); + } + + protected override void OnModelCreating(ModelBuilder m) + { + m.Entity().HasOne(e => e.Representative).WithMany(e => e.Companies); + } +} diff --git a/src/Nyanlabs.Umogen.Server/Nyanlabs.Umogen.Server.csproj b/src/Nyanlabs.Umogen.Server/Nyanlabs.Umogen.Server.csproj index 1770d66..fc5d989 100644 --- a/src/Nyanlabs.Umogen.Server/Nyanlabs.Umogen.Server.csproj +++ b/src/Nyanlabs.Umogen.Server/Nyanlabs.Umogen.Server.csproj @@ -6,6 +6,8 @@ + + diff --git a/src/Nyanlabs.Umogen.Server/Pages/Contracts.razor b/src/Nyanlabs.Umogen.Server/Pages/Contracts.razor index 0a94e73..52d9c9d 100644 --- a/src/Nyanlabs.Umogen.Server/Pages/Contracts.razor +++ b/src/Nyanlabs.Umogen.Server/Pages/Contracts.razor @@ -2,19 +2,43 @@ @using Umogen.Core; @using Umogen.Core.Models; @inject IJSRuntime js +@inject DataContext db +@using Microsoft.EntityFrameworkCore; Umowy

Umowy

-
- -
-
- -
-
- +
+
+
+
+ + + @(employer == null ? "" : $"{employer.GetName()}") +
+
+ + + @(employee == null ? "" : $"{employee.Name} {employee.Surname}") +
+
+
+
+ + +
+
+ + +
+
+
+ + +
+ +
@@ -22,19 +46,69 @@
OUT: @output
- - @code { - private string _prompt, _person, _value; + private string _employerId = "", _employeeId = ""; + private Person? employee; + private IEntity? employer; + private string _query = ""; + private ValidTime _validity; + private decimal _payment; private string output = ""; + private bool disB = false; + + private async Task ChgEmployer(ChangeEventArgs e) + { + _employerId = (string)e.Value!; + employer = await db.Persons.AsNoTracking().SingleOrDefaultAsync(s => s.Pesel == _employerId) as IEntity ?? await + db.Companies.AsNoTracking().SingleOrDefaultAsync(s => s.Nip == _employerId); + } + + private async Task ChgEmployee(ChangeEventArgs e) + { + _employeeId = (string)e.Value!; + employee = await db.Persons.AsNoTracking().SingleOrDefaultAsync(s => s.Pesel == _employeeId); + } + + private string _validityText = ""; + + private async Task ChgValidity(ChangeEventArgs e) + { + string val = (string)e.Value!; + _validityText = val; + StateHasChanged(); + + var now = DateTime.Now; + + if (!TimeSpan.TryParse(val, out TimeSpan span)) + { + _validityText = "nieokreślony"; + _validity = ValidTime.Invalid; + } + + if (span.TotalDays < 1) + { + _validityText = "nieokreślony"; + _validity = ValidTime.Invalid; + StateHasChanged(); + return; + } + + _validityText = $"{span.TotalDays} d"; + _validity = new ValidTime(now, span); + } private async Task Process() { + disB = true; + this.StateHasChanged(); + await Task.Yield(); + output = string.Empty; using UmoEngine eng = new("/home/nya/Dev/csharp/umogen/umogenkey.secret"); UmoProcess proc = new(eng); Thread.Yield(); - await foreach (var str in proc.Ask(_prompt)) + + await foreach (var str in proc.Ask(_query + $" wynagrodzenie {_payment} zł")) { output += str; Console.Write(str); @@ -42,13 +116,14 @@ } var anal = System.Text.Json.JsonSerializer.Deserialize(output, Umogen.JSON_OPTS); - UmoDocumentResult res = new(null, new Person("Kotek", "Miauczyński", "CBS4327563", "04281308999", new DateTime(2004, 08, - 13)), - new Person("Kotek", "Miauczyński", "CBS4327563", "04281308999", new DateTime(2004, 08, 13)), ValidTime.Invalid, 2137, + UmoDocumentResult res = new(null, employee, employer, _validity, _payment, anal); var bytes = await res.ProcessPdf(Path.Combine("/home/nya/Dev/csharp/umogen/", $"templates/{anal.Doctype.TemplateDoc()}")); await js.InvokeVoidAsync("blobby", bytes); + + disB = false; + this.StateHasChanged(); } } diff --git a/src/Nyanlabs.Umogen.Server/Pages/Entities.razor b/src/Nyanlabs.Umogen.Server/Pages/Entities.razor new file mode 100644 index 0000000..4cb587f --- /dev/null +++ b/src/Nyanlabs.Umogen.Server/Pages/Entities.razor @@ -0,0 +1,97 @@ +@page "/entities" +@inject DataContext db +@using Microsoft.EntityFrameworkCore + +Podmioty + +

Podmioty

+ + + +@if (_mode == 1) +{ +
+ +
+} + +
+ + + + + + + + + + + @foreach (var person in db.Persons.AsNoTracking().ToList()) + { + + + + + + + } + +
IdImięPESELSeria dowodu
@person.Id@person.Name @person.Surname@person.Pesel@person.IdC
+
+ + + +@if (_mode == 2) +{ +
+ +
+} + +
+ + + + + + + + + + + + @foreach (var company in db.Companies.AsNoTracking().Include(c => c.Representative).ToList()) + { + + + + + + + + } + +
IdNazwaNIPSiedzibaReprezentant
@company.Id@company.Name@company.Nip@company.Headquarters@(company.Representative!.Name) @(company.Representative.Surname) [ID @company.Representative.Id] +
+
+ +@code { + int _mode = 0; + private void HideAll() + { + _mode = 0; + } + private void ShowCompanyDialog() + { + _mode = (_mode != 2 ? 2 : 0); + } + + private void ShowPersonDialog() + { + _mode = (_mode != 1 ? 1 : 0); + } +} diff --git a/src/Nyanlabs.Umogen.Server/Pages/Index.razor b/src/Nyanlabs.Umogen.Server/Pages/Index.razor index d90107f..a6d3026 100644 --- a/src/Nyanlabs.Umogen.Server/Pages/Index.razor +++ b/src/Nyanlabs.Umogen.Server/Pages/Index.razor @@ -2,4 +2,14 @@ Index -

Hello, meowmeow~

+
+

umogen

+

Silly Integrated Unified System for Accounting and Contract Handling

+

umogen is an experiment that aims to leverage large language models in order to decrease the effort of + handwriting various Polish legal contracts. This iteration will focus on employment and service contracts, + namely: contract of employment + and civic-law contracts which are the primary contracts used to employ workers. Moreover, civic-law contracts + can be utilized to exchange services.

+

Check podmioty for entity data manipulation and umowy to get + started with contract generation.

+
diff --git a/src/Nyanlabs.Umogen.Server/Pages/People.razor b/src/Nyanlabs.Umogen.Server/Pages/People.razor deleted file mode 100644 index acfb104..0000000 --- a/src/Nyanlabs.Umogen.Server/Pages/People.razor +++ /dev/null @@ -1,20 +0,0 @@ -@page "/people" - -Osoby - -

Osoby

- - -@if (addVis) -{ - -} - -@code { - private bool addVis = false; - - private void Toggle() - { - addVis = !addVis; - } -} diff --git a/src/Nyanlabs.Umogen.Server/Pages/_Host.cshtml b/src/Nyanlabs.Umogen.Server/Pages/_Host.cshtml index fa06e99..a64dbd5 100644 --- a/src/Nyanlabs.Umogen.Server/Pages/_Host.cshtml +++ b/src/Nyanlabs.Umogen.Server/Pages/_Host.cshtml @@ -10,8 +10,7 @@ - + diff --git a/src/Nyanlabs.Umogen.Server/Program.cs b/src/Nyanlabs.Umogen.Server/Program.cs index 11fa1b3..8aac821 100644 --- a/src/Nyanlabs.Umogen.Server/Program.cs +++ b/src/Nyanlabs.Umogen.Server/Program.cs @@ -1,14 +1,24 @@ using Femsci.AspapajNet; +using Nyanlabs.Umogen.Server; + var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddRazorPages(); builder.Services.AddServerSideBlazor(); builder.Services.AddPapiez(); +builder.Services.AddDbContextFactory(); var app = builder.Build(); +using (var db = new DataContext()) +{ + await db.Database.EnsureCreatedAsync(); +} + + + if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Error"); diff --git a/src/Nyanlabs.Umogen.Server/Shared/CompanyAddForm.razor b/src/Nyanlabs.Umogen.Server/Shared/CompanyAddForm.razor new file mode 100644 index 0000000..4cf49e8 --- /dev/null +++ b/src/Nyanlabs.Umogen.Server/Shared/CompanyAddForm.razor @@ -0,0 +1,56 @@ +@inject DataContext db + +

Dodaj firmę

+
+
+
+ + +
+
+
+ + +
+
+ + +
+
+
+ + +
+ +
+
+ +@code { + [Parameter] + public required Action OnSubmit { get; set; } + + private long _reprId; + private string _name = string.Empty, + _loc = string.Empty, + _nip = string.Empty; + + private async Task Submit() + { + Core.Models.LegalEntity p = new() + { + Name = _name, + Nip = _nip, + Headquarters = _loc, + PersonId = _reprId, + }; + await db.AddAsync(p); + await db.SaveChangesAsync(); + + _name = string.Empty; + _loc = string.Empty; + _nip = string.Empty; + _reprId = 0; + + OnSubmit.Invoke(); + } +} diff --git a/src/Nyanlabs.Umogen.Server/Shared/Navi.razor b/src/Nyanlabs.Umogen.Server/Shared/Navi.razor index 5003a2e..ad5228d 100644 --- a/src/Nyanlabs.Umogen.Server/Shared/Navi.razor +++ b/src/Nyanlabs.Umogen.Server/Shared/Navi.razor @@ -11,7 +11,7 @@ Home