Очистка и анализ кода

This commit is contained in:
Фролов 2021-08-09 15:41:42 +05:00
parent a14e5134bf
commit 3a325f6c94
36 changed files with 120 additions and 140 deletions

View File

@ -1,12 +1,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections.Generic;
namespace AsbCloudApp.Data
{
public class ClusterStatDto: ClusterDto
public class ClusterStatDto : ClusterDto
{
public IEnumerable<WellStatDto> WellsStat { get; set; }
}

View File

@ -1,10 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AsbCloudApp.Data
namespace AsbCloudApp.Data
{
public class WellOperationDto : IId
{

View File

@ -1,6 +1,6 @@
namespace AsbCloudApp.Data
{
public class WellSectionDto: IId
public class WellSectionDto : IId
{
public int Id { get; set; }
/// <summary>

View File

@ -6,8 +6,8 @@ namespace AsbCloudApp.Services
{
public interface IAnalyticsService
{
PaginationContainer<TelemetryOperationDto> GetOperationsByWell(int idWell,
IEnumerable<int> categoryids = default, DateTime begin = default,
PaginationContainer<TelemetryOperationDto> GetOperationsByWell(int idWell,
IEnumerable<int> categoryids = default, DateTime begin = default,
DateTime end = default, int skip = 0, int take = 32);
IEnumerable<WellDepthToDayDto> GetWellDepthToDay(int idWell);
IEnumerable<WellDepthToIntervalDto> GetWellDepthToInterval(int idWell,

View File

@ -1,13 +1,13 @@
using System;
using AsbCloudApp.Data;
using System;
using System.Collections.Generic;
using AsbCloudApp.Data;
namespace AsbCloudApp.Services
{
public interface IFileService
{
string RootPath { get; }
IDictionary<string, int> SaveFilesPropertiesToDb(int idWell,
IDictionary<string, int> SaveFilesPropertiesToDb(int idWell,
int idCategory, IEnumerable<(string fileName, int idWell, int idCategory,
DateTime date, int idUser)> filesInfo);

View File

@ -7,13 +7,13 @@ namespace AsbCloudApp.Services
public interface IReportService
{
int ReportCategoryId { get; }
int CreateReport(int idWell, int idUser, int stepSeconds,
int CreateReport(int idWell, int idUser, int stepSeconds,
int format, DateTime begin, DateTime end,
Action<float, string, int> handleReportProgress,
Action<float, string, int> handleReportProgress,
Action<string, int> handleReportName);
int GetReportPagesCount(int idWell, DateTime begin, DateTime end,
int GetReportPagesCount(int idWell, DateTime begin, DateTime end,
int stepSeconds, int format);
IEnumerable<ReportPropertiesDto> GetSuitableReports(int idWell,
IEnumerable<ReportPropertiesDto> GetSuitableReports(int idWell,
DateTime begin, DateTime end, int stepSeconds, int format);
DatesRangeDto GetReportsDatesRange(int idWell);
}

View File

@ -1,5 +1,5 @@
using System.Collections.Generic;
using AsbCloudApp.Data;
using AsbCloudApp.Data;
using System.Collections.Generic;
namespace AsbCloudApp.Services
{

View File

@ -139,7 +139,8 @@ namespace AsbCloudDb.Model
.HasConstraintName("t_well_t_telemetry_id_fk");
});
modelBuilder.Entity<RelationCompanyWell>(entity => {
modelBuilder.Entity<RelationCompanyWell>(entity =>
{
entity.HasKey(nameof(RelationCompanyWell.IdCompany), nameof(RelationCompanyWell.IdWell));
@ -298,7 +299,8 @@ namespace AsbCloudDb.Model
});
});
modelBuilder.Entity<RelationCompanyWell>(entity => {
modelBuilder.Entity<RelationCompanyWell>(entity =>
{
entity.HasData(new List<RelationCompanyWell> {
new RelationCompanyWell{ IdWell = 1, IdCompany = 1},
new RelationCompanyWell{ IdWell = 2, IdCompany = 1},

View File

@ -35,7 +35,7 @@ namespace AsbCloudDb.Model
Task<int> SaveChangesAsync(CancellationToken cancellationToken);
DbSet<TEntity> Set<TEntity>(string name) where TEntity : class;
DbSet<TEntity> Set<TEntity>() where TEntity : class;
IQueryable<Well> GetWellsForCompany(int idCompany);

View File

@ -1,6 +1,4 @@
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
#nullable disable

View File

@ -1,5 +1,5 @@
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

View File

@ -36,8 +36,8 @@ namespace AsbCloudInfrastructure
services.AddTransient<IReportService, ReportService>();
services.AddTransient<IAnalyticsService, AnalyticsService>();
services.AddTransient<IFileService, FileService>();
services.AddTransient<ILastDataService<FluidDataDto>, LastDataService<FluidDataDto, FluidData>>();
services.AddTransient<ILastDataService<MudDiagramDataDto>, LastDataService<MudDiagramDataDto, MudDiagramData>>();
services.AddTransient<ILastDataService<NnbDataDto>, LastDataService<NnbDataDto, NnbData>>();

View File

@ -1,8 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Mapster
{

View File

@ -59,13 +59,13 @@ namespace AsbSaubReport
public AnalyzeResult Analyze()
{
var messagesQuery = from item in context.Messages
where item.IdTelemetry == idTelemetry
select item;
var messagesQuery = from item in context.Messages
where item.IdTelemetry == idTelemetry
select item;
var messagesCount = messagesQuery.Count();
var messagesMinDate = messagesQuery.Min(e=>e.Date);
var messagesMaxDate = messagesQuery.Max(e=>e.Date);
var messagesMinDate = messagesQuery.Min(e => e.Date);
var messagesMaxDate = messagesQuery.Max(e => e.Date);
var dataQuery = from item in context.DataSaubBases
where item.IdTelemetry == idTelemetry
@ -74,7 +74,8 @@ namespace AsbSaubReport
var dataMinDate = dataQuery.Min(e => e.Date);
var dataMaxDate = dataQuery.Max(e => e.Date);
var result = new AnalyzeResult {
var result = new AnalyzeResult
{
MaxDate = dataMinDate < messagesMinDate ? dataMinDate : messagesMinDate,
MinDate = dataMaxDate > messagesMaxDate ? dataMaxDate : messagesMaxDate,
MessagesCount = messagesCount,

View File

@ -2,11 +2,11 @@
using AsbCloudApp.Services;
using AsbCloudDb.Model;
using AsbCloudInfrastructure.Services.Cache;
using Mapster;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Mapster;
namespace AsbCloudInfrastructure.Services
{
@ -84,8 +84,8 @@ namespace AsbCloudInfrastructure.Services
return wellDepthToIntervalData;
}
public PaginationContainer<TelemetryOperationDto> GetOperationsByWell(int idWell,
IEnumerable<int> categoryIds = default, DateTime begin = default,
public PaginationContainer<TelemetryOperationDto> GetOperationsByWell(int idWell,
IEnumerable<int> categoryIds = default, DateTime begin = default,
DateTime end = default, int skip = 0, int take = 32)
{
var telemetryId = telemetryService.GetIdTelemetryByIdWell(idWell);
@ -94,7 +94,7 @@ namespace AsbCloudInfrastructure.Services
return null;
var operations = from a in db.TelemetryAnalysis.Include(t => t.Operation)
where a.IdTelemetry == telemetryId
where a.IdTelemetry == telemetryId
select a;
if ((categoryIds != default) && (categoryIds.Any()))
@ -130,7 +130,7 @@ namespace AsbCloudInfrastructure.Services
if (operationsList.Count == 0)
return result;
foreach(var operation in operations)
foreach (var operation in operations)
{
var operationDto = new TelemetryOperationDto
{
@ -160,7 +160,7 @@ namespace AsbCloudInfrastructure.Services
var unixEnd = (end - new DateTime(1970, 1, 1)).TotalSeconds;
var operations = (from a in db.TelemetryAnalysis
where a.IdTelemetry == telemetryId &&
where a.IdTelemetry == telemetryId &&
a.UnixDate > unixBegin && a.UnixDate < unixEnd
join o in db.Operations on a.IdOperation equals o.Id
group a by new { a.IdOperation, o.Name } into g

View File

@ -24,7 +24,7 @@ namespace AsbCloudInfrastructure.Services.Cache
public int Refresh()
{
if(cached.Any())
if (cached.Any())
cached.Clear();
var dbEntities = context.Set<TEntity>().ToList();
cached.AddRange(dbEntities);

View File

@ -1,13 +1,13 @@
using System.Collections.Generic;
using System.Linq;
using AsbCloudApp.Services;
using AsbCloudDb.Model;
using AsbCloudApp.Services;
using Mapster;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
namespace AsbCloudInfrastructure.Services
{
public class CrudService<Tdto, TModel> : ICrudService<Tdto>
public class CrudService<Tdto, TModel> : ICrudService<Tdto>
where TModel : class, AsbCloudDb.Model.IId
where Tdto : AsbCloudApp.Data.IId
{

View File

@ -43,9 +43,9 @@ namespace AsbCloudInfrastructure.Services
var datEnd = dateBegin.AddSeconds(intervalSec);
var query = from data in db.DataSaubBases
where data.IdTelemetry == telemetry.Id
&& data.Date >= dateBegin && data.Date < datEnd
select data;
where data.IdTelemetry == telemetry.Id
&& data.Date >= dateBegin && data.Date < datEnd
select data;
var fullDataCount = query.Count();
@ -83,7 +83,7 @@ namespace AsbCloudInfrastructure.Services
if (oldDataSaubBase.Any())
db.DataSaubBases.RemoveRange(oldDataSaubBase);
foreach (var dto in dtos)
{

View File

@ -1,10 +1,10 @@
using System;
using System.Linq;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore;
using AsbCloudApp.Data;
using AsbCloudApp.Data;
using AsbCloudApp.Services;
using AsbCloudDb.Model;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
namespace AsbCloudInfrastructure.Services
{
@ -12,22 +12,20 @@ namespace AsbCloudInfrastructure.Services
{
public string RootPath { get; private set; }
private readonly IAsbCloudDbContext db;
private readonly ITelemetryService telemetryService;
public FileService(IAsbCloudDbContext db, ITelemetryService telemetryService)
public FileService(IAsbCloudDbContext db)
{
RootPath = "files";
this.db = db;
this.telemetryService = telemetryService;
}
public IDictionary<string, int> SaveFilesPropertiesToDb(int idWell, int idCategory,
public IDictionary<string, int> SaveFilesPropertiesToDb(int idWell, int idCategory,
IEnumerable<(string fileName, int idWell, int idCategory, DateTime date, int idUser)> filesInfo)
{
var fileIdsToNames = new Dictionary<string, int>();
foreach(var fileInfo in filesInfo)
{
foreach (var fileInfo in filesInfo)
{
var file = new File()
{
Name = fileInfo.fileName,
@ -46,7 +44,7 @@ namespace AsbCloudInfrastructure.Services
}
public PaginationContainer<FilePropertiesDto> GetFilesInfo(int idWell,
int idCategory, DateTime begin = default, DateTime end = default,
int idCategory, DateTime begin = default, DateTime end = default,
int skip = 0, int take = 32)
{
var filesInfoQuery = db.Files.Include(f => f.User)

View File

@ -1,7 +1,7 @@
using System.Linq;
using AsbCloudApp.Services;
using AsbCloudDb.Model;
using AsbCloudApp.Services;
using Mapster;
using System.Linq;
using System.Text.Json;
namespace AsbCloudInfrastructure.Services
@ -17,7 +17,7 @@ namespace AsbCloudInfrastructure.Services
public Tdto Get(int idWell, int idCategory)
{
var entity = db.LastData.FirstOrDefault(e =>
var entity = db.LastData.FirstOrDefault(e =>
e.IdWell == idWell && e.IdCategory == idCategory);
if (entity is null)
@ -41,8 +41,8 @@ namespace AsbCloudInfrastructure.Services
}
else
{
var newLastData = new LastData
{
var newLastData = new LastData
{
IdWell = idWell,
IdCategory = idCategory,
Data = model

View File

@ -26,12 +26,12 @@ namespace AsbCloudInfrastructure.Services
}
public PaginationContainer<MessageDto> GetMessages(
int idWell,
IEnumerable<int> categoryids = default,
DateTime begin = default,
DateTime end = default,
string searchString = default,
int skip = 0,
int idWell,
IEnumerable<int> categoryids = default,
DateTime begin = default,
DateTime end = default,
string searchString = default,
int skip = 0,
int take = 32)
{
var telemetryId = telemetryService.GetIdTelemetryByIdWell(idWell);
@ -45,7 +45,7 @@ namespace AsbCloudInfrastructure.Services
var messages = db.Messages.Where(m => m.IdTelemetry == telemetryId);
if((categoryids?.Any() == true) || !string.IsNullOrEmpty(searchString))
if ((categoryids?.Any() == true) || !string.IsNullOrEmpty(searchString))
{
if (!string.IsNullOrEmpty(searchString))
events = events.Where(e => e.MessageTemplate.Contains(searchString, StringComparison.OrdinalIgnoreCase));
@ -53,7 +53,7 @@ namespace AsbCloudInfrastructure.Services
if (categoryids?.Any() == true)
events = events.Where(e => categoryids.ToList().Contains(e.IdCategory));
var eventIds = events.Select(e=> e.IdEvent);
var eventIds = events.Select(e => e.IdEvent);
if (!eventIds.Any())
return null;
@ -70,10 +70,10 @@ namespace AsbCloudInfrastructure.Services
messages = messages.Where(m => m.Date <= end);
var result = new PaginationContainer<MessageDto>
{
Skip = skip,
Take = take,
Count = messages.Count()
{
Skip = skip,
Take = take,
Count = messages.Count()
};
if (skip > 0)

View File

@ -1,5 +1,5 @@
using AsbCloudDb.Model;
using AsbCloudApp.Data;
using AsbCloudApp.Data;
using AsbCloudDb.Model;
using System;
namespace AsbCloudInfrastructure.Services

View File

@ -1,5 +1,5 @@
using AsbCloudDb.Model;
using AsbCloudApp.Data;
using AsbCloudApp.Data;
using AsbCloudDb.Model;
using System.Collections.Generic;
using System.Linq;

View File

@ -43,7 +43,7 @@ namespace AsbCloudInfrastructure.Services
public bool IsCompanyInvolvedInWell(int idCompany, int idWell)
=> cacheRelationCompaniesWells.Contains(r => r.IdWell == idWell && r.IdCompany == idCompany);
public IEnumerable<WellSectionDto> GetSections(int idWell)
{
var entities = db
@ -52,8 +52,8 @@ namespace AsbCloudInfrastructure.Services
.ToList();
var dtos = entities.Adapt<WellSection, WellSectionDto>(
(s, d) => { d.SectionType = s.WellSectionType.Caption;});
(s, d) => { d.SectionType = s.WellSectionType.Caption; });
return dtos;
}

View File

@ -18,13 +18,13 @@ namespace AsbCloudInfrastructure.Services
public IEnumerable<DepositDto> GetDeposits(int idCompany)
{
var wellEntities = (from well in db.Wells
var wellEntities = (from well in db.Wells
.Include(w => w.RelationCompaniesWells)
.Include(w => w.WellType)
.Include(w=>w.Cluster)
.Include(w => w.Cluster)
.ThenInclude(c => c.Deposit)
where well.RelationCompaniesWells.Any(r => r.IdCompany == idCompany)
select well).ToList();
where well.RelationCompaniesWells.Any(r => r.IdCompany == idCompany)
select well).ToList();
var gDepositEntities = wellEntities
.GroupBy(w => w.Cluster)
@ -37,13 +37,15 @@ namespace AsbCloudInfrastructure.Services
Latitude = gDeposit.Key.Latitude,
Longitude = gDeposit.Key.Longitude,
Description = "",
Clusters = gDeposit.Select(gCluster=>new ClusterDto {
Clusters = gDeposit.Select(gCluster => new ClusterDto
{
Id = gCluster.Key.Id,
Caption = gCluster.Key.Caption,
Latitude = gCluster.Key.Latitude,
Longitude = gCluster.Key.Longitude,
Description = "",
Wells = gCluster.Select(well => new WellDto {
Wells = gCluster.Select(well => new WellDto
{
Id = well.Id,
Caption = well.Caption,
Latitude = well.Latitude,
@ -117,8 +119,8 @@ namespace AsbCloudInfrastructure.Services
public ClusterStatDto GetStat(int idCompany, int idCluster)
{
var wellEntities = from w in db.Wells
where w.IdCluster == idCluster && w.RelationCompaniesWells.Any(c => c.IdCompany == idCompany)
select w;
where w.IdCluster == idCluster && w.RelationCompaniesWells.Any(c => c.IdCompany == idCompany)
select w;
var wellStatDtos = wellEntities.Select(e => new WellStatDto
{
@ -169,7 +171,8 @@ namespace AsbCloudInfrastructure.Services
var clusterById = db.Clusters.FirstOrDefault(c => c.Id == idCluster);
return new ClusterStatDto {
return new ClusterStatDto
{
Id = clusterById.Id,
Description = "",
Caption = clusterById.Caption,

View File

@ -35,7 +35,7 @@ namespace AsbCloudWebApi.Controllers
[HttpGet]
[Route("{idWell}/operationsByWell")]
[ProducesResponseType(typeof(PaginationContainer<TelemetryOperationDto>), (int)System.Net.HttpStatusCode.OK)]
public IActionResult GetOperationsByWell(int idWell, int skip = 0, int take = 32,
public IActionResult GetOperationsByWell(int idWell, int skip = 0, int take = 32,
[FromQuery] IEnumerable<int> categoryIds = default, DateTime begin = default, DateTime end = default)
{
int? idCompany = User.GetCompanyId();

View File

@ -1,7 +1,6 @@
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using AsbCloudApp.Data;
using AsbCloudApp.Data;
using AsbCloudApp.Services;
using Microsoft.AspNetCore.Mvc;
// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860
@ -9,7 +8,7 @@ namespace AsbCloudWebApi.Controllers
{
[ApiController]
public abstract class CrudController<T> : ControllerBase
where T: IId
where T : IId
{
protected readonly ICrudService<T> service;

View File

@ -1,11 +1,11 @@
using System;
using System.Linq;
using System.IO;
using AsbCloudApp.Data;
using AsbCloudApp.Data;
using AsbCloudApp.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System;
using System.IO;
using System.Linq;
namespace AsbCloudWebApi.Controllers
{
@ -90,7 +90,7 @@ namespace AsbCloudWebApi.Controllers
if (idCompany is null || !wellService.IsCompanyInvolvedInWell((int)idCompany, idWell))
return Forbid();
var filesInfo = fileService.GetFilesInfo(idWell, idCategory,
var filesInfo = fileService.GetFilesInfo(idWell, idCategory,
begin, end, skip, take);
if (filesInfo is null || !filesInfo.Items.Any())
@ -126,7 +126,7 @@ namespace AsbCloudWebApi.Controllers
throw new FileNotFoundException();
// TODO: словарь content typoв
var relativePath = Path.Combine(fileService.RootPath, $"{idWell}", $"{fileInfo.Value.IdCategory}",
var relativePath = Path.Combine(fileService.RootPath, $"{idWell}", $"{fileInfo.Value.IdCategory}",
$"{fileInfo.Value.Id}" + Path.GetExtension($"{fileInfo.Value.Name}"));
return PhysicalFile(Path.GetFullPath(relativePath), "application/octet-stream", fileInfo.Value.Name);
}

View File

@ -1,7 +1,7 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using AsbCloudApp.Data;
using AsbCloudApp.Data;
using AsbCloudApp.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace AsbCloudWebApi.Controllers
{
@ -10,7 +10,7 @@ namespace AsbCloudWebApi.Controllers
[Authorize]
public class FluidController : LastDataController<FluidDataDto>
{
public FluidController(ILastDataService<FluidDataDto> lastDataService,
public FluidController(ILastDataService<FluidDataDto> lastDataService,
IWellService wellService) : base(lastDataService, wellService)
{ }
}

View File

@ -1,6 +1,6 @@
using Microsoft.AspNetCore.Mvc;
using AsbCloudApp.Services;
using Microsoft.AspNetCore.Authorization;
using AsbCloudApp.Services;
using Microsoft.AspNetCore.Mvc;
namespace AsbCloudWebApi.Controllers
{

View File

@ -1,7 +1,7 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using AsbCloudApp.Data;
using AsbCloudApp.Data;
using AsbCloudApp.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace AsbCloudWebApi.Controllers
{

View File

@ -1,7 +1,7 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using AsbCloudApp.Data;
using AsbCloudApp.Data;
using AsbCloudApp.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace AsbCloudWebApi.Controllers
{

View File

@ -75,7 +75,7 @@ namespace AsbCloudWebApi.Controllers
if (!wellService.IsCompanyInvolvedInWell((int)idCompany, idWell))
return Forbid();
var id = reportService.CreateReport(idWell, idUser,
var id = reportService.CreateReport(idWell, idUser,
stepSeconds, format, begin, end, HandleReportProgressAsync, HandleReportNameAsync);
return Ok(id);
@ -102,7 +102,7 @@ namespace AsbCloudWebApi.Controllers
if (!wellService.IsCompanyInvolvedInWell((int)idCompany, idWell))
return Forbid();
// TODO: словарь content typoв
var relativePath = Path.Combine(fileService.RootPath, $"{idWell}",
var relativePath = Path.Combine(fileService.RootPath, $"{idWell}",
$"{reportService.ReportCategoryId}", reportName);
return PhysicalFile(Path.GetFullPath(relativePath), "application/pdf", reportName);
}

View File

@ -44,7 +44,7 @@ namespace AsbCloudWebApi.Controllers
{
var idCompany = User.GetCompanyId();
if (idCompany is null)
if (idCompany is null)
return NoContent();
if (!wellService.IsCompanyInvolvedInWell((int)idCompany, idWell))

View File

@ -1,12 +1,7 @@
using AsbCloudApp.Data;
using AsbCloudApp.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace AsbCloudWebApi.Controllers
{
@ -16,7 +11,7 @@ namespace AsbCloudWebApi.Controllers
public class WellSectionController : CrudController<WellSectionDto>
{
public WellSectionController(ICrudService<WellSectionDto> service)
:base(service)
: base(service)
{
}

View File

@ -6,9 +6,6 @@
//using AutoMapper;
//using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.IO;
using Mapster;
namespace ConsoleApp1
{