diff --git a/AnalysisData/AnalysisData/AnalysisData.csproj b/AnalysisData/AnalysisData/AnalysisData.csproj index 02e5e38..c984ac9 100644 --- a/AnalysisData/AnalysisData/AnalysisData.csproj +++ b/AnalysisData/AnalysisData/AnalysisData.csproj @@ -7,6 +7,7 @@ + diff --git a/AnalysisData/AnalysisData/Controllers/UploadDataController.cs b/AnalysisData/AnalysisData/Controllers/UploadDataController.cs new file mode 100644 index 0000000..82f565f --- /dev/null +++ b/AnalysisData/AnalysisData/Controllers/UploadDataController.cs @@ -0,0 +1,46 @@ +using AnalysisData.DataProcessService; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace AnalysisData.Controllers; + +[ApiController] +[Authorize(Roles = "admin")] +[Route("api/[controller]")] +public class DataController : ControllerBase +{ + private readonly IDataProcessor _dataProcessor; + + public DataController(IDataProcessor dataProcessor) + { + _dataProcessor = dataProcessor; + } + + [HttpPost("upload/account")] + public async Task UploadAccountFile(IFormFile file) + { + if (file == null || file.Length == 0) + { + return BadRequest("File is empty or not provided."); + } + + var stream = file.OpenReadStream(); + await _dataProcessor.ProcessDataAsync(stream, "account"); + + return Ok("Account data processed successfully."); + } + + [HttpPost("upload/transaction")] + public async Task UploadTransactionFile(IFormFile file) + { + if (file == null || file.Length == 0) + { + return BadRequest("File is empty or not provided."); + } + + var stream = file.OpenReadStream(); + await _dataProcessor.ProcessDataAsync(stream, "transaction"); + + return Ok("Transaction data processed successfully."); + } +} \ No newline at end of file diff --git a/AnalysisData/AnalysisData/Data/ApplicationDbContext.cs b/AnalysisData/AnalysisData/Data/ApplicationDbContext.cs index 845bf6d..eee4799 100644 --- a/AnalysisData/AnalysisData/Data/ApplicationDbContext.cs +++ b/AnalysisData/AnalysisData/Data/ApplicationDbContext.cs @@ -1,4 +1,5 @@ -using Microsoft.EntityFrameworkCore; +using AnalysisData.DataManage.Model; +using Microsoft.EntityFrameworkCore; namespace AnalysisData.Data; @@ -10,5 +11,6 @@ public ApplicationDbContext(DbContextOptions options) } public DbSet Users { get; set; } public DbSet Roles { get; set; } - + public DbSet Accounts { get; set; } + public DbSet Transactions { get; set; } } \ No newline at end of file diff --git a/AnalysisData/AnalysisData/DataManage/Model/Account.cs b/AnalysisData/AnalysisData/DataManage/Model/Account.cs new file mode 100644 index 0000000..eb1fd26 --- /dev/null +++ b/AnalysisData/AnalysisData/DataManage/Model/Account.cs @@ -0,0 +1,15 @@ +namespace AnalysisData.DataManage.Model; + +public class Account +{ + public string AccountID { get; set; } + public string CardID { get; set; } + public string IBAN { get; set; } + public string AccountType { get; set; } + public string BranchTelephone { get; set; } + public string BranchAdress { get; set; } + public string BranchName { get; set; } + public string OwnerName { get; set; } + public string OwnerLastName { get; set; } + public string OwnerID { get; set; } +} \ No newline at end of file diff --git a/AnalysisData/AnalysisData/DataManage/Model/Trancsaction.cs b/AnalysisData/AnalysisData/DataManage/Model/Trancsaction.cs new file mode 100644 index 0000000..3a7d29c --- /dev/null +++ b/AnalysisData/AnalysisData/DataManage/Model/Trancsaction.cs @@ -0,0 +1,21 @@ +using AnalysisData.DataProcessService; +using CsvHelper.Configuration.Attributes; + +namespace AnalysisData.DataManage.Model; + +public class Transaction +{ + [Name("SourceAcount")] + public string SourceAccount { get; set; } + + [Name("DestiantionAccount")] + public string DestinationAccount { get; set; } + + public decimal Amount { get; set; } + + public string Date { get; set; } + + public string TransactionID { get; set; } + + public string Type { get; set; } +} \ No newline at end of file diff --git a/AnalysisData/AnalysisData/DataProcessService/DataReadProcessor.cs b/AnalysisData/AnalysisData/DataProcessService/DataReadProcessor.cs new file mode 100644 index 0000000..cb20868 --- /dev/null +++ b/AnalysisData/AnalysisData/DataProcessService/DataReadProcessor.cs @@ -0,0 +1,41 @@ +using CsvHelper; +using System.Globalization; +using AnalysisData.DataManage.Model; +using AnalysisData.Repository.AccountRepository.Abstraction; +using AnalysisData.Repository.TrancsactionRepository.Abstraction; +using CsvHelper.Configuration; + +namespace AnalysisData.DataProcessService; + +public class DataReadProcessor : IDataProcessor +{ + private readonly IAccountRepository _accountRepository; + private readonly ITransactionRepository _transactionRepository; + + public DataReadProcessor(IAccountRepository accountRepository, ITransactionRepository transactionRepository) + { + _accountRepository = accountRepository; + _transactionRepository = transactionRepository; + } + + public async Task ProcessDataAsync(Stream fileStream, string fileType) + { + var reader = new StreamReader(fileStream); + var csv = new CsvReader(reader, new CsvHelper.Configuration.CsvConfiguration(CultureInfo.InvariantCulture)); + + if (fileType == "account") + { + var accounts = csv.GetRecords().ToList(); + await _accountRepository.AddAccountsAsync(accounts); + } + else if (fileType == "transaction") + { + var transactions = csv.GetRecords().ToList(); + await _transactionRepository.AddTransactionsAsync(transactions); + } + else + { + throw new ArgumentException("Invalid file type"); + } + } +} \ No newline at end of file diff --git a/AnalysisData/AnalysisData/DataProcessService/IDataProcessor.cs b/AnalysisData/AnalysisData/DataProcessService/IDataProcessor.cs new file mode 100644 index 0000000..81878a3 --- /dev/null +++ b/AnalysisData/AnalysisData/DataProcessService/IDataProcessor.cs @@ -0,0 +1,6 @@ +namespace AnalysisData.DataProcessService; + +public interface IDataProcessor +{ + Task ProcessDataAsync(Stream fileStream, string fileType); +} \ No newline at end of file diff --git a/AnalysisData/AnalysisData/Migrations/20240815172546_InitialCreate.Designer.cs b/AnalysisData/AnalysisData/Migrations/20240815172546_InitialCreate.Designer.cs deleted file mode 100644 index 9ea1abf..0000000 --- a/AnalysisData/AnalysisData/Migrations/20240815172546_InitialCreate.Designer.cs +++ /dev/null @@ -1,102 +0,0 @@ -// -using AnalysisData.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace AnalysisData.Migrations -{ - [DbContext(typeof(ApplicationDbContext))] - [Migration("20240815172546_InitialCreate")] - partial class InitialCreate - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "8.0.7") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("Role", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("RoleName") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("Roles"); - }); - - modelBuilder.Entity("User", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Email") - .IsRequired() - .HasColumnType("text"); - - b.Property("FirstName") - .IsRequired() - .HasColumnType("text"); - - b.Property("ImageURL") - .HasColumnType("text"); - - b.Property("LastName") - .IsRequired() - .HasColumnType("text"); - - b.Property("Password") - .IsRequired() - .HasColumnType("text"); - - b.Property("PhoneNumber") - .IsRequired() - .HasColumnType("text"); - - b.Property("RoleId") - .HasColumnType("integer"); - - b.Property("Username") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("Users"); - }); - - modelBuilder.Entity("User", b => - { - b.HasOne("Role", "Role") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Role"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/AnalysisData/AnalysisData/Migrations/20240815172546_InitialCreate.cs b/AnalysisData/AnalysisData/Migrations/20240815172546_InitialCreate.cs deleted file mode 100644 index 85e95d4..0000000 --- a/AnalysisData/AnalysisData/Migrations/20240815172546_InitialCreate.cs +++ /dev/null @@ -1,69 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace AnalysisData.Migrations -{ - /// - public partial class InitialCreate : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "Roles", - columns: table => new - { - Id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - RoleName = table.Column(type: "text", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_Roles", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "Users", - columns: table => new - { - Id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - Username = table.Column(type: "text", nullable: false), - Password = table.Column(type: "text", nullable: false), - FirstName = table.Column(type: "text", nullable: false), - LastName = table.Column(type: "text", nullable: false), - Email = table.Column(type: "text", nullable: false), - PhoneNumber = table.Column(type: "text", nullable: false), - ImageURL = table.Column(type: "text", nullable: true), - RoleId = table.Column(type: "integer", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_Users", x => x.Id); - table.ForeignKey( - name: "FK_Users_Roles_RoleId", - column: x => x.RoleId, - principalTable: "Roles", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateIndex( - name: "IX_Users_RoleId", - table: "Users", - column: "RoleId"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "Users"); - - migrationBuilder.DropTable( - name: "Roles"); - } - } -} diff --git a/AnalysisData/AnalysisData/Migrations/ApplicationDbContextModelSnapshot.cs b/AnalysisData/AnalysisData/Migrations/ApplicationDbContextModelSnapshot.cs deleted file mode 100644 index 4e24b76..0000000 --- a/AnalysisData/AnalysisData/Migrations/ApplicationDbContextModelSnapshot.cs +++ /dev/null @@ -1,99 +0,0 @@ -// -using AnalysisData.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace AnalysisData.Migrations -{ - [DbContext(typeof(ApplicationDbContext))] - partial class ApplicationDbContextModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "8.0.7") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("Role", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("RoleName") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("Roles"); - }); - - modelBuilder.Entity("User", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Email") - .IsRequired() - .HasColumnType("text"); - - b.Property("FirstName") - .IsRequired() - .HasColumnType("text"); - - b.Property("ImageURL") - .HasColumnType("text"); - - b.Property("LastName") - .IsRequired() - .HasColumnType("text"); - - b.Property("Password") - .IsRequired() - .HasColumnType("text"); - - b.Property("PhoneNumber") - .IsRequired() - .HasColumnType("text"); - - b.Property("RoleId") - .HasColumnType("integer"); - - b.Property("Username") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("Users"); - }); - - modelBuilder.Entity("User", b => - { - b.HasOne("Role", "Role") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Role"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/AnalysisData/AnalysisData/Program.cs b/AnalysisData/AnalysisData/Program.cs index de016d9..ec16286 100644 --- a/AnalysisData/AnalysisData/Program.cs +++ b/AnalysisData/AnalysisData/Program.cs @@ -5,11 +5,16 @@ using AnalysisData.CookieService; using AnalysisData.CookieService.abstractions; using AnalysisData.Data; +using AnalysisData.DataProcessService; using AnalysisData.Exception; using AnalysisData.JwtService; using AnalysisData.JwtService.abstractions; +using AnalysisData.Repository.AccountRepository; +using AnalysisData.Repository.AccountRepository.Abstraction; using AnalysisData.Repository.RoleRepository; using AnalysisData.Repository.RoleRepository.Abstraction; +using AnalysisData.Repository.TrancsactionRepository; +using AnalysisData.Repository.TrancsactionRepository.Abstraction; using AnalysisData.Repository.UserRepository; using AnalysisData.Repository.UserRepository.Abstraction; @@ -30,7 +35,9 @@ builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddHttpContextAccessor(); - +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); builder.Services.AddDbContext(options => options.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection"))); builder.Services.AddControllers(); diff --git a/AnalysisData/AnalysisData/Repository/AccountRepository/Abstraction/IAccountRepository.cs b/AnalysisData/AnalysisData/Repository/AccountRepository/Abstraction/IAccountRepository.cs new file mode 100644 index 0000000..83985e6 --- /dev/null +++ b/AnalysisData/AnalysisData/Repository/AccountRepository/Abstraction/IAccountRepository.cs @@ -0,0 +1,8 @@ +using AnalysisData.DataManage.Model; + +namespace AnalysisData.Repository.AccountRepository.Abstraction; + +public interface IAccountRepository +{ + Task AddAccountsAsync(IEnumerable accounts); +} \ No newline at end of file diff --git a/AnalysisData/AnalysisData/Repository/AccountRepository/AccountRepository.cs b/AnalysisData/AnalysisData/Repository/AccountRepository/AccountRepository.cs new file mode 100644 index 0000000..828224b --- /dev/null +++ b/AnalysisData/AnalysisData/Repository/AccountRepository/AccountRepository.cs @@ -0,0 +1,21 @@ +using AnalysisData.Data; +using AnalysisData.DataManage.Model; +using AnalysisData.Repository.AccountRepository.Abstraction; + +namespace AnalysisData.Repository.AccountRepository; + +public class AccountRepository : IAccountRepository +{ + private readonly ApplicationDbContext _context; + + public AccountRepository(ApplicationDbContext context) + { + _context = context; + } + + public async Task AddAccountsAsync(IEnumerable accounts) + { + _context.Accounts.AddRange(accounts); + await _context.SaveChangesAsync(); + } +} \ No newline at end of file diff --git a/AnalysisData/AnalysisData/Repository/TransactionRepository/Abstraction/ITransactionRepository.cs b/AnalysisData/AnalysisData/Repository/TransactionRepository/Abstraction/ITransactionRepository.cs new file mode 100644 index 0000000..652e964 --- /dev/null +++ b/AnalysisData/AnalysisData/Repository/TransactionRepository/Abstraction/ITransactionRepository.cs @@ -0,0 +1,8 @@ +using AnalysisData.DataManage.Model; + +namespace AnalysisData.Repository.TrancsactionRepository.Abstraction; + +public interface ITransactionRepository +{ + Task AddTransactionsAsync(IEnumerable transactions); +} \ No newline at end of file diff --git a/AnalysisData/AnalysisData/Repository/TransactionRepository/TransactionRepository.cs b/AnalysisData/AnalysisData/Repository/TransactionRepository/TransactionRepository.cs new file mode 100644 index 0000000..3b4502a --- /dev/null +++ b/AnalysisData/AnalysisData/Repository/TransactionRepository/TransactionRepository.cs @@ -0,0 +1,21 @@ +using AnalysisData.Data; +using AnalysisData.DataManage.Model; +using AnalysisData.Repository.TrancsactionRepository.Abstraction; + +namespace AnalysisData.Repository.TrancsactionRepository; + +public class TransactionRepository : ITransactionRepository +{ + private readonly ApplicationDbContext _context; + + public TransactionRepository(ApplicationDbContext context) + { + _context = context; + } + + public async Task AddTransactionsAsync(IEnumerable transactions) + { + _context.Transactions.AddRange(transactions); + await _context.SaveChangesAsync(); + } +} \ No newline at end of file diff --git a/AnalysisData/AnalysisData/appsettings.json b/AnalysisData/AnalysisData/appsettings.json index 95494ad..a389ab9 100644 --- a/AnalysisData/AnalysisData/appsettings.json +++ b/AnalysisData/AnalysisData/appsettings.json @@ -11,7 +11,7 @@ } }, "ConnectionStrings": { - "DefaultConnection": "Host=localhost;Database=mohaymen;Username=postgres;Password=mahdijm;Timeout=300" + "DefaultConnection": "Host=localhost;Database=mohaymen;Username=postgres;Password=12345;Timeout=300" }, "AllowedHosts": "*"