diff --git a/Web/Azaion.Web/Azaion.Repository/Azaion.Repository.csproj b/Web/Azaion.Web/Azaion.Repository/Azaion.Repository.csproj new file mode 100644 index 0000000..51e0b98 --- /dev/null +++ b/Web/Azaion.Web/Azaion.Repository/Azaion.Repository.csproj @@ -0,0 +1,14 @@ + + + + net8.0 + enable + enable + + + + + + + + diff --git a/Web/Azaion.Web/Azaion.Repository/AzaionDbSql.cs b/Web/Azaion.Web/Azaion.Repository/AzaionDbSql.cs new file mode 100644 index 0000000..2f19c85 --- /dev/null +++ b/Web/Azaion.Web/Azaion.Repository/AzaionDbSql.cs @@ -0,0 +1,12 @@ +using Azaion.Repository.Entities; +using LinqToDB; +using LinqToDB.Data; + +namespace Azaion.Repository; + +public class AzaionDbSql(DataOptions dataOptions) : DataConnection(dataOptions) +{ + public ITable Medias => this.GetTable(); + public ITable Users => this.GetTable(); + public ITable Annotations => this.GetTable(); +} \ No newline at end of file diff --git a/Web/Azaion.Web/Azaion.Repository/AzaionDbSqlSchemaHolder.cs b/Web/Azaion.Web/Azaion.Repository/AzaionDbSqlSchemaHolder.cs new file mode 100644 index 0000000..8e398c3 --- /dev/null +++ b/Web/Azaion.Web/Azaion.Repository/AzaionDbSqlSchemaHolder.cs @@ -0,0 +1,26 @@ +using Azaion.Repository.Entities; +using LinqToDB.Mapping; + +namespace Azaion.Repository; + +public static class AzaionDbSqlSchemaHolder +{ + public static readonly MappingSchema MappingSchema; + + static AzaionDbSqlSchemaHolder() + { + MappingSchema = new MappingSchema(); + var builder = new FluentMappingBuilder(MappingSchema); + + builder.Entity() + .HasIdentity(x => x.Id); + + builder.Entity() + .HasIdentity(x => x.Id); + + builder.Entity() + .HasIdentity(x => x.Id); + + builder.Build(); + } +} \ No newline at end of file diff --git a/Web/Azaion.Web/Azaion.Repository/DTO/ConnectionStrings.cs b/Web/Azaion.Web/Azaion.Repository/DTO/ConnectionStrings.cs new file mode 100644 index 0000000..5cb592a --- /dev/null +++ b/Web/Azaion.Web/Azaion.Repository/DTO/ConnectionStrings.cs @@ -0,0 +1,7 @@ +namespace Azaion.Repository.DTO; + +public class ConnectionStrings +{ + public string? FraudDb { get; set; } + public string? FraudDbMsSql { get; set; } +} \ No newline at end of file diff --git a/Web/Azaion.Web/Azaion.Repository/DTO/MediaStatusEnum.cs b/Web/Azaion.Web/Azaion.Repository/DTO/MediaStatusEnum.cs new file mode 100644 index 0000000..4eb1d1c --- /dev/null +++ b/Web/Azaion.Web/Azaion.Repository/DTO/MediaStatusEnum.cs @@ -0,0 +1,9 @@ +namespace Azaion.Video.DTO; + +public enum MediaStatusEnum +{ + None = 0, + Uploaded = 10, + Annotating = 20, + Annotated = 30 +} \ No newline at end of file diff --git a/Web/Azaion.Web/Azaion.Repository/DTO/VideoDto.cs b/Web/Azaion.Web/Azaion.Repository/DTO/VideoDto.cs new file mode 100644 index 0000000..bf919a3 --- /dev/null +++ b/Web/Azaion.Web/Azaion.Repository/DTO/VideoDto.cs @@ -0,0 +1,11 @@ +using Azaion.Video.DTO; + +namespace Azaion.Repository.DTO; + +public class VideoDto +{ + public Guid Id { get; set; } + public string Path { get; set; } = null!; + public MediaStatusEnum MediaStatus { get; set; } + public DateTime CreatedDate { get; set; } +} \ No newline at end of file diff --git a/Web/Azaion.Web/Azaion.Repository/DbFactory.cs b/Web/Azaion.Web/Azaion.Repository/DbFactory.cs new file mode 100644 index 0000000..55116fe --- /dev/null +++ b/Web/Azaion.Web/Azaion.Repository/DbFactory.cs @@ -0,0 +1,48 @@ +using System.Diagnostics; +using LinqToDB; + +namespace Azaion.Repository; + +public interface IDbFactory +{ + Task Run(Func> func); + Task Run(Func func); + + T Run(Func func); +} + +public class DbFactory : IDbFactory +{ + private readonly DataOptions _dataOptions; + + public DbFactory(string connectionString, bool useTracing = true) + { + if (string.IsNullOrEmpty(connectionString)) + throw new ArgumentException("Empty connectionString", nameof(connectionString)); + + _dataOptions = new DataOptions() + .UseMySqlConnector(connectionString) + .UseMappingSchema(AzaionDbSqlSchemaHolder.MappingSchema); + + if (useTracing) + _ = _dataOptions.UseTracing(TraceLevel.Info, t => Console.WriteLine(t.SqlText)); + } + + public async Task Run(Func> func) + { + await using var db = new AzaionDbSql(_dataOptions); + return await func(db); + } + + public async Task Run(Func func) + { + await using var db = new AzaionDbSql(_dataOptions); + await func(db); + } + + public T Run(Func func) + { + using var db = new AzaionDbSql(_dataOptions); + return func(db); + } +} diff --git a/Web/Azaion.Web/Azaion.Repository/Entities/Annotation.cs b/Web/Azaion.Web/Azaion.Repository/Entities/Annotation.cs new file mode 100644 index 0000000..1b83a50 --- /dev/null +++ b/Web/Azaion.Web/Azaion.Repository/Entities/Annotation.cs @@ -0,0 +1,10 @@ +namespace Azaion.Repository.Entities; + +public class Annotation +{ + public Guid Id { get; set; } + public Guid MediaId { get; set; } + public string ImagePath { get; set; } = null!; + public string LabelPath { get; set; } = null!; + public DateTime CreatedDate { get; set; } +} diff --git a/Web/Azaion.Web/Azaion.Repository/Entities/Media.cs b/Web/Azaion.Web/Azaion.Repository/Entities/Media.cs new file mode 100644 index 0000000..686d134 --- /dev/null +++ b/Web/Azaion.Web/Azaion.Repository/Entities/Media.cs @@ -0,0 +1,12 @@ +using Azaion.Video.DTO; + +namespace Azaion.Repository.Entities; + +public class Media +{ + public Guid Id { get; set; } + public string Path { get; set; } = null!; + public Guid? AnnotatorId { get; set; } + public MediaStatusEnum Status { get; set; } + public DateTime CreatedDate { get; set; } +} diff --git a/Web/Azaion.Web/Azaion.Repository/Entities/User.cs b/Web/Azaion.Web/Azaion.Repository/Entities/User.cs new file mode 100644 index 0000000..e0117d5 --- /dev/null +++ b/Web/Azaion.Web/Azaion.Repository/Entities/User.cs @@ -0,0 +1,12 @@ +namespace Azaion.Repository.Entities; + +public class User +{ + public Guid Id { get; set; } + public string Username { get; set; } = null!; + public string Email { get; set; } = null!; + public string PasswordHash { get; set; } = null!; + public string PasswordSalt { get; set; } = null!; + public DateTime CreatedDate { get; set; } + public DateTime UpdatedDate { get; set; } +} diff --git a/Web/Azaion.Web/Azaion.Repository/sql/01 initial.sql b/Web/Azaion.Web/Azaion.Repository/sql/01 initial.sql new file mode 100644 index 0000000..5f0785b --- /dev/null +++ b/Web/Azaion.Web/Azaion.Repository/sql/01 initial.sql @@ -0,0 +1,6 @@ +CREATE USER 'azaion-user' IDENTIFIED BY 'Aza1on@db123' +CREATE DATABASE azaion; +GRANT SELECT, INSERT, REFERENCES, UPDATE, DELETE, CREATE, INDEX, DROP, ALTER, CREATE TEMPORARY TABLES, LOCK TABLES + ON azaion.* + TO 'azaion-user'; +GRANT FILE ON *.* TO 'azaion-user'; diff --git a/Web/Azaion.Web/Azaion.Repository/sql/02 create_tables.sql b/Web/Azaion.Web/Azaion.Repository/sql/02 create_tables.sql new file mode 100644 index 0000000..1a8fa81 --- /dev/null +++ b/Web/Azaion.Web/Azaion.Repository/sql/02 create_tables.sql @@ -0,0 +1,34 @@ +CREATE TABLE User ( + Id CHAR(16) NOT NULL, + Username VARCHAR(255) NOT NULL, + Email VARCHAR(255) NOT NULL, + PasswordHash VARCHAR(255) NOT NULL, + PasswordSalt VARCHAR(255) NOT NULL, + CreatedDate DATETIME NOT NULL, + UpdatedDate DATETIME NOT NULL, + PRIMARY KEY (Id), + UNIQUE KEY (Username), + UNIQUE KEY (Email) +); + +DROP TABLE Annotation; +DROP TABLE Media; +CREATE TABLE Media ( + Id CHAR(16) NOT NULL, + Path VARCHAR(255) NOT NULL, + AnnotatorId CHAR(16) NULL, + Status INT NOT NULL, -- replace with actual enum values + CreatedDate DATETIME NOT NULL, + PRIMARY KEY (Id), + FOREIGN KEY (AnnotatorId) REFERENCES User(Id) +); + +CREATE TABLE Annotation ( + Id CHAR(16) NOT NULL, + MediaId CHAR(16) NOT NULL, + ImagePath VARCHAR(255) NOT NULL, + LabelPath VARCHAR(255) NOT NULL, + CreatedDate DATETIME NOT NULL, + PRIMARY KEY (Id), + FOREIGN KEY (MediaId) REFERENCES Media(Id) +); diff --git a/Web/Azaion.Web/Azaion.Video/Azaion.Video.csproj b/Web/Azaion.Web/Azaion.Video/Azaion.Video.csproj new file mode 100644 index 0000000..4232095 --- /dev/null +++ b/Web/Azaion.Web/Azaion.Video/Azaion.Video.csproj @@ -0,0 +1,13 @@ + + + + net8.0 + enable + enable + + + + + + + diff --git a/Web/Azaion.Web/Azaion.Video/IVideoManager.cs b/Web/Azaion.Web/Azaion.Video/IVideoManager.cs new file mode 100644 index 0000000..4259dae --- /dev/null +++ b/Web/Azaion.Web/Azaion.Video/IVideoManager.cs @@ -0,0 +1,58 @@ +using Azaion.Repository.DTO; + +namespace Azaion.Video; + +public interface IVideoManager +{ + List GetVideos(); + void OpenVideo(Guid mediaId); + + void CreateAnnotation(Guid mediaId, DateTime time) + { + + } + void FinishAnnotation(Guid mediaId); + + // after video ends, refresh list of free to edit videos. + // on attempt to open video, check whether it is already taken, if yes, then businessError, else take it to user + // + // Users + // Guid Username Email PasswordHash PasswordSalt CreatedDate UpdatedDate + // + // Media + // Guid Path UserId Status CreatedDate + // + // Annotations + // Guid MediaId ImagePath LabelPath CreatedDate + // + // Sftp Changes crawler + // Google Downloader + // + // Video_path User + // + // v01 NULL + // v02 NULL + // v03 NULL + // v04 NULL + // v05 user1 + // v06 user1 + // v07 user1 + // v08 NULL + // v09 user2 + // v10 user3 + // v11 user3 + // v12 NULL + // v13 user2 + // v14 NULL + // v15 + // v16 + // v17 + // v18 + // v19 + // v20 + // v21 + // v22 + // v23 + // v24 + // v25 +} \ No newline at end of file diff --git a/Web/Azaion.Web/Azaion.Video/VideoManager.cs b/Web/Azaion.Web/Azaion.Video/VideoManager.cs new file mode 100644 index 0000000..11ac107 --- /dev/null +++ b/Web/Azaion.Web/Azaion.Video/VideoManager.cs @@ -0,0 +1,36 @@ +using Azaion.Repository; +using Azaion.Repository.DTO; + +namespace Azaion.Video; + +public class VideoManager(IDbFactory dbFactory) : IVideoManager +{ + public List GetVideos() + { + return dbFactory.Run(db => db.Medias + .Where(x => x.AnnotatorId != null) + .Select(x => new VideoDto + { + Id = x.Id, + Path = x.Path, + CreatedDate = x.CreatedDate, + MediaStatus = x.Status + }) + .ToList()); + } + + public void OpenVideo(Guid mediaId) + { + throw new NotImplementedException(); + } + + public void OpenTestVideo() + { + + } + + public void FinishAnnotation(Guid mediaId) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/Web/Azaion.Web/Azaion.Web.sln b/Web/Azaion.Web/Azaion.Web.sln index 29de87a..f234869 100644 --- a/Web/Azaion.Web/Azaion.Web.sln +++ b/Web/Azaion.Web/Azaion.Web.sln @@ -2,6 +2,10 @@ Microsoft Visual Studio Solution File, Format Version 12.00 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azaion.WebService", "Azaion.WebService\Azaion.WebService.csproj", "{1D56907A-00A6-4D8E-88C0-929747B7FB67}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azaion.Video", "Azaion.Video\Azaion.Video.csproj", "{0FB51B6B-2C77-4F24-B640-FEEBC5395E33}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azaion.Repository", "Azaion.Repository\Azaion.Repository.csproj", "{9620B192-0B9F-4AA7-A6EE-64616FBE1B5F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -12,5 +16,13 @@ Global {1D56907A-00A6-4D8E-88C0-929747B7FB67}.Debug|Any CPU.Build.0 = Debug|Any CPU {1D56907A-00A6-4D8E-88C0-929747B7FB67}.Release|Any CPU.ActiveCfg = Release|Any CPU {1D56907A-00A6-4D8E-88C0-929747B7FB67}.Release|Any CPU.Build.0 = Release|Any CPU + {0FB51B6B-2C77-4F24-B640-FEEBC5395E33}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0FB51B6B-2C77-4F24-B640-FEEBC5395E33}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0FB51B6B-2C77-4F24-B640-FEEBC5395E33}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0FB51B6B-2C77-4F24-B640-FEEBC5395E33}.Release|Any CPU.Build.0 = Release|Any CPU + {9620B192-0B9F-4AA7-A6EE-64616FBE1B5F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9620B192-0B9F-4AA7-A6EE-64616FBE1B5F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9620B192-0B9F-4AA7-A6EE-64616FBE1B5F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9620B192-0B9F-4AA7-A6EE-64616FBE1B5F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/Web/Azaion.Web/Azaion.WebService/Azaion.WebService.csproj b/Web/Azaion.Web/Azaion.WebService/Azaion.WebService.csproj index 48e39a5..79e6b59 100644 --- a/Web/Azaion.Web/Azaion.WebService/Azaion.WebService.csproj +++ b/Web/Azaion.Web/Azaion.WebService/Azaion.WebService.csproj @@ -11,4 +11,8 @@ + + + + diff --git a/Web/Azaion.Web/Azaion.WebService/Program.cs b/Web/Azaion.Web/Azaion.WebService/Program.cs index 161f695..9962963 100644 --- a/Web/Azaion.Web/Azaion.WebService/Program.cs +++ b/Web/Azaion.Web/Azaion.WebService/Program.cs @@ -1,13 +1,17 @@ +using Azaion.Repository; +using Azaion.Repository.DTO; +using Microsoft.Extensions.Options; + var builder = WebApplication.CreateBuilder(args); -// Add services to the container. -// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); +builder.Services.Configure(builder.Configuration.GetSection(nameof(ConnectionStrings))); +builder.Services.AddSingleton(sp => new DbFactory(sp.GetService>()!.Value.FraudDb!)); + var app = builder.Build(); -// Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.UseSwagger();