From 464f69fecae0ad261e3fc83b9166d401e867e3fe Mon Sep 17 00:00:00 2001 From: Hamid Reza Mohammadi Date: Sun, 30 Aug 2020 16:19:37 +0430 Subject: [PATCH 1/2] #5 basic view model for audio narrations get api --- RMuseum/Models/GanjoorAudio/PoemNarration.cs | 5 + .../ViewModels/PoemNarrationViewModel.cs | 214 ++++++++++++++++++ RMuseum/RMuseum.xml | 170 ++++++++++++++ 3 files changed, 389 insertions(+) create mode 100644 RMuseum/Models/GanjoorAudio/ViewModels/PoemNarrationViewModel.cs diff --git a/RMuseum/Models/GanjoorAudio/PoemNarration.cs b/RMuseum/Models/GanjoorAudio/PoemNarration.cs index 8da3ed50..3ecc3522 100644 --- a/RMuseum/Models/GanjoorAudio/PoemNarration.cs +++ b/RMuseum/Models/GanjoorAudio/PoemNarration.cs @@ -89,6 +89,11 @@ namespace RMuseum.Models.GanjoorAudio /// public string RemoteXMLFilePath { get { return $"/i/{SoundFilesFolder}/x/{FileNameWithoutExtension}.xml"; } } + /// + /// XML url + /// + public string XMLUrl { get { return $"https://i.ganjoor.net/{SoundFilesFolder}/x/{FileNameWithoutExtension}.xml"; } } + /// /// Audio Title /// diff --git a/RMuseum/Models/GanjoorAudio/ViewModels/PoemNarrationViewModel.cs b/RMuseum/Models/GanjoorAudio/ViewModels/PoemNarrationViewModel.cs new file mode 100644 index 00000000..9721abb3 --- /dev/null +++ b/RMuseum/Models/GanjoorAudio/ViewModels/PoemNarrationViewModel.cs @@ -0,0 +1,214 @@ +using RSecurityBackend.Models.Auth.ViewModels; +using System; +using System.Collections.Generic; + +namespace RMuseum.Models.GanjoorAudio.ViewModels +{ + /// + /// Poem Narration view model + /// + public class PoemNarrationViewModel + { + /// + /// constructor + /// + /// + public PoemNarrationViewModel(PoemNarration src) + { + Id = src.Id; + Owner = new PublicRAppUser(src.Owner); + GanjoorAudioId = src.GanjoorAudioId; + GanjoorPostId = src.GanjoorPostId; + AudioOrder = src.AudioOrder; + FileNameWithoutExtension = src.FileNameWithoutExtension; + SoundFilesFolder = src.SoundFilesFolder; + AudioTitle = src.AudioTitle; + AudioArtist = src.AudioArtist; + AudioArtistUrl = src.AudioArtistUrl; + AudioSrc = src.AudioSrc; + AudioSrcUrl = src.AudioSrcUrl; + LegacyAudioGuid = src.LegacyAudioGuid; + Mp3FileCheckSum = src.Mp3FileCheckSum; + Mp3SizeInBytes = src.Mp3SizeInBytes; + OggSizeInBytes = src.OggSizeInBytes; + LocalMp3FilePath = src.LocalMp3FilePath; + LocalXmlFilePath = src.LocalXmlFilePath; + ReviewStatus = src.ReviewStatus; + List lst = new List(); + foreach(AudioSyncStatus status in + new AudioSyncStatus[] + { + AudioSyncStatus.SynchronizedOrRejected, + AudioSyncStatus.NewItem, + AudioSyncStatus.MetadataChanged, + AudioSyncStatus.SoundFilesChanged, + AudioSyncStatus.XmlFileChanged, + AudioSyncStatus.Deleted + }) + { + if( + src.AudioSyncStatus == (( (int)status )| src.AudioSyncStatus) + ) + { + lst.Add(status); + } + } + AudioSyncStatusArray = lst.ToArray(); + } + + /// + /// Id + /// + public Guid Id { get; set; } + + /// + /// Owner User + /// + public PublicRAppUser Owner { get; set; } + + /// + /// Final data is actually exported to a MySQL database which this auto increment field is its key + /// + public int GanjoorAudioId { get; set; } + + /// + /// Ganjoor Post Id + /// + public int GanjoorPostId { get; set; } + + /// + /// This determines where an audio is displayed between a list of sounds related to a specfic poem + /// + public int AudioOrder { get; set; } + + /// + /// Using this field content you would determine xml, mp3 and ogg file names + /// + public string FileNameWithoutExtension { get; set; } + + /// + /// combining this with + .ext would preduce relative path of sound files on our file server the full path would become [HOMEPath] + '/i/' + this value + /// + /// + /// "a2" + /// + /// + /// We had previously used different pathes because when a directory became overcrowded our connection time would increase unbearably due to IRAN internet problems, so it is sensible to store it per record and not as a global option value + /// + public string SoundFilesFolder { get; set; } + + /// + /// MP3 File Path + /// + public string RemoteMp3FilePath { get { return $"/i/{SoundFilesFolder}/{FileNameWithoutExtension}.mp3"; } } + + /// + /// MP3 url + /// + public string Mp3Url { get { return $"https://i.ganjoor.net/{SoundFilesFolder}/{FileNameWithoutExtension}.mp3"; } } + + + /// + /// OGG File Path + /// + public string RemoteOggFilePath { get { return $"/i/{SoundFilesFolder}/{FileNameWithoutExtension}.ogg"; } } + + /// + /// OGG url + /// + public string OggUrl { get { return $"https://i.ganjoor.net/{SoundFilesFolder}/{FileNameWithoutExtension}.ogg"; } } + + /// + /// This is also a legacy field + /// + /// + /// "/i/a2/x" + /// + /// + /// XML Schema is defined based on Desktop Ganjoor (https://github.com/ganjoor/ganjoor) code, for + /// more information take a look at this code: + /// https://github.com/ganjoor/ganjoor/blob/master/ganjoor/Audio%20Support/PoemAudioListProcessor.cs + /// + public string RemoteXMLFilePath { get { return $"/i/{SoundFilesFolder}/x/{FileNameWithoutExtension}.xml"; } } + + /// + /// XML url + /// + public string XMLUrl { get { return $"https://i.ganjoor.net/{SoundFilesFolder}/x/{FileNameWithoutExtension}.xml"; } } + + /// + /// Audio Title + /// + public string AudioTitle { get; set; } + + /// + /// Audio Artist + /// + public string AudioArtist { get; set; } + + /// + /// Audio Artist Url + /// + public string AudioArtistUrl { get; set; } + + /// + /// Audio Source + /// + public string AudioSrc { get; set; } + + /// + /// Audio Src Url + /// + public string AudioSrcUrl { get; set; } + + /// + /// Legacy Audio Guid + /// + public Guid LegacyAudioGuid { get; set; } + + /// + /// Audio File CheckSum + /// + public string Mp3FileCheckSum { get; set; } + + /// + /// mp3 size in bytes + /// + public int Mp3SizeInBytes { get; set; } + + /// + /// ogg size in bytes + /// + public int OggSizeInBytes { get; set; } + + /// + /// Upload Date + /// + public DateTime UploadDate { get; set; } + + /// + /// Review Date (Approve or Reject) + /// + public DateTime ReviewDate { get; set; } + + /// + /// MP3 File local path on Windows Server (if item is not rejected probably it is not valid and it is deleted) + /// + public string LocalMp3FilePath { get; set; } + + /// + /// XML File local path on Windows Server (if item is not rejected probably it is not valid and it is deleted) + /// + public string LocalXmlFilePath { get; set; } + + /// + /// Value is one or a combination of + /// + public AudioSyncStatus[] AudioSyncStatusArray { get; set; } + + /// + /// Review Status + /// + public AudioReviewStatus ReviewStatus { get; set; } + } +} diff --git a/RMuseum/RMuseum.xml b/RMuseum/RMuseum.xml index 2a51a003..80293aaf 100644 --- a/RMuseum/RMuseum.xml +++ b/RMuseum/RMuseum.xml @@ -1668,6 +1668,11 @@ https://github.com/ganjoor/ganjoor/blob/master/ganjoor/Audio%20Support/PoemAudioListProcessor.cs + + + XML url + + Audio Title @@ -1743,6 +1748,171 @@ Review Status + + + Poem Narration view model + + + + + constructor + + + + + + Id + + + + + Owner User + + + + + Final data is actually exported to a MySQL database which this auto increment field is its key + + + + + Ganjoor Post Id + + + + + This determines where an audio is displayed between a list of sounds related to a specfic poem + + + + + Using this field content you would determine xml, mp3 and ogg file names + + + + + combining this with + .ext would preduce relative path of sound files on our file server the full path would become [HOMEPath] + '/i/' + this value + + + "a2" + + + We had previously used different pathes because when a directory became overcrowded our connection time would increase unbearably due to IRAN internet problems, so it is sensible to store it per record and not as a global option value + + + + + MP3 File Path + + + + + MP3 url + + + + + OGG File Path + + + + + OGG url + + + + + This is also a legacy field + + + "/i/a2/x" + + + XML Schema is defined based on Desktop Ganjoor (https://github.com/ganjoor/ganjoor) code, for + more information take a look at this code: + https://github.com/ganjoor/ganjoor/blob/master/ganjoor/Audio%20Support/PoemAudioListProcessor.cs + + + + + XML url + + + + + Audio Title + + + + + Audio Artist + + + + + Audio Artist Url + + + + + Audio Source + + + + + Audio Src Url + + + + + Legacy Audio Guid + + + + + Audio File CheckSum + + + + + mp3 size in bytes + + + + + ogg size in bytes + + + + + Upload Date + + + + + Review Date (Approve or Reject) + + + + + MP3 File local path on Windows Server (if item is not rejected probably it is not valid and it is deleted) + + + + + XML File local path on Windows Server (if item is not rejected probably it is not valid and it is deleted) + + + + + Value is one or a combination of + + + + + Review Status + + ganjoor link for artifacts and items From 80ccc62162d7c37254a3adeb1f4b5746c3c797ec Mon Sep 17 00:00:00 2001 From: Hamid Reza Mohammadi Date: Sun, 30 Aug 2020 17:15:07 +0430 Subject: [PATCH 2/2] #5 audio narrations secure get api is ready --- RMuseum/Controllers/ArtifactController.cs | 4 +- .../Controllers/AudioNarrationController.cs | 51 +++++++++++++++++++ .../Auth/Memory/RMuseumSecurableItem.cs | 6 +-- .../Models/GanjoorAudio/AudioReviewStatus.cs | 14 +++-- .../ViewModels/PoemNarrationViewModel.cs | 2 + RMuseum/RMuseum.xml | 39 +++++++++++++- RMuseum/Services/IAudioNarrationService.cs | 13 ++++- .../Implementation/AudioNarrationService.cs | 45 +++++++++++++++- .../Models/Generic/PagingParameterModel.cs | 6 +-- RSecurityBackend/RSecurityBackend.xml | 6 +-- 10 files changed, 169 insertions(+), 17 deletions(-) diff --git a/RMuseum/Controllers/ArtifactController.cs b/RMuseum/Controllers/ArtifactController.cs index 28463542..514250f4 100644 --- a/RMuseum/Controllers/ArtifactController.cs +++ b/RMuseum/Controllers/ArtifactController.cs @@ -1423,7 +1423,7 @@ namespace RMuseum.Controllers loggedOnUserId, new Guid(User.Claims.FirstOrDefault(c => c.Type == "SessionId").Value), RMuseumSecurableItem.NoteEntityShortName, - RMuseumSecurableItem.ModerateCommentsOperationShortName + RMuseumSecurableItem.ModerateOperationShortName ); if (!string.IsNullOrEmpty(canModerate.ExceptionString)) return BadRequest(canModerate.ExceptionString); @@ -1460,7 +1460,7 @@ namespace RMuseum.Controllers loggedOnUserId, new Guid(User.Claims.FirstOrDefault(c => c.Type == "SessionId").Value), RMuseumSecurableItem.NoteEntityShortName, - RMuseumSecurableItem.ModerateCommentsOperationShortName + RMuseumSecurableItem.ModerateOperationShortName ); if (!string.IsNullOrEmpty(canModerate.ExceptionString)) return BadRequest(canModerate.ExceptionString); diff --git a/RMuseum/Controllers/AudioNarrationController.cs b/RMuseum/Controllers/AudioNarrationController.cs index ca3448c8..b0012285 100644 --- a/RMuseum/Controllers/AudioNarrationController.cs +++ b/RMuseum/Controllers/AudioNarrationController.cs @@ -1,10 +1,14 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json; using RMuseum.Models.Auth.Memory; +using RMuseum.Models.GanjoorAudio; +using RMuseum.Models.GanjoorAudio.ViewModels; using RMuseum.Services; using RSecurityBackend.Models.Generic; using RSecurityBackend.Services; using System; +using System.Collections.Generic; using System.Linq; using System.Net; using System.Threading.Tasks; @@ -16,6 +20,53 @@ namespace RMuseum.Controllers public class AudioNarrationController : Controller { + /// + /// Gets audio narrations, user must have narration::moderate permission to be able to see all users narrations + /// + /// + /// default: false, user must have narration::moderate permission to be able to see all users narrations + /// default: -1, unfiltered + /// + [HttpGet] + [Authorize] + [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(IEnumerable))] + [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] + [ProducesResponseType((int)HttpStatusCode.Forbidden, Type = typeof(string))] + + public async Task Get([FromQuery] PagingParameterModel paging, bool allUsers = false, AudioReviewStatus status = AudioReviewStatus.All ) + { + Guid loggedOnUserId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); + Guid sessionId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "SessionId").Value); + + if (allUsers) + { + RServiceResult + canView = + await _userPermissionChecker.Check + ( + loggedOnUserId, + sessionId, + RMuseumSecurableItem.AudioNarrationEntityShortName, + RMuseumSecurableItem.ModerateOperationShortName + ); + if (!string.IsNullOrEmpty(canView.ExceptionString)) + return BadRequest(canView.ExceptionString); + + if (!canView.Result) + return StatusCode((int)HttpStatusCode.Forbidden); + } + + var res = await _audioService.GetAll(paging, allUsers ? Guid.Empty : loggedOnUserId, status); + if(!string.IsNullOrEmpty(res.ExceptionString)) + return BadRequest(res.ExceptionString); + + // Paging Header + HttpContext.Response.Headers.Add("paging-headers", JsonConvert.SerializeObject(res.Result.PagingMeta)); + + return Ok(res.Result.Items); + } + + /// /// imports data from ganjoor MySql database /// diff --git a/RMuseum/Models/Auth/Memory/RMuseumSecurableItem.cs b/RMuseum/Models/Auth/Memory/RMuseumSecurableItem.cs index 62a7b425..81127224 100644 --- a/RMuseum/Models/Auth/Memory/RMuseumSecurableItem.cs +++ b/RMuseum/Models/Auth/Memory/RMuseumSecurableItem.cs @@ -51,7 +51,7 @@ namespace RMuseum.Models.Auth.Memory /// /// moderate /// - public const string ModerateCommentsOperationShortName = "moderate"; + public const string ModerateOperationShortName = "moderate"; /// /// review suggested ganjoor links @@ -108,7 +108,7 @@ namespace RMuseum.Models.Auth.Memory Description = "یادداشتها", Operations = new SecurableItemOperation[] { - new SecurableItemOperation(ModerateCommentsOperationShortName, "بررسی", false) + new SecurableItemOperation(ModerateOperationShortName, "بررسی", false) } }, new SecurableItem() @@ -118,7 +118,7 @@ namespace RMuseum.Models.Auth.Memory Operations = new SecurableItemOperation[] { new SecurableItemOperation(ImportOperationShortName, "ورود اطلاعات", false), - new SecurableItemOperation(ModerateCommentsOperationShortName, "بررسی", false) + new SecurableItemOperation(ModerateOperationShortName, "بررسی", false) } }, diff --git a/RMuseum/Models/GanjoorAudio/AudioReviewStatus.cs b/RMuseum/Models/GanjoorAudio/AudioReviewStatus.cs index 16d730da..4df7a2cc 100644 --- a/RMuseum/Models/GanjoorAudio/AudioReviewStatus.cs +++ b/RMuseum/Models/GanjoorAudio/AudioReviewStatus.cs @@ -5,19 +5,27 @@ /// public enum AudioReviewStatus { + /// + /// All / Unfiltered (for queris) + /// + All = -1, + /// + /// Draft + /// + Draft = 0, /// /// pending for review /// - Pending = 0, + Pending = 1, /// /// approved /// - Approved = 1, + Approved = 2, /// /// Rejected /// - Rejected = 2 + Rejected = 3 } } diff --git a/RMuseum/Models/GanjoorAudio/ViewModels/PoemNarrationViewModel.cs b/RMuseum/Models/GanjoorAudio/ViewModels/PoemNarrationViewModel.cs index 9721abb3..40d6288e 100644 --- a/RMuseum/Models/GanjoorAudio/ViewModels/PoemNarrationViewModel.cs +++ b/RMuseum/Models/GanjoorAudio/ViewModels/PoemNarrationViewModel.cs @@ -34,6 +34,8 @@ namespace RMuseum.Models.GanjoorAudio.ViewModels LocalMp3FilePath = src.LocalMp3FilePath; LocalXmlFilePath = src.LocalXmlFilePath; ReviewStatus = src.ReviewStatus; + UploadDate = src.UploadDate; + ReviewDate = src.ReviewDate; List lst = new List(); foreach(AudioSyncStatus status in new AudioSyncStatus[] diff --git a/RMuseum/RMuseum.xml b/RMuseum/RMuseum.xml index 80293aaf..d9821015 100644 --- a/RMuseum/RMuseum.xml +++ b/RMuseum/RMuseum.xml @@ -620,6 +620,15 @@ Captcha service + + + Gets audio narrations, user must have narration::moderate permission to be able to see all users narrations + + + default: false, user must have narration::moderate permission to be able to see all users narrations + default: -1, unfiltered + + imports data from ganjoor MySql database @@ -1404,7 +1413,7 @@ import - + moderate @@ -1534,6 +1543,16 @@ Audio Review Status + + + All / Unfiltered (for queris) + + + + + Draft + + pending for review @@ -3199,6 +3218,15 @@ Audio Narration Service + + + returns list of narrations + + + send Guid.Empty if you want all narrations + + + imports data from ganjoor MySql database @@ -3919,6 +3947,15 @@ Audio Narration Service Implementation + + + returns list of narrations + + + send Guid.Empty if you want all narrations + + + imports data from ganjoor MySql database diff --git a/RMuseum/Services/IAudioNarrationService.cs b/RMuseum/Services/IAudioNarrationService.cs index 86286ee1..98687b32 100644 --- a/RMuseum/Services/IAudioNarrationService.cs +++ b/RMuseum/Services/IAudioNarrationService.cs @@ -1,4 +1,6 @@ -using RSecurityBackend.Models.Generic; +using RMuseum.Models.GanjoorAudio; +using RMuseum.Models.GanjoorAudio.ViewModels; +using RSecurityBackend.Models.Generic; using System; using System.Threading.Tasks; @@ -9,6 +11,15 @@ namespace RMuseum.Services /// public interface IAudioNarrationService { + /// + /// returns list of narrations + /// + /// + /// send Guid.Empty if you want all narrations + /// + /// + public Task> GetAll(PagingParameterModel paging, Guid filteredUserId, AudioReviewStatus status); + /// /// imports data from ganjoor MySql database /// diff --git a/RMuseum/Services/Implementation/AudioNarrationService.cs b/RMuseum/Services/Implementation/AudioNarrationService.cs index 6172bc66..b7e7843c 100644 --- a/RMuseum/Services/Implementation/AudioNarrationService.cs +++ b/RMuseum/Services/Implementation/AudioNarrationService.cs @@ -3,21 +3,64 @@ using Microsoft.Extensions.Configuration; using MySql.Data.MySqlClient; using RMuseum.DbContext; using RMuseum.Models.GanjoorAudio; +using RMuseum.Models.GanjoorAudio.ViewModels; using RSecurityBackend.Models.Generic; +using RSecurityBackend.Services.Implementation; using System; +using System.Collections.Generic; using System.Data; using System.IO; using System.Linq; -using System.Security.Policy; using System.Threading.Tasks; namespace RMuseum.Services.Implementation { + /// /// Audio Narration Service Implementation /// public class AudioNarrationService : IAudioNarrationService { + /// + /// returns list of narrations + /// + /// + /// send Guid.Empty if you want all narrations + /// + /// + public async Task> GetAll(PagingParameterModel paging, Guid filteredUserId, AudioReviewStatus status) + { + try + { + var source = + + _context.AudioFiles + .Include(a => a.Owner) + .Where(a => + (filteredUserId == Guid.Empty || a.OwnerId == filteredUserId) + && + (status == AudioReviewStatus.All || a.ReviewStatus == status) + ) + .OrderBy(a => a.UploadDate) + .AsQueryable(); + + (PaginationMetadata PagingMeta, PoemNarration[] Items) paginatedResult = + await QueryablePaginator.Paginate(source, paging); + + List res = new List(); + foreach(PoemNarration audio in paginatedResult.Items) + { + res.Add(new PoemNarrationViewModel(audio)); + } + + return new RServiceResult<(PaginationMetadata PagingMeta, PoemNarrationViewModel[] Items)>((paginatedResult.PagingMeta, res.ToArray())); + } + catch (Exception exp) + { + return new RServiceResult<(PaginationMetadata PagingMeta, PoemNarrationViewModel[] Items)>((PagingMeta: null, Items: null), exp.ToString()); + } + } + /// /// imports data from ganjoor MySql database /// diff --git a/RSecurityBackend/Models/Generic/PagingParameterModel.cs b/RSecurityBackend/Models/Generic/PagingParameterModel.cs index 12243baa..114c6b5c 100644 --- a/RSecurityBackend/Models/Generic/PagingParameterModel.cs +++ b/RSecurityBackend/Models/Generic/PagingParameterModel.cs @@ -14,17 +14,17 @@ const int MaxPageSize = 1000; /// - /// page number + /// page number starting from 1 /// public int PageNumber { get; set; } = 1; /// - /// actual page size + /// actual page size (-1 == all items) /// private int _pageSize { get; set; } = -1; /// - /// settable page size + /// settable page size (-1 == all items, 1000 maxpagesize) /// public int PageSize { diff --git a/RSecurityBackend/RSecurityBackend.xml b/RSecurityBackend/RSecurityBackend.xml index 7f5fbf08..1c6f20f6 100644 --- a/RSecurityBackend/RSecurityBackend.xml +++ b/RSecurityBackend/RSecurityBackend.xml @@ -1669,17 +1669,17 @@ - page number + page number starting from 1 - actual page size + actual page size (-1 == all items) - settable page size + settable page size (-1 == all items, 1000 maxpagesize)