From 1b43dd3109e8ae7d48c3c3db210b00f51ec7d17b Mon Sep 17 00:00:00 2001 From: Mitchel van Hamburg Date: Mon, 30 Jan 2023 09:20:18 +0100 Subject: [PATCH] Initial commit --- .gitignore | 80 ++++++++++++++++ Controllers/CardController.cs | 108 +++++++++++++++++++++ Controllers/FreezerController.cs | 129 +++++++++++++++++++++++++ Controllers/FreezerItemController.cs | 136 +++++++++++++++++++++++++++ Data/ThuisDbContext.cs | 32 +++++++ Models/Card.cs | 9 ++ Models/Freezer.cs | 7 ++ Models/FreezerDto.cs | 8 ++ Models/FreezerItem.cs | 13 +++ Models/FreezerItemDto.cs | 11 +++ Models/ThuisApiProfile.cs | 18 ++++ Program.cs | 36 +++++++ Properties/launchSettings.json | 31 ++++++ ThuisApi.csproj | 27 ++++++ appsettings.Development.json | 8 ++ appsettings.json | 9 ++ test.pfx | Bin 0 -> 2383 bytes 17 files changed, 662 insertions(+) create mode 100644 .gitignore create mode 100644 Controllers/CardController.cs create mode 100644 Controllers/FreezerController.cs create mode 100644 Controllers/FreezerItemController.cs create mode 100644 Data/ThuisDbContext.cs create mode 100644 Models/Card.cs create mode 100644 Models/Freezer.cs create mode 100644 Models/FreezerDto.cs create mode 100644 Models/FreezerItem.cs create mode 100644 Models/FreezerItemDto.cs create mode 100644 Models/ThuisApiProfile.cs create mode 100644 Program.cs create mode 100644 Properties/launchSettings.json create mode 100644 ThuisApi.csproj create mode 100644 appsettings.Development.json create mode 100644 appsettings.json create mode 100644 test.pfx diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c61018e --- /dev/null +++ b/.gitignore @@ -0,0 +1,80 @@ +# User-specific files +*.suo +*.user +*.sln.docstates + +# Build results +[Dd]ebug/ +[Rr]elease/ +x64/ +[Bb]in/ +[Oo]bj/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.log +*.svclog +*.scc + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# Click-Once directory +publish/ + +# Publish Web Output +*.Publish.xml +*.pubxml +*.azurePubxml + +# NuGet Packages Directory +## TODO: If you have NuGet Package Restore enabled, uncomment the next line +packages/ +## TODO: If the tool you use requires repositories.config, also uncomment the next line +!packages/repositories.config + +# Windows Azure Build Output +csx/ +*.build.csdef + +# Others +sql/ +*.Cache +ClientBin/ +[Ss]tyle[Cc]op.* +![Ss]tyle[Cc]op.targets +~$* +*~ +*.dbmdl +*.[Pp]ublish.xml + +*.publishsettings + +.idea/ +*.sqlite3 \ No newline at end of file diff --git a/Controllers/CardController.cs b/Controllers/CardController.cs new file mode 100644 index 0000000..104629d --- /dev/null +++ b/Controllers/CardController.cs @@ -0,0 +1,108 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using ThuisApi.Data; +using ThuisApi.Models; + +namespace ThuisApi.Controllers +{ + [Route("api/[controller]")] + [ApiController] + public class CardController : ControllerBase + { + private readonly ThuisDbContext _context; + + public CardController(ThuisDbContext context) + { + _context = context; + } + + // GET: api/Card + [HttpGet] + public async Task>> GetCards() + { + return await _context.Cards.ToListAsync(); + } + + // GET: api/Card/5 + [HttpGet("{id}")] + public async Task> GetCard(int id) + { + var card = await _context.Cards.FindAsync(id); + + if (card == null) + { + return NotFound(); + } + + return card; + } + + // PUT: api/Card/5 + // To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754 + [HttpPut("{id}")] + public async Task PutCard(int id, Card card) + { + if (id != card.CardId) + { + return BadRequest(); + } + + _context.Entry(card).State = EntityState.Modified; + + try + { + await _context.SaveChangesAsync(); + } + catch (DbUpdateConcurrencyException) + { + if (!CardExists(id)) + { + return NotFound(); + } + else + { + throw; + } + } + + return NoContent(); + } + + // POST: api/Card + // To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754 + [HttpPost] + public async Task> PostCard(Card card) + { + _context.Cards.Add(card); + await _context.SaveChangesAsync(); + + return CreatedAtAction("GetCard", new { id = card.CardId }, card); + } + + // DELETE: api/Card/5 + [HttpDelete("{id}")] + public async Task DeleteCard(int id) + { + var card = await _context.Cards.FindAsync(id); + if (card == null) + { + return NotFound(); + } + + _context.Cards.Remove(card); + await _context.SaveChangesAsync(); + + return NoContent(); + } + + private bool CardExists(int id) + { + return _context.Cards.Any(e => e.CardId == id); + } + } +} diff --git a/Controllers/FreezerController.cs b/Controllers/FreezerController.cs new file mode 100644 index 0000000..7b904f1 --- /dev/null +++ b/Controllers/FreezerController.cs @@ -0,0 +1,129 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using AutoMapper; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using ThuisApi.Data; +using ThuisApi.Models; + +namespace ThuisApi.Controllers +{ + [Route("api/[controller]")] + [ApiController] + public class FreezerController : ControllerBase + { + private readonly ThuisDbContext _context; + private readonly IMapper _mapper; + + public FreezerController(ThuisDbContext context, IMapper mapper) + { + _context = context; + _mapper = mapper; + } + + // GET: api/Freezer + [HttpGet] + public async Task>> GetFreezer() + { + // Iterate over the freezers to add amount in freezer. + var freezers = await _context.Freezer.ToListAsync(); + var freezerResponse = new List(); + foreach (var freezer in freezers) + { + freezerResponse.Add(new FreezerDto + { + FreezerId = freezer.FreezerId, + Location = freezer.Name, + AmountInFreezer = + await _context.FreezerItem.CountAsync(a => a.Freezer.FreezerId == freezer.FreezerId) + }); + } + + return Ok(freezerResponse.ToArray()); + } + + // GET: api/Freezer/5 + [HttpGet("{id}")] + public async Task> GetFreezer(int id) + { + var freezer = await _context.Freezer.FindAsync(id); + + if (freezer == null) + { + return NotFound(); + } + + + return Ok(_mapper.Map(freezer)); + } + + // PUT: api/Freezer/5 + // To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754 + [HttpPut("{id}")] + public async Task PutFreezer(int id, FreezerDto freezer) + { + if (id != freezer.FreezerId) + { + return BadRequest(); + } + + _context.Entry(_mapper.Map(freezer)).State = EntityState.Modified; + + try + { + await _context.SaveChangesAsync(); + } + catch (DbUpdateConcurrencyException) + { + if (!FreezerExists(id)) + { + return NotFound(); + } + else + { + throw; + } + } + + return NoContent(); + } + + // POST: api/Freezer + // To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754 + [HttpPost] + public async Task> PostFreezer(FreezerDto freezerDto) + { + var mappedFreezer = _mapper.Map(freezerDto); + var newFreezer = new Freezer { Name = mappedFreezer.Name }; + _context.Freezer.Add(newFreezer); + await _context.SaveChangesAsync(); + + return CreatedAtAction("GetFreezer", new { id = newFreezer.FreezerId }, + _mapper.Map(newFreezer)); + } + + // DELETE: api/Freezer/5 + [HttpDelete("{id}")] + public async Task DeleteFreezer(int id) + { + var freezer = await _context.Freezer.FindAsync(id); + if (freezer == null) + { + return NotFound(); + } + + _context.Freezer.Remove(freezer); + await _context.SaveChangesAsync(); + + return NoContent(); + } + + private bool FreezerExists(int id) + { + return _context.Freezer.Any(e => e.FreezerId == id); + } + } +} \ No newline at end of file diff --git a/Controllers/FreezerItemController.cs b/Controllers/FreezerItemController.cs new file mode 100644 index 0000000..84aa984 --- /dev/null +++ b/Controllers/FreezerItemController.cs @@ -0,0 +1,136 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text.Json; +using System.Threading.Tasks; +using AutoMapper; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using ThuisApi.Data; +using ThuisApi.Models; + +namespace ThuisApi.Controllers +{ + [Route("api/[controller]")] + [ApiController] + public class FreezerItemController : ControllerBase + { + private readonly ThuisDbContext _context; + private readonly IMapper _mapper; + + public FreezerItemController(ThuisDbContext context, IMapper mapper) + { + _context = context; + _mapper = mapper; + } + + // GET: api/FreezerItem + [HttpGet] + public async Task>> GetFreezerItem() + { + return Ok(_mapper.Map>(await _context.FreezerItem.ToArrayAsync())); + } + + // GET: api/FreezerItem/InFreezer/2 + [HttpGet("InFreezer/{id}")] + public async Task>> GetFreezerItemsInFreezer(int id) + { + if (!_context.Freezer.Any(a => a.FreezerId == id)) + { + return NotFound(); + } + + return Ok(_mapper.Map>(await _context.FreezerItem + .Where(a => a.FreezerId == id) + .ToArrayAsync())); + } + + // GET: api/FreezerItem/5 + [HttpGet("{id}")] + public async Task> GetFreezerItem(int id) + { + var freezerItem = await _context.FreezerItem.FindAsync(id); + + if (freezerItem == null) + { + return NotFound(); + } + + return Ok(_mapper.Map(freezerItem)); + } + + // PUT: api/FreezerItem/5 + // To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754 + [HttpPut("{id}")] + public async Task PutFreezerItem(int id, FreezerItemDto freezerItem) + { + if (id != freezerItem.FreezerItemId) + { + return BadRequest(); + } + + _context.Entry(_mapper.Map(freezerItem)).State = EntityState.Modified; + + try + { + await _context.SaveChangesAsync(); + } + catch (DbUpdateConcurrencyException) + { + if (!FreezerItemExists(id)) + { + return NotFound(); + } + else + { + throw; + } + } + + return NoContent(); + } + + // POST: api/FreezerItem + // To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754 + [HttpPost] + public async Task> PostFreezerItem(FreezerItemDto freezerItemDto) + { + var mappedItem = _mapper.Map(freezerItemDto); + if (mappedItem.DatePlacedInFreezer.ToString(CultureInfo.CurrentCulture).Equals("")) + { + mappedItem.DatePlacedInFreezer = DateTime.Now; + } + + mappedItem.Freezer = + await _context.Freezer.SingleAsync(freezer => freezer.FreezerId == mappedItem.FreezerId); + _context.FreezerItem.Add(mappedItem); + await _context.SaveChangesAsync(); + + return CreatedAtAction("GetFreezerItem", new { id = mappedItem.FreezerItemId }, + _mapper.Map(mappedItem)); + } + + // DELETE: api/FreezerItem/5 + [HttpDelete("{id}")] + public async Task DeleteFreezerItem(int id) + { + var freezerItem = await _context.FreezerItem.FindAsync(id); + if (freezerItem == null) + { + return NotFound(); + } + + _context.FreezerItem.Remove(freezerItem); + await _context.SaveChangesAsync(); + + return NoContent(); + } + + private bool FreezerItemExists(int id) + { + return _context.FreezerItem.Any(e => e.FreezerItemId == id); + } + } +} \ No newline at end of file diff --git a/Data/ThuisDbContext.cs b/Data/ThuisDbContext.cs new file mode 100644 index 0000000..89f1749 --- /dev/null +++ b/Data/ThuisDbContext.cs @@ -0,0 +1,32 @@ +using Microsoft.EntityFrameworkCore; +using ThuisApi.Models; + +namespace ThuisApi.Data; + +public class ThuisDbContext : DbContext +{ + public DbSet Cards { get; set; } + public DbSet Freezer { get; set; } + public DbSet FreezerItem { get; set; } + + public ThuisDbContext(DbContextOptions options) : base(options) + { + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + + modelBuilder.Entity().HasData( + new Freezer + { + FreezerId = 1, + Name = "Keuken" + }, + new Freezer + { + FreezerId = 2, + Name = "Berging" + }); + } +} \ No newline at end of file diff --git a/Models/Card.cs b/Models/Card.cs new file mode 100644 index 0000000..e8ed6e7 --- /dev/null +++ b/Models/Card.cs @@ -0,0 +1,9 @@ +namespace ThuisApi.Models +{ + public class Card + { + public int CardId { get; set; } + public string Issuer { get; set; } + public string Code { get; set; } + } +} \ No newline at end of file diff --git a/Models/Freezer.cs b/Models/Freezer.cs new file mode 100644 index 0000000..3da25c5 --- /dev/null +++ b/Models/Freezer.cs @@ -0,0 +1,7 @@ +namespace ThuisApi.Models; + +public class Freezer +{ + public int FreezerId { get; set; } + public string Name { get; set; } +} \ No newline at end of file diff --git a/Models/FreezerDto.cs b/Models/FreezerDto.cs new file mode 100644 index 0000000..75a24ba --- /dev/null +++ b/Models/FreezerDto.cs @@ -0,0 +1,8 @@ +namespace ThuisApi.Models; + +public class FreezerDto +{ + public int FreezerId { get; set; } + public string Location { get; set; } + public int AmountInFreezer { get; set; } +} \ No newline at end of file diff --git a/Models/FreezerItem.cs b/Models/FreezerItem.cs new file mode 100644 index 0000000..87fde06 --- /dev/null +++ b/Models/FreezerItem.cs @@ -0,0 +1,13 @@ +namespace ThuisApi.Models; + +public class FreezerItem +{ + public int FreezerItemId { get; set; } + public string Item { get; set; } + public int Amount { get; set; } + public int Drawer { get; set; } + public DateTime DatePlacedInFreezer { get; set; } + + public int FreezerId { get; set; } + public Freezer Freezer { get; set; } +} \ No newline at end of file diff --git a/Models/FreezerItemDto.cs b/Models/FreezerItemDto.cs new file mode 100644 index 0000000..9b914a5 --- /dev/null +++ b/Models/FreezerItemDto.cs @@ -0,0 +1,11 @@ +namespace ThuisApi.Models; + +public class FreezerItemDto +{ + public int FreezerItemId { get; set; } + public string Item { get; set; } + public int Amount { get; set; } + public int Drawer { get; set; } + public string DateTimeAdded { get; set; } + public int FreezerId { get; set; } +} \ No newline at end of file diff --git a/Models/ThuisApiProfile.cs b/Models/ThuisApiProfile.cs new file mode 100644 index 0000000..043abb1 --- /dev/null +++ b/Models/ThuisApiProfile.cs @@ -0,0 +1,18 @@ +using AutoMapper; + +namespace ThuisApi.Models; + +public class ThuisApiProfile : Profile +{ + public ThuisApiProfile() + { + CreateMap(); + CreateMap() + .ForMember(dest => dest.FreezerId, opt => opt.Ignore()) + .ForSourceMember(src => src.AmountInFreezer, opt => opt.DoNotValidate()); + CreateMap(); + CreateMap() + // .ForSourceMember(src => src.Freezer, opt => opt.DoNotValidate()) + .ForMember(dest => dest.Freezer, opt => opt.Ignore()); + } +} \ No newline at end of file diff --git a/Program.cs b/Program.cs new file mode 100644 index 0000000..e824d4b --- /dev/null +++ b/Program.cs @@ -0,0 +1,36 @@ +using Microsoft.EntityFrameworkCore; +using ThuisApi.Data; +using ThuisApi.Models; + +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container. +builder.Services.AddControllers(); + +// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(); + +builder.Services.AddDbContext(options => +{ + options.UseSqlite("Data Source=db.sqlite3"); +}); + +builder.Services.AddAutoMapper(typeof(ThuisApiProfile)); + +var app = builder.Build(); + +// Configure the HTTP request pipeline. +if (app.Environment.IsDevelopment()) +{ + app.UseSwagger(); + app.UseSwaggerUI(); +} + +// app.UseHttpsRedirection(); + +app.UseAuthorization(); + +app.MapControllers(); + +app.Run(); diff --git a/Properties/launchSettings.json b/Properties/launchSettings.json new file mode 100644 index 0000000..d9d2da2 --- /dev/null +++ b/Properties/launchSettings.json @@ -0,0 +1,31 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:33778", + "sslPort": 44391 + } + }, + "profiles": { + "ThuisApi": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": false, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:7239;http://localhost:5052", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": false, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/ThuisApi.csproj b/ThuisApi.csproj new file mode 100644 index 0000000..325b36d --- /dev/null +++ b/ThuisApi.csproj @@ -0,0 +1,27 @@ + + + + net6.0 + enable + enable + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + diff --git a/appsettings.Development.json b/appsettings.Development.json new file mode 100644 index 0000000..a6e86ac --- /dev/null +++ b/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Debug", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/appsettings.json b/appsettings.json new file mode 100644 index 0000000..10f68b8 --- /dev/null +++ b/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/test.pfx b/test.pfx new file mode 100644 index 0000000000000000000000000000000000000000..b93a77980437a8c871f2086bff9cf3a1367ac186 GIT binary patch literal 2383 zcmZXUX*3iH8^_HU+i)AMB~r#nWf_by7?NbqPL>ccmMjzHHpE=fjP07ro)XiLV(bmB z$TnHVPAHj-vX*tSgt6qUbKdv9=RNOp&hzE>JOAg~^G6T_EP()S1VMn0S3oJjJYoAc z03U!t;NJle_%{&*{%;5ZkIw%n9wiWg=k!l3_j6@<1^=BA0t0{)0%!|C0IehBdBOj| zKaWd*goR|4`Rjr9YS13K^9z8r1f~#=?XH>t18~zo~fj14edzcgQRz2NU(UD-3>rc!W<2H z45ugkxMzE->F>LXd7qx7j8Kc+Cp@~3&bvr3j9sG8o7ZwVHgc1DiG$vQ#X}`<)hZBW*CO@vWNx`ncTgl>_)0Cat@#g&&kjEXnCuVOL zdo@>a_{1wA+Ds?3`|RD~SZg76Vw_T4(1(gQOBvr(rLHOsCu_8gfDdq|h|p@g@zYKY zMQfNk_#s~H6qb{bJ2KgXAM2bMQpl0(Y%YD)2sd1-&c3Q-&3x71A5ZWMRYECqe=?#;KYOtcGLuYwr%xB^HH7Akm9u3PHU4S?4vuMg`rkU-ts*kY1%qbec3b5b?-@Y zzF=aiSEgB-EnGhJ04-B{5t1P`PsAo&GQ2&uxmP4vWuf zZTmSC=ydKct@OHQ^w37}JGT$AhYYp+{ne^0xIpqJV9Lk%oC+6?tvE}~`Kmg~3L(j$M% zjl2ZkR6ZF*ua(LG*bi63BaS3Ch*=+Q+8XFSKd0EyI^Jc}hwIQ8@X8cJd3~JHF20T$ zs%g}4>Eo5?qMo+g86pf!xjD5RX|)z?%-Qwii$fz@8P_qb4Z4Yr+#sIiMpIJ-|BW*$ zAmOT7i61&G^$WjC0lO5mKZl@GDR=TYPlIMFn^LBAT2_DPmV?>%(tO#otU%q653GF7 znUj$`r7t0f37aAKW-X3Pk3qq%iTMhiCnhIq6Qh$Vl5h|`XPqGfg8T!5Tv9M_Reu2P zs-tXx^(aeawQ4Uf(;xj_F>v?^Xepav8~G~@#$1&)Jp3KjgDg<%<6U@%{nS$>O5BfaK}I@mof7E>@|c z{kf$VKds*9!mNTqj?0ThiQIC}+m;Hl#gXAK-U|GZ=IzPeMszwP+=c&ZgEr2?E>iGb zrfK(5IS+&RRA1YIec{*$Uh;$gSJXimW zx;R4gpOzF5SLX$RxPA_Tz+L$d=HsOhxG6tTE)ej`Wc}9w{=YE+iU3E-+*(q0_5u&NU!U93R-){{-aCVi@0GdNq7>`$Ro2PtJR^~pY-@J=r_IVyB(Aqyyo(^DukSW_Nkw!c zedb1^>S7=A@h8W5Jo%lUk5vKLf^c|wqf+4#wIY)~7{#EsZrEkyB(C}DH3!Q!=Q~se zI`#CMl9OdR0~v*2)8hNfNc-CBI$2OoDl7S2^4?#jf4o&;&$t=`ju9{WZ@l#p#wvFl zq;e4kUE>ynIijzaWACGTM{@p%DVWa=aHBwXf=r{RQ!^e>sZN-=VEPF3CO+aN<&I{; zib8$LYQTA5@6k=RZ&L-_tLp?t`TlN?$&6oUraN{uOsHU}f!Y-O^&%wBpY)3P&^(;A z41I17v$Xnfp;tGx-R-Q=!9g8yopR8Iy>dG=oKf;o10xMh%sVz$^bPamVJT5z|0vF0 zgDzdHUZxsu7Qs>%iOnGUUSGB)xUAmvlfEGivKUbdIFiCllu~v=gE$LovHeNSh zSILaQN_hz9LMtTeA1x#G| zPa~`wA8*O&sDQ1ThD3{ThbLi*o1FA*`Mx*uJ53HrBvr#~4I2s5msv15rPog`9C(l* zekN)f?2}&v3$w74D)km~g2b1mYv|0URvLcloCfmyY5RRzaadU4fJ`c(dp2M)FW#+B zawgexxXHF8{;83V#Z1c*OWkb}sR} z|J|4gXZ6_QRRN=r%u{RwFR&;@RSiScpZW@~LwKp^27X(wO5pcg*?@nnSl38f(M8QV(dkkNb5C`e(d z$_=xH`WNh-o02p|(ZxhZZ|i~a=SOIbVn!-s4{Au(mQM+=?nT5|1Qfx?%cFdR8+Z&P xHo2TG@RutDPbC~XWQN$Tp}MIkkXX^K54;q&b3_JuLLX|4XY-Ja7k)kHzW~S?a`^xN literal 0 HcmV?d00001