#23 sqlite import and GanjoorStructures migration

This commit is contained in:
Hamid Reza Mohammadi 2020-10-22 21:04:50 +03:30
parent 78822dea8d
commit 174e115d1f
12 changed files with 2120 additions and 2 deletions

View File

@ -0,0 +1,48 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using RMuseum.Models.Auth.Memory;
using RMuseum.Services;
using RSecurityBackend.Models.Generic;
using System.Net;
using System.Threading.Tasks;
namespace RMuseum.Controllers
{
[Produces("application/json")]
[Route("api/ganjoor")]
public class GanjoorController : Controller
{
/// <summary>
/// imports data from ganjoor SQLite database
/// </summary>
/// <returns></returns>
[HttpPost]
[Route("sqliteimport")]
[Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + RMuseumSecurableItem.ImportOperationShortName)]
[ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(bool))]
[ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))]
public async Task<IActionResult> ImportLocalSQLiteDb()
{
RServiceResult<bool> res =
await _ganjoorService.ImportLocalSQLiteDb();
if (res.Result)
return Ok();
return BadRequest(res.ExceptionString);
}
/// <summary>
/// Ganjoor Service
/// </summary>
protected readonly IGanjoorService _ganjoorService;
/// <summary>
/// constructor
/// </summary>
/// <param name="ganjoorService"></param>
public GanjoorController(IGanjoorService ganjoorService)
{
_ganjoorService = ganjoorService;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,128 @@
using Microsoft.EntityFrameworkCore.Migrations;
namespace RMuseum.Migrations
{
public partial class GanjoorStructures : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "GanjoorPoets",
columns: table => new
{
Id = table.Column<int>(nullable: false),
Name = table.Column<string>(nullable: true),
Description = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_GanjoorPoets", x => x.Id);
});
migrationBuilder.CreateTable(
name: "GanjoorCategories",
columns: table => new
{
Id = table.Column<int>(nullable: false),
PoetId = table.Column<int>(nullable: false),
Title = table.Column<string>(nullable: true),
ParentId = table.Column<int>(nullable: true),
UrlSlug = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_GanjoorCategories", x => x.Id);
table.ForeignKey(
name: "FK_GanjoorCategories_GanjoorCategories_ParentId",
column: x => x.ParentId,
principalTable: "GanjoorCategories",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_GanjoorCategories_GanjoorPoets_PoetId",
column: x => x.PoetId,
principalTable: "GanjoorPoets",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "GanjoorPoems",
columns: table => new
{
Id = table.Column<int>(nullable: false),
CatId = table.Column<int>(nullable: false),
Title = table.Column<string>(nullable: true),
FullTitle = table.Column<string>(nullable: true),
UrlSlug = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_GanjoorPoems", x => x.Id);
table.ForeignKey(
name: "FK_GanjoorPoems_GanjoorCategories_CatId",
column: x => x.CatId,
principalTable: "GanjoorCategories",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "GanjoorVerses",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
PoemId = table.Column<int>(nullable: false),
VOrder = table.Column<int>(nullable: false),
VersePosition = table.Column<int>(nullable: false),
Text = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_GanjoorVerses", x => x.Id);
table.ForeignKey(
name: "FK_GanjoorVerses_GanjoorPoems_PoemId",
column: x => x.PoemId,
principalTable: "GanjoorPoems",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_GanjoorCategories_ParentId",
table: "GanjoorCategories",
column: "ParentId");
migrationBuilder.CreateIndex(
name: "IX_GanjoorCategories_PoetId",
table: "GanjoorCategories",
column: "PoetId");
migrationBuilder.CreateIndex(
name: "IX_GanjoorPoems_CatId",
table: "GanjoorPoems",
column: "CatId");
migrationBuilder.CreateIndex(
name: "IX_GanjoorVerses_PoemId",
table: "GanjoorVerses",
column: "PoemId");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "GanjoorVerses");
migrationBuilder.DropTable(
name: "GanjoorPoems");
migrationBuilder.DropTable(
name: "GanjoorCategories");
migrationBuilder.DropTable(
name: "GanjoorPoets");
}
}
}

View File

@ -336,6 +336,98 @@ namespace RMuseum.Migrations
b.ToTable("UserBookmarks");
});
modelBuilder.Entity("RMuseum.Models.Ganjoor.GanjoorCat", b =>
{
b.Property<int>("Id")
.HasColumnType("int");
b.Property<int?>("ParentId")
.HasColumnType("int");
b.Property<int>("PoetId")
.HasColumnType("int");
b.Property<string>("Title")
.HasColumnType("nvarchar(max)");
b.Property<string>("UrlSlug")
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.HasIndex("ParentId");
b.HasIndex("PoetId");
b.ToTable("GanjoorCategories");
});
modelBuilder.Entity("RMuseum.Models.Ganjoor.GanjoorPoem", b =>
{
b.Property<int>("Id")
.HasColumnType("int");
b.Property<int>("CatId")
.HasColumnType("int");
b.Property<string>("FullTitle")
.HasColumnType("nvarchar(max)");
b.Property<string>("Title")
.HasColumnType("nvarchar(max)");
b.Property<string>("UrlSlug")
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.HasIndex("CatId");
b.ToTable("GanjoorPoems");
});
modelBuilder.Entity("RMuseum.Models.Ganjoor.GanjoorPoet", b =>
{
b.Property<int>("Id")
.HasColumnType("int");
b.Property<string>("Description")
.HasColumnType("nvarchar(max)");
b.Property<string>("Name")
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.ToTable("GanjoorPoets");
});
modelBuilder.Entity("RMuseum.Models.Ganjoor.GanjoorVerse", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<int>("PoemId")
.HasColumnType("int");
b.Property<string>("Text")
.HasColumnType("nvarchar(max)");
b.Property<int>("VOrder")
.HasColumnType("int");
b.Property<int>("VersePosition")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("PoemId");
b.ToTable("GanjoorVerses");
});
modelBuilder.Entity("RMuseum.Models.GanjoorAudio.PoemNarration", b =>
{
b.Property<Guid>("Id")
@ -1255,6 +1347,37 @@ namespace RMuseum.Migrations
.HasForeignKey("RArtifactMasterRecordId");
});
modelBuilder.Entity("RMuseum.Models.Ganjoor.GanjoorCat", b =>
{
b.HasOne("RMuseum.Models.Ganjoor.GanjoorCat", "Parent")
.WithMany()
.HasForeignKey("ParentId");
b.HasOne("RMuseum.Models.Ganjoor.GanjoorPoet", "Poet")
.WithMany()
.HasForeignKey("PoetId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("RMuseum.Models.Ganjoor.GanjoorPoem", b =>
{
b.HasOne("RMuseum.Models.Ganjoor.GanjoorCat", "Cat")
.WithMany()
.HasForeignKey("CatId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("RMuseum.Models.Ganjoor.GanjoorVerse", b =>
{
b.HasOne("RMuseum.Models.Ganjoor.GanjoorPoem", "Poem")
.WithMany()
.HasForeignKey("PoemId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("RMuseum.Models.GanjoorAudio.PoemNarration", b =>
{
b.HasOne("RSecurityBackend.Models.Auth.Db.RAppUser", "Owner")

View File

@ -63,6 +63,11 @@ namespace RMuseum.Models.Auth.Memory
/// </summary>
public const string AudioNarrationEntityShortName = "narration";
/// <summary>
/// ganjoor contents
/// </summary>
public const string GanjoorEntityShortName = "ganjoor";
/// <summary>
/// list of forms and their permissions
/// </summary>
@ -121,6 +126,15 @@ namespace RMuseum.Models.Auth.Memory
new SecurableItemOperation(ModerateOperationShortName, "بررسی", false)
}
},
new SecurableItem()
{
ShortName = GanjoorEntityShortName,
Description = "محتوای گنجور",
Operations = new SecurableItemOperation[]
{
new SecurableItemOperation(ImportOperationShortName, "ورود اطلاعات", false),
}
},
});
return lst.ToArray();

View File

@ -27,6 +27,10 @@ namespace RMuseum.Models.GanjoorAudio
/// <summary>
/// Final data is actually exported to a MySQL database which this auto increment field is its key
/// </summary>
/// <remarks>
/// Caution: this could become a relation with newly added table <see cref="RMuseum.Models.Ganjoor.GanjoorPoem"/>
/// but if we do this we lose the ability to drop and create <see cref="RMuseum.Models.Ganjoor.GanjoorPoem"/> without losing narration data
/// </remarks>
public int GanjoorAudioId { get; set; }
/// <summary>

View File

@ -13,6 +13,7 @@
<ItemGroup>
<PackageReference Include="Audit.WebApi.Core" Version="15.0.5" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="3.1.3" />
<PackageReference Include="Microsoft.Data.Sqlite" Version="3.1.9" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="3.1.3">
<PrivateAssets>all</PrivateAssets>

View File

@ -738,6 +738,23 @@
<param name="auditLogService">
</param>
</member>
<member name="M:RMuseum.Controllers.GanjoorController.ImportLocalSQLiteDb">
<summary>
imports data from ganjoor SQLite database
</summary>
<returns></returns>
</member>
<member name="F:RMuseum.Controllers.GanjoorController._ganjoorService">
<summary>
Ganjoor Service
</summary>
</member>
<member name="M:RMuseum.Controllers.GanjoorController.#ctor(RMuseum.Services.IGanjoorService)">
<summary>
constructor
</summary>
<param name="ganjoorService"></param>
</member>
<member name="M:RMuseum.Controllers.ImageController.Get(System.Guid,System.String)">
<summary>
returns image stream
@ -1536,6 +1553,11 @@
audio narrations
</summary>
</member>
<member name="F:RMuseum.Models.Auth.Memory.RMuseumSecurableItem.GanjoorEntityShortName">
<summary>
ganjoor contents
</summary>
</member>
<member name="P:RMuseum.Models.Auth.Memory.RMuseumSecurableItem.Items">
<summary>
list of forms and their permissions
@ -1735,6 +1757,10 @@
<summary>
Final data is actually exported to a MySQL database which this auto increment field is its key
</summary>
<remarks>
Caution: this could become a relation with newly added table <see cref="T:RMuseum.Models.Ganjoor.GanjoorPoem"/>
but if we do this we lose the ability to drop and create <see cref="T:RMuseum.Models.Ganjoor.GanjoorPoem"/> without losing narration data
</remarks>
</member>
<member name="P:RMuseum.Models.GanjoorAudio.PoemNarration.GanjoorPostId">
<summary>
@ -3855,7 +3881,7 @@
</member>
<member name="M:RMuseum.Services.IAudioNarrationService.OneTimeImport(System.Guid)">
<summary>
imports data from ganjoor MySql database
imports narration data from ganjoor MySql database
</summary>
<param name="OwnrRAppUserId">User Id which becomes owner of imported data</param>
<returns></returns>
@ -3940,6 +3966,17 @@
retry publish unpublished narrations
</summary>
</member>
<member name="T:RMuseum.Services.IGanjoorService">
<summary>
Ganjoor Poems Content Privider Service
</summary>
</member>
<member name="M:RMuseum.Services.IGanjoorService.ImportLocalSQLiteDb">
<summary>
imports unimported poem data from a locally accessible ganjoor SqlLite database
</summary>
<returns></returns>
</member>
<member name="T:RMuseum.Services.Implementation.ArtifactService">
<summary>
IArtifactService implementation
@ -4785,6 +4822,34 @@
<param name="format"></param>
<returns></returns>
</member>
<member name="T:RMuseum.Services.Implementation.GanjoorService">
<summary>
IGanjoorService implementation
</summary>
</member>
<member name="M:RMuseum.Services.Implementation.GanjoorService.ImportLocalSQLiteDb">
<summary>
imports unimported poem data from a locally accessible ganjoor SqlLite database
</summary>
<returns></returns>
</member>
<member name="F:RMuseum.Services.Implementation.GanjoorService._context">
<summary>
Database Contetxt
</summary>
</member>
<member name="P:RMuseum.Services.Implementation.GanjoorService.Configuration">
<summary>
Configuration
</summary>
</member>
<member name="M:RMuseum.Services.Implementation.GanjoorService.#ctor(RMuseum.DbContext.RMuseumDbContext,Microsoft.Extensions.Configuration.IConfiguration)">
<summary>
constructor
</summary>
<param name="context"></param>
<param name="configuration"></param>
</member>
<member name="T:RMuseum.Services.Implementation.ImportedFromDesktopGanjoor.GPersianTextSync">
<summary>
Persian specific text utility class

View File

@ -38,7 +38,7 @@ namespace RMuseum.Services
Task<RServiceResult<bool>> UpdatePoemNarration(Guid id, PoemNarrationUpdateViewModel metadata);
/// <summary>
/// imports data from ganjoor MySql database
/// imports narration data from ganjoor MySql database
/// </summary>
/// <param name="OwnrRAppUserId">User Id which becomes owner of imported data</param>
/// <returns></returns>

View File

@ -0,0 +1,17 @@
using RSecurityBackend.Models.Generic;
using System.Threading.Tasks;
namespace RMuseum.Services
{
/// <summary>
/// Ganjoor Poems Content Privider Service
/// </summary>
public interface IGanjoorService
{
/// <summary>
/// imports unimported poem data from a locally accessible ganjoor SqlLite database
/// </summary>
/// <returns></returns>
Task<RServiceResult<bool>> ImportLocalSQLiteDb();
}
}

View File

@ -0,0 +1,174 @@
using Microsoft.Data.Sqlite;
using RMuseum.DbContext;
using RSecurityBackend.Models.Generic;
using System.Threading.Tasks;
using System.Data;
using Dapper;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using RMuseum.Models.Ganjoor;
using System;
using Microsoft.Extensions.Configuration;
namespace RMuseum.Services.Implementation
{
/// <summary>
/// IGanjoorService implementation
/// </summary>
public class GanjoorService : IGanjoorService
{
#region SQLite import
/// <summary>
/// imports unimported poem data from a locally accessible ganjoor SqlLite database
/// </summary>
/// <returns></returns>
public async Task<RServiceResult<bool>> ImportLocalSQLiteDb()
{
try
{
SqliteConnectionStringBuilder connectionStringBuilder = new SqliteConnectionStringBuilder();
connectionStringBuilder.DataSource = @"C:\Users\moham\AppData\Local\ganjoor\ganjoor.s3db";
using (SqliteConnection connection = new SqliteConnection(connectionStringBuilder.ToString()))
{
await connection.OpenAsync();
IDbConnection dapper = connection;
foreach (var poet in await dapper.QueryAsync("SELECT * FROM poet ORDER BY id"))
{
int poetId = (int)poet.id;
using(RMuseumDbContext context = new RMuseumDbContext(Configuration))
{
if ((await context.GanjoorPoets.Where(p => p.Id == poetId).FirstOrDefaultAsync()) != null)
{
continue;
}
context.GanjoorPoets.Add
(
new GanjoorPoet()
{
Id = poetId,
Name = poet.name,
Description = poet.description
}
);
await context.SaveChangesAsync();
}
await _ImportSQLiteCatChildren(dapper, poetId, 0, poet.name);
}
}
}
catch(Exception exp)
{
return new RServiceResult<bool>(false, exp.ToString());
}
return new RServiceResult<bool>(true);
}
private async Task _ImportSQLiteCatChildren(IDbConnection dapper, int poetId, int catId, string itemFullTitle)
{
foreach(var cat in await dapper.QueryAsync($"SELECT * FROM cat WHERE poet_id = {poetId} AND parent_id = {catId} ORDER BY id"))
{
using (RMuseumDbContext context = new RMuseumDbContext(Configuration))
{
context.GanjoorCategories.Add
(
new GanjoorCat()
{
Id = (int)cat.id,
PoetId = poetId,
Title = cat.text,
UrlSlug = _ExtractUrlSlug(cat.url)
}
);
foreach (var poem in await dapper.QueryAsync($"SELECT * FROM poem WHERE cat_id = {cat.id} ORDER BY id"))
{
context.GanjoorPoems.Add
(
new GanjoorPoem()
{
Id = (int)poem.id,
CatId = (int)cat.id,
Title = poem.title,
UrlSlug = _ExtractUrlSlug(poem.url),
FullTitle = $"{itemFullTitle} » {cat.text} » {poem.title}"
}
);
foreach (var verse in await dapper.QueryAsync($"SELECT * FROM verse WHERE poem_id = {poem.id} ORDER BY vorder"))
{
context.GanjoorVerses.Add
(
new GanjoorVerse()
{
PoemId = (int)poem.id,
VOrder = (int)verse.vorder,
VersePosition = (VersePosition)verse.position,
Text = verse.text
}
);
}
await context.SaveChangesAsync();
}
await _ImportSQLiteCatChildren(dapper, poetId, (int)cat.id, $"{itemFullTitle} » {cat.text}");
}
}
}
#endregion
private static string _ExtractUrlSlug(string slug)
{
if (!string.IsNullOrEmpty(slug))
{
//sample: https://ganjoor.net/hafez
if (slug.LastIndexOf('/') != -1)
{
// => result = hafez
slug = slug.Substring(slug.LastIndexOf('/') + 1);
}
}
return slug;
}
/// <summary>
/// Database Contetxt
/// </summary>
protected readonly RMuseumDbContext _context;
/// <summary>
/// Configuration
/// </summary>
protected IConfiguration Configuration { get; }
/// <summary>
/// constructor
/// </summary>
/// <param name="context"></param>
/// <param name="configuration"></param>
public GanjoorService(RMuseumDbContext context, IConfiguration configuration)
{
_context = context;
Configuration = configuration;
}
}
}

View File

@ -254,6 +254,9 @@ namespace RMuseum
//audio service
services.AddTransient<IAudioNarrationService, AudioNarrationService>();
//ganjoor service
services.AddTransient<IGanjoorService, GanjoorService>();
//upload limit for IIS
services.Configure<IISServerOptions>(options =>
{