Advertisement
Jeffeek

Untitled

Feb 15th, 2023 (edited)
221
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 25.87 KB | None | 0 0
  1. public enum OperationType
  2. {
  3. Create,
  4. Undo,
  5. Redo,
  6. Delete,
  7. Add,
  8. Move
  9. }
  10.  
  11. public enum SlideType : byte
  12. {
  13. Introduction = 1,
  14. Content = 2,
  15. FinancialInstrument = 3
  16. }
  17.  
  18. public class SlideDeckSession
  19. {
  20. public Guid Id { get; set; }
  21. public int LanguageId { get; set; }
  22. public List<SlideDeckStep> Steps { get; set; } = new();
  23. public List<SlideDeckSlide> Slides { get; set; } = new();
  24. }
  25.  
  26. public class SlideDeckSlide
  27. {
  28. public SlideType Type { get; set; }
  29. public string? Name { get; set; }
  30. public string Path { get; set; } = default!;
  31. public int LanguageId { get; set; }
  32. public int? ContentId { get; set; }
  33. public int? ContentTypeId { get; set; }
  34. public int? FinancialInstrumentId { get; set; }
  35. public int? FinancialInstrumentTypeId { get; set; }
  36. }
  37.  
  38. public class SlideDeckStep
  39. {
  40. public OperationType Type { get; set; }
  41. public List<int>? SlideIds { get; set; }
  42. public bool Undone { get; set; }
  43. }
  44.  
  45. public class AddContentHandler : BaseAddHandler<AddContentRequest, AddContentInfo>
  46. {
  47. public AddContentHandler(
  48. IOptions<StorageOptions> storageOptions
  49. , IMediator mediator
  50. , IDbContextFactory<ApplicationDbContext> dbContextFactory
  51. ) : base(storageOptions, mediator, dbContextFactory)
  52. {
  53. }
  54.  
  55. protected override Task<int> GetItemLanguageIdAsync(int id, int languageId, ApplicationDbContext db, CancellationToken cancellationToken)
  56. => db.Set<LocalizedContent>()
  57. .Where(x => x.Id == id && !x.IsDeleted)
  58. .OrderByDescending(x => x.LanguageId == languageId)
  59. .ThenByDescending(x => x.Language.IsDefault)
  60. .ThenBy(x => x.Language.Priority)
  61. .Select(x => x.LanguageId)
  62. .FirstOrDefaultAsync(cancellationToken);
  63.  
  64. protected override SlideDeckSlide GetSlideInfo(AddContentInfo item, int itemLanguageId, string slideName, int stepIndex, int pictureIndex)
  65. => new()
  66. {
  67. ContentId = item.Id,
  68. ContentTypeId = item.GetItemTypeId(),
  69. LanguageId = itemLanguageId,
  70. Type = SlideType.Content,
  71. Name = slideName,
  72. Path = GetScreenShotUrlPath(stepIndex, pictureIndex)
  73. };
  74.  
  75. protected override Task SendTransformRequestAsync(int id, int typeId, int languageId, CancellationToken cancellationToken)
  76. => _mediator.Send(new TransformRequest
  77. {
  78. Id = id,
  79. LanguageId = languageId,
  80. ContentTypeId = typeId
  81. }, cancellationToken);
  82.  
  83. protected override Task<Asset?> GetPresentationContentAsync(int id, int languageId, ApplicationDbContext db, CancellationToken cancellationToken)
  84. => db.Set<LocalizedContent>()
  85. .Where(x => x.Id == id && x.LanguageId == languageId)
  86. .Select(x => x.SlideManagerContent)
  87. .FirstOrDefaultAsync(cancellationToken);
  88.  
  89. protected override Task<string?> GetSlideNameAsync(int id, int languageId, ApplicationDbContext db, CancellationToken cancellationToken)
  90. => db.Set<LocalizedContent>()
  91. .Where(x => x.Id == id && x.LanguageId == languageId)
  92. .Select(x => x.Title)
  93. .FirstOrDefaultAsync(cancellationToken);
  94. }
  95.  
  96. public class AddFinancialInstrumentHandler : BaseAddHandler<AddFinancialInstrumentRequest, AddFinancialInstrumentInfo>
  97. {
  98. public AddFinancialInstrumentHandler(
  99. IOptions<StorageOptions> storageOptions
  100. , IMediator mediator
  101. , IDbContextFactory<ApplicationDbContext> dbContextFactory
  102. ) : base(storageOptions, mediator, dbContextFactory)
  103. {
  104. }
  105.  
  106. protected override Task<int> GetItemLanguageIdAsync(int id, int languageId, ApplicationDbContext db, CancellationToken cancellationToken)
  107. => db.Set<LocalizedFinancialInstrument>()
  108. .Where(x => x.Id == id)
  109. .OrderByDescending(x => x.LanguageId == languageId)
  110. .ThenByDescending(x => x.Language.IsDefault)
  111. .ThenBy(x => x.Language.Priority)
  112. .Select(x => x.LanguageId)
  113. .FirstOrDefaultAsync(cancellationToken);
  114.  
  115. protected override SlideDeckSlide GetSlideInfo(AddFinancialInstrumentInfo item, int itemLanguageId, string slideName, int stepIndex, int pictureIndex)
  116. => new()
  117. {
  118. FinancialInstrumentId = item.Id,
  119. FinancialInstrumentTypeId = item.GetItemTypeId(),
  120. LanguageId = itemLanguageId,
  121. Type = SlideType.FinancialInstrument,
  122. Name = slideName,
  123. Path = GetScreenShotUrlPath(stepIndex, pictureIndex)
  124. };
  125.  
  126. protected override Task SendTransformRequestAsync(int id, int typeId, int languageId, CancellationToken cancellationToken)
  127. => _mediator.Send(new TransformRequest
  128. {
  129. Id = id,
  130. LanguageId = languageId,
  131. FinancialInstrumentTypeId = typeId
  132. }, cancellationToken);
  133.  
  134. protected override Task<Asset?> GetPresentationContentAsync(int id, int languageId, ApplicationDbContext db, CancellationToken cancellationToken)
  135. => db.Set<LocalizedFinancialInstrument>()
  136. .Where(x => x.Id == id && x.LanguageId == languageId)
  137. .Select(x => x.SlideManagerContent)
  138. .FirstOrDefaultAsync(cancellationToken);
  139.  
  140. protected override Task<string?> GetSlideNameAsync(int id, int languageId, ApplicationDbContext db, CancellationToken cancellationToken)
  141. => db.Set<LocalizedFinancialInstrument>()
  142. .Where(x => x.Id == id && x.LanguageId == languageId)
  143. .Select(x => x.Name)
  144. .FirstOrDefaultAsync(cancellationToken);
  145. }
  146.  
  147. public abstract class BaseHandler<TRequest, TResponse> : IRequestHandler<TRequest, TResponse>
  148. where TRequest : IRequest<TResponse>
  149. {
  150. private const string PresentationName = "result.pptx";
  151. protected SlideDeckSession CurrentSession { get; set; } = default!;
  152.  
  153. private string BaseFolder => Path.Combine(_storageOptions.DocumentsFolder, _storageOptions.TempFolder);
  154. private LanguageInfo Language { get; set; } = default!;
  155. protected string CurrentPresentationPath => Path.Combine(GetCurrentPresentationFolder(), PresentationName);
  156. protected int NextStepIndex => CurrentSession.Steps.Count;
  157. protected string NextStepFolder => Path.Combine(GetCurrentPresentationFolder(), NextStepIndex.ToString());
  158. protected string NextStepPresentationPath => Path.Combine(NextStepFolder, PresentationName);
  159.  
  160. // ReSharper disable once StaticMemberInGenericType
  161. private static readonly JsonSerializerOptions JsonSerializerOptions = new()
  162. {
  163. DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
  164. Converters = { new JsonStringEnumConverter() },
  165. #if DEBUG
  166. WriteIndented = true
  167. #endif
  168. };
  169.  
  170. protected readonly StorageOptions _storageOptions;
  171. protected readonly IMediator _mediator;
  172.  
  173. protected BaseHandler(
  174. IOptions<StorageOptions> storageOptions
  175. , IMediator mediator
  176. )
  177. {
  178. _storageOptions = storageOptions.Value;
  179. _mediator = mediator;
  180. }
  181.  
  182. public abstract Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken);
  183.  
  184. private Task<LanguageInfo> GetLanguageAsync(int languageId, CancellationToken cancellationToken)
  185. => GetLanguage(languageId, _mediator, cancellationToken);
  186.  
  187. private string GetCurrentPresentationFolder()
  188. => Path.Combine(BaseFolder, CurrentSession.Id.ToString(), Language.Code);
  189.  
  190. protected string GetCurrentPresentationPath()
  191. => Path.Combine(GetCurrentPresentationFolder(), PresentationName);
  192.  
  193. protected string GetPresentationWithDisclaimersPath()
  194. => Path.Combine(GetCurrentPresentationFolder(), $"{CurrentSession.Id}.pptx");
  195.  
  196. private string GetPresentationFolderByStep(int stepIndex)
  197. => Path.Combine(GetCurrentPresentationFolder(), stepIndex.ToString());
  198.  
  199. protected string GetPresentationPathByStep(int stepIndex)
  200. => Path.Combine(GetPresentationFolderByStep(stepIndex), PresentationName);
  201.  
  202. protected Task SendScreenShotRequestAsync(string presentationPath, List<(int, string)> slides, CancellationToken cancellationToken)
  203. => _mediator.Send(new TakePresentationScreenShotsRequest
  204. {
  205. PresentationPath = presentationPath,
  206. Slides = slides
  207. }, cancellationToken);
  208.  
  209. protected string GetScreenShotLocalPath(int stepIndex, int slideIndex)
  210. => Path.Combine(GetCurrentPresentationFolder(), stepIndex.ToString(), $"{slideIndex}.png");
  211.  
  212. protected string GetScreenShotUrlPath(int stepIndex, int slideIndex)
  213. => $"docs/{_storageOptions.TempFolder}/{CurrentSession.Id}/{Language.Code}/{stepIndex}/{slideIndex}.png";
  214.  
  215. protected async Task OpenSessionAsync(Guid sessionId, CancellationToken cancellationToken)
  216. {
  217. await using var stream = new FileStream(GetCurrentStatePath(sessionId), FileMode.Open);
  218.  
  219. CurrentSession = await JsonSerializer.DeserializeAsync<SlideDeckSession>(stream, JsonSerializerOptions, cancellationToken) ?? throw new Exception($"Can not deserialize current state of slide deck session: {sessionId}");
  220. Language = await GetLanguageAsync(CurrentSession.LanguageId, cancellationToken);
  221. }
  222.  
  223. protected Task SaveSessionAsync(CancellationToken cancellationToken)
  224. => File.WriteAllTextAsync(
  225. GetCurrentStatePath(CurrentSession.Id),
  226. JsonSerializer.Serialize(CurrentSession, JsonSerializerOptions),
  227. cancellationToken);
  228.  
  229. private string GetCurrentStatePath(Guid sessionId)
  230. => Path.Combine(BaseFolder, sessionId.ToString(), "currentState.json");
  231.  
  232. protected static IPresentation MergePresentations(IPresentation firstPresentation, IPresentation secondPresentation)
  233. {
  234. var resultPresentation = new Presentation();
  235.  
  236. resultPresentation.SlideSize.SetSize(firstPresentation.SlideSize.Size.Width, firstPresentation.SlideSize.Size.Height, SlideSizeScaleType.EnsureFit);
  237.  
  238. MergeInternal(firstPresentation);
  239. MergeInternal(secondPresentation);
  240.  
  241. resultPresentation.Slides.RemoveAt(0);
  242.  
  243. return resultPresentation;
  244.  
  245. void MergeInternal(IPresentation toMerge)
  246. {
  247. foreach (var slide in toMerge.Slides)
  248. resultPresentation.Slides.AddClone(slide);
  249. }
  250. }
  251. }
  252.  
  253. public abstract class BaseAddHandler<TRequest, TItem> : BaseHandler<TRequest, SlideDeckSession>
  254. where TItem : BaseAddInfo
  255. where TRequest : BaseAddRequest<TItem>
  256. {
  257. private readonly IDbContextFactory<ApplicationDbContext> _dbContextFactory;
  258.  
  259. protected BaseAddHandler(
  260. IOptions<StorageOptions> storageOptions
  261. , IMediator mediator
  262. , IDbContextFactory<ApplicationDbContext> dbContextFactory
  263. ) : base(storageOptions, mediator)
  264. {
  265. _dbContextFactory = dbContextFactory;
  266. }
  267.  
  268. public override async Task<SlideDeckSession> Handle(TRequest request, CancellationToken cancellationToken)
  269. {
  270. await OpenSessionAsync(request.SessionId, cancellationToken);
  271.  
  272. var slideIndexes = new List<int>();
  273. var currentStepSlideIndex = 0;
  274.  
  275. Directory.CreateDirectory(NextStepFolder);
  276.  
  277. await using var db = await _dbContextFactory.CreateDbContextAsync(cancellationToken);
  278.  
  279. foreach (var item in request.Items)
  280. {
  281. var itemLanguageId = await GetItemLanguageIdAsync(item.Id, CurrentSession.LanguageId, db, cancellationToken);
  282. var presentationContent = await GetPresentationContentAsync(item.Id, itemLanguageId, db, cancellationToken);
  283.  
  284. if (presentationContent == null)
  285. {
  286. await SendTransformRequestAsync(item.Id, item.GetItemTypeId(), itemLanguageId, cancellationToken);
  287.  
  288. presentationContent = await GetPresentationContentAsync(item.Id, itemLanguageId, db, cancellationToken);
  289.  
  290. if (presentationContent == null) continue; // todo: temp solution. wait till all types of template will be created
  291. }
  292.  
  293. var slideName = await GetSlideNameAsync(item.Id, itemLanguageId, db, cancellationToken);
  294.  
  295. var itemSlideIndexes = await ProcessItemAsync(
  296. Path.Combine(_storageOptions.DocumentsFolder, presentationContent.Link),
  297. NextStepIndex,
  298. currentStepSlideIndex,
  299. cancellationToken);
  300.  
  301. foreach (var itemSlideIndex in itemSlideIndexes)
  302. {
  303. CurrentSession.Slides.Add(GetSlideInfo(item, itemLanguageId, slideName!, NextStepIndex, currentStepSlideIndex));
  304. slideIndexes.Add(itemSlideIndex);
  305. currentStepSlideIndex++;
  306. }
  307.  
  308. File.Copy(NextStepPresentationPath, CurrentPresentationPath, true);
  309. }
  310.  
  311. if (!slideIndexes.Any()) return CurrentSession;
  312.  
  313. CurrentSession.Steps.Add(new SlideDeckStep
  314. {
  315. Type = OperationType.Add,
  316. SlideIds = slideIndexes
  317. });
  318.  
  319. await SaveSessionAsync(cancellationToken);
  320.  
  321. return CurrentSession;
  322. }
  323.  
  324. private async Task<List<int>> ProcessItemAsync(
  325. string itemPresentationPath
  326. , int stepIndex
  327. , int slideIndexCounter
  328. , CancellationToken cancellationToken
  329. )
  330. {
  331. var stepPresentationPath = GetPresentationPathByStep(stepIndex);
  332.  
  333. using var currentPresentation = new Presentation(CurrentPresentationPath);
  334. using var itemPresentation = new Presentation(itemPresentationPath);
  335. using var mergedPresentations = MergePresentations(currentPresentation, itemPresentation);
  336. mergedPresentations.Save(stepPresentationPath, SaveFormat.Pptx);
  337.  
  338. var slidesToScreenShot = Enumerable.Range(mergedPresentations.Slides.Count - itemPresentation.Slides.Count, itemPresentation.Slides.Count)
  339. .Select(slideIndex => (slideIndex, GetScreenShotLocalPath(stepIndex, slideIndexCounter++))).ToList();
  340.  
  341. await SendScreenShotRequestAsync(stepPresentationPath, slidesToScreenShot, cancellationToken);
  342.  
  343. return slidesToScreenShot.Select(x => x.slideIndex).ToList();
  344. }
  345.  
  346. protected abstract SlideDeckSlide GetSlideInfo(TItem item, int itemLanguageId, string slideName, int stepIndex, int pictureIndex);
  347.  
  348. protected abstract Task<int> GetItemLanguageIdAsync(int id, int languageId, ApplicationDbContext db, CancellationToken cancellationToken);
  349.  
  350. protected abstract Task SendTransformRequestAsync(int id, int typeId, int languageId, CancellationToken cancellationToken);
  351.  
  352. protected abstract Task<Asset?> GetPresentationContentAsync(int id, int languageId, ApplicationDbContext db, CancellationToken cancellationToken);
  353.  
  354. protected abstract Task<string?> GetSlideNameAsync(int id, int languageId, ApplicationDbContext db, CancellationToken cancellationToken);
  355. }
  356.  
  357. public class CreateHandler : BaseHandler<CreateRequest, SlideDeckSession>
  358. {
  359. public CreateHandler(
  360. IMediator mediator
  361. , IOptions<StorageOptions> storageOptions
  362. ) : base(storageOptions, mediator)
  363. {
  364. }
  365.  
  366. public override async Task<SlideDeckSession> Handle(CreateRequest request, CancellationToken cancellationToken)
  367. {
  368. CurrentSession = new SlideDeckSession
  369. {
  370. Id = Guid.NewGuid(),
  371. LanguageId = request.LanguageId
  372. };
  373.  
  374. var (presentationPath, slidesCount) = await _mediator.Send(new TransformRequest { Id = CurrentSession.Id, LanguageId = request.LanguageId }, cancellationToken);
  375.  
  376. await SaveSessionAsync(cancellationToken);
  377. await OpenSessionAsync(CurrentSession.Id, cancellationToken);
  378.  
  379. File.Copy(presentationPath, GetCurrentPresentationPath());
  380.  
  381. var titleSlideScreenShots = Enumerable.Range(0, slidesCount)
  382. .Select(slideId => (slideId, GetScreenShotLocalPath(0, slideId)))
  383. .ToList();
  384.  
  385. CurrentSession.Slides.AddRange(titleSlideScreenShots.Select(x => new SlideDeckSlide
  386. {
  387. Path = GetScreenShotUrlPath(0, x.slideId),
  388. LanguageId = request.LanguageId,
  389. Type = SlideType.Introduction
  390. }));
  391. CurrentSession.Steps.Add(new()
  392. {
  393. Type = OperationType.Create,
  394. SlideIds = titleSlideScreenShots.Select(x => x.slideId).ToList()
  395. });
  396.  
  397. await SendScreenShotRequestAsync(presentationPath, titleSlideScreenShots, cancellationToken);
  398. await SaveSessionAsync(cancellationToken);
  399.  
  400. return CurrentSession;
  401. }
  402. }
  403.  
  404. public class DownloadHandler : BaseHandler<DownloadRequest, FileStream>
  405. {
  406. private readonly IMapper _mapper;
  407. private readonly IDbContextFactory<ApplicationDbContext> _dbContextFactory;
  408.  
  409. public DownloadHandler(
  410. IOptions<StorageOptions> storageOptions
  411. , IMediator mediator
  412. , IMapper mapper
  413. , IDbContextFactory<ApplicationDbContext> dbContextFactory
  414. ) : base(storageOptions, mediator)
  415. {
  416. _mapper = mapper;
  417. _dbContextFactory = dbContextFactory;
  418. }
  419.  
  420. public override async Task<FileStream> Handle(DownloadRequest request, CancellationToken cancellationToken)
  421. {
  422. await OpenSessionAsync(request.SessionId, cancellationToken);
  423.  
  424. var resultFilePath = GetPresentationWithDisclaimersPath();
  425.  
  426. await using var db = await _dbContextFactory.CreateDbContextAsync(cancellationToken);
  427. var disclaimerTypes = new HashSet<int>(2);
  428. var disclaimerContents = new List<AssetInfo>(2);
  429.  
  430. foreach (var slide in CurrentSession.Slides.Where(x => x.Type is SlideType.FinancialInstrument or SlideType.Content))
  431. disclaimerTypes.Add(slide.Type switch
  432. {
  433. SlideType.Content when slide.ContentTypeId is
  434. (int)ContentType.AllAssetsRatingComment or
  435. (int)ContentType.AssetRatingComment or
  436. (int)ContentType.SubAssetRatingComment or
  437. (int)ContentType.WorldRatingComment or
  438. (int)ContentType.RegionRatingComment or
  439. (int)ContentType.CountryRatingComment or
  440. (int)ContentType.AllSectorsRatingComment or
  441. (int)ContentType.SectorTypeRatingComment or
  442. (int)ContentType.SectorRatingComment => (int)ContentType.DisclaimerWithoutPerformance,
  443. _ => (int)ContentType.DisclaimerWithPerformance
  444. });
  445.  
  446. foreach (var contentTypeId in disclaimerTypes)
  447. {
  448. var content = await GetDisclaimerContentAsync(db, CurrentSession.LanguageId, contentTypeId, cancellationToken);
  449.  
  450. if (content != null)
  451. disclaimerContents.Add(content);
  452. }
  453.  
  454. File.Copy(CurrentPresentationPath, resultFilePath, true);
  455.  
  456. foreach (var disclaimerAsset in disclaimerContents)
  457. {
  458. using var currentPresentation = new Presentation(resultFilePath);
  459. using var disclaimerPresentation = new Presentation(Path.Combine(_storageOptions.DocumentsFolder, disclaimerAsset.Link));
  460. using var mergedPresentations = MergePresentations(currentPresentation, disclaimerPresentation);
  461.  
  462. mergedPresentations.Save(resultFilePath, SaveFormat.Pptx);
  463. }
  464.  
  465. return new FileStream(resultFilePath, FileMode.Open, FileAccess.Read);
  466. }
  467.  
  468. private async Task<AssetInfo?> GetDisclaimerContentAsync(ApplicationDbContext db, int languageId, int contentTypeId, CancellationToken cancellationToken)
  469. {
  470. var disclaimerInfo =
  471. await (contentTypeId == (int)ContentType.DisclaimerWithPerformance
  472. ? db.Set<DisclaimerWithPerformance>()
  473. .ApplyQuery(new Application.Features.ContentManagement.Content.DisclaimerWithPerformance.GetLatestQuery())
  474. .SelectMany(x => x.Localizations)
  475. : db.Set<DisclaimerWithoutPerformance>()
  476. .ApplyQuery(new Application.Features.ContentManagement.Content.DisclaimerWithoutPerformance.GetLatestQuery())
  477. .SelectMany(x => x.Localizations))
  478. .OrderByDescending(x => x.LanguageId == languageId)
  479. .ThenByDescending(x => x.Language.IsDefault)
  480. .ThenBy(x => x.Language.Priority)
  481. .Select(x => new { x.Id, x.LanguageId, x.SlideManagerContent })
  482. .FirstOrDefaultAsync(cancellationToken);
  483.  
  484. if (disclaimerInfo == null) return null;
  485.  
  486. if (disclaimerInfo.SlideManagerContent != null)
  487. return _mapper.Map<AssetInfo>(disclaimerInfo.SlideManagerContent);
  488.  
  489. await _mediator.Send(new Transformation.Features.Content.TransformRequest
  490. {
  491. Id = disclaimerInfo.Id,
  492. LanguageId = disclaimerInfo.LanguageId,
  493. ContentTypeId = contentTypeId
  494. }, cancellationToken);
  495.  
  496. return await db.Set<LocalizedContent>()
  497. .Where(x => x.Id == disclaimerInfo.Id && x.LanguageId == disclaimerInfo.LanguageId)
  498. .Select(x => x.SlideManagerContent)
  499. .ProjectTo<AssetInfo>(_mapper.ConfigurationProvider)
  500. .FirstOrDefaultAsync(cancellationToken);
  501. }
  502. }
  503.  
  504. public class RemoveHandler : BaseHandler<RemoveRequest, SlideDeckSession>
  505. {
  506. public RemoveHandler(IOptions<StorageOptions> storageOptions, IMediator mediator) : base(storageOptions, mediator)
  507. {
  508. }
  509.  
  510. public override async Task<SlideDeckSession> Handle(RemoveRequest request, CancellationToken cancellationToken)
  511. {
  512. await OpenSessionAsync(request.SessionId, cancellationToken);
  513. Directory.CreateDirectory(NextStepFolder);
  514.  
  515. using var presentation = new Presentation(CurrentPresentationPath);
  516.  
  517. if (presentation.Slides.Count <= request.SlideIndex)
  518. throw new BadRequestException("Slide index was outside of valid values");
  519.  
  520. presentation.Slides.RemoveAt(request.SlideIndex);
  521.  
  522. presentation.Save(NextStepPresentationPath, SaveFormat.Pptx);
  523. presentation.Save(CurrentPresentationPath, SaveFormat.Pptx);
  524.  
  525. CurrentSession.Slides.RemoveAt(request.SlideIndex);
  526. CurrentSession.Steps.Add(new()
  527. {
  528. Type = OperationType.Delete,
  529. SlideIds = new()
  530. {
  531. request.SlideIndex
  532. }
  533. });
  534.  
  535. await SaveSessionAsync(cancellationToken);
  536.  
  537. return CurrentSession;
  538. }
  539. }
  540.  
  541. public class UndoHandler : BaseHandler<UndoRequest, SlideDeckSession>
  542. {
  543. public UndoHandler(IOptions<StorageOptions> storageOptions, IMediator mediator) : base(storageOptions, mediator)
  544. {
  545. }
  546.  
  547. public override async Task<SlideDeckSession> Handle(UndoRequest request, CancellationToken cancellationToken)
  548. {
  549. await OpenSessionAsync(request.SessionId, cancellationToken);
  550.  
  551. var lastStepToUndo = GetLastUndoableStep() ?? throw new BadRequestException("Can't undo last step");
  552.  
  553. Directory.CreateDirectory(NextStepFolder);
  554.  
  555. switch (lastStepToUndo.Step.Type)
  556. {
  557. case OperationType.Add:
  558. {
  559. foreach (var stepSlideId in lastStepToUndo.Step.SlideIds!.OrderByDescending(x => x))
  560. CurrentSession.Slides.RemoveAt(stepSlideId);
  561.  
  562. break;
  563. }
  564. case OperationType.Move:
  565. (CurrentSession.Slides[lastStepToUndo.Step.SlideIds![0]], CurrentSession.Slides[lastStepToUndo.Step.SlideIds![1]]) =
  566. (CurrentSession.Slides[lastStepToUndo.Step.SlideIds![1]], CurrentSession.Slides[lastStepToUndo.Step.SlideIds![0]]);
  567.  
  568. break;
  569. case OperationType.Delete:
  570. {
  571. var previousSessionState = await OpenSessionByStepAsync(lastStepToUndo.PreviousStepIndex, cancellationToken);
  572.  
  573. CurrentSession.Slides.Insert(lastStepToUndo.Step.SlideIds![0], previousSessionState.Slides[lastStepToUndo.Step.SlideIds![0]]);
  574.  
  575. break;
  576. }
  577. }
  578.  
  579. File.Copy(GetPresentationPathByStep(lastStepToUndo.PreviousStepIndex), NextStepPresentationPath);
  580. File.Copy(NextStepPresentationPath, CurrentPresentationPath, true);
  581.  
  582. lastStepToUndo.Step.Undone = true;
  583. CurrentSession.Steps.Add(new()
  584. {
  585. SlideIds = lastStepToUndo.Step.SlideIds,
  586. Type = OperationType.Undo
  587. });
  588.  
  589. await SaveSessionAsync(cancellationToken);
  590.  
  591. return CurrentSession;
  592. }
  593.  
  594. private (SlideDeckStep Step, int StepIndex, int PreviousStepIndex)? GetLastUndoableStep()
  595. {
  596. for (var i = CurrentSession.Steps.Count - 1; i >= 1; i--)
  597. if (CurrentSession.Steps[i].Type is OperationType.Add or OperationType.Delete or OperationType.Move && !CurrentSession.Steps[i].Undone)
  598. return (CurrentSession.Steps[i], i, i - 1);
  599.  
  600. return null;
  601. }
  602. }
  603.  
  604. public class TakePresentationScreenShotsHandler : IRequestHandler<TakePresentationScreenShotsRequest, Unit>
  605. {
  606. private const int DesiredX = 1280;
  607. private const int DesiredY = 720;
  608.  
  609. public Task<Unit> Handle(TakePresentationScreenShotsRequest request, CancellationToken cancellationToken)
  610. {
  611. if (!File.Exists(request.PresentationPath)) throw new FileNotFoundException("Not found presentation", request.PresentationPath);
  612.  
  613. using var presentation = new Presentation(request.PresentationPath);
  614.  
  615. var scaleX = (float)(1.0 / presentation.SlideSize.Size.Width) * DesiredX;
  616. var scaleY = (float)(1.0 / presentation.SlideSize.Size.Height) * DesiredY;
  617.  
  618. foreach (var (slideId, screenShotPath) in request.Slides)
  619. {
  620. var bmp = presentation.Slides[slideId].GetThumbnail(scaleX, scaleY);
  621. Directory.CreateDirectory(Directory.GetParent(screenShotPath)!.FullName);
  622. bmp.Save(screenShotPath, System.Drawing.Imaging.ImageFormat.Png);
  623. }
  624.  
  625. return Unit.Task;
  626. }
  627. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement