Added S3Explorer a cmdline tool to grab and display data from our s3 buckets
This commit is contained in:
parent
add66565c4
commit
7ff1d05708
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"Key": "EXO1abcb772bf43ab72951ba1dc",
|
||||||
|
"Secret": "_ym1KsGBSp90S5dwhZn18XD-u9Y4ghHvyIxg5gv5fHw"
|
||||||
|
}
|
|
@ -9,9 +9,5 @@ public static class S3Access
|
||||||
{
|
{
|
||||||
public static S3Cmd ReadOnly => Deserialize<S3Cmd>(OpenRead("./Resources/s3ReadOnlyKey.json"))!;
|
public static S3Cmd ReadOnly => Deserialize<S3Cmd>(OpenRead("./Resources/s3ReadOnlyKey.json"))!;
|
||||||
public static S3Cmd ReadWrite => Deserialize<S3Cmd>(OpenRead("./Resources/s3ReadWriteKey.json"))!;
|
public static S3Cmd ReadWrite => Deserialize<S3Cmd>(OpenRead("./Resources/s3ReadWriteKey.json"))!;
|
||||||
|
public static S3Cmd Admin => Deserialize<S3Cmd>(OpenRead("./Resources/s3AdminKey.json"))!;
|
||||||
public static async Task<String> CreateKey(String bucketName)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -57,11 +57,23 @@ public class S3Cmd
|
||||||
</CORSConfiguration>";
|
</CORSConfiguration>";
|
||||||
|
|
||||||
var result = await Run(bucketName, "mb");
|
var result = await Run(bucketName, "mb");
|
||||||
var setCors = await Run(bucketName, "PutBucketCors", cors);
|
var setCors = await Run(bucketName, "setcors", cors);
|
||||||
|
|
||||||
return result.ExitCode == 0 && setCors.ExitCode == 0;
|
return result.ExitCode == 0 && setCors.ExitCode == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<String> ListFilesInBucket(String bucketName)
|
||||||
|
{
|
||||||
|
var result = await Run(bucketName, "ls");
|
||||||
|
return result.StandardOutput;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<String[]> GetFileText(String bucketName, String filename)
|
||||||
|
{
|
||||||
|
var result = await Run(bucketName + "/" + filename, "get", "--force");
|
||||||
|
return File.ReadAllLines("./" + filename);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<Boolean> DeleteBucket(String bucketName)
|
public async Task<Boolean> DeleteBucket(String bucketName)
|
||||||
{
|
{
|
||||||
var result = await Run(bucketName, "rb");
|
var result = await Run(bucketName, "rb");
|
||||||
|
@ -86,4 +98,5 @@ public class S3Cmd
|
||||||
.WithArguments(args)
|
.WithArguments(args)
|
||||||
.ExecuteBufferedAsync();
|
.ExecuteBufferedAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -0,0 +1,79 @@
|
||||||
|
using InnovEnergy.App.Backend.S3;
|
||||||
|
using InnovEnergy.Lib.Utils;
|
||||||
|
|
||||||
|
namespace S3Explorer;
|
||||||
|
|
||||||
|
public static class Program
|
||||||
|
{
|
||||||
|
|
||||||
|
public static async Task<Int32> Main(String[] args)
|
||||||
|
{
|
||||||
|
if (args.Contains("-s"))
|
||||||
|
{
|
||||||
|
await SnakeGameSs.PlaySnake();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.Length < 4 || args.Contains("-h"))
|
||||||
|
{
|
||||||
|
Console.WriteLine("To use: $S3Explorer [BucketId] [from:Unix-time] [to:Unix-time] [#Data-points]");
|
||||||
|
Console.WriteLine("-h shows this message");
|
||||||
|
Console.WriteLine("-s 🐍");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Console.WriteLine("");
|
||||||
|
|
||||||
|
var bucketName = args[0] + "-3e5b3069-214a-43ee-8d85-57d72000c19d";
|
||||||
|
var fromTime = Int64.Parse(args[1]);
|
||||||
|
var toTime = Int64.Parse(args[2]);
|
||||||
|
var numberOfDataPoints = Int64.Parse(args[3]);
|
||||||
|
|
||||||
|
var time = toTime - fromTime;
|
||||||
|
var timeBetweenDataPoints = time / numberOfDataPoints;
|
||||||
|
timeBetweenDataPoints = Math.Max(timeBetweenDataPoints, 2);
|
||||||
|
|
||||||
|
// var fileList = ListAllFileNamesInBucket(bucketName);
|
||||||
|
var timestampList = new List<String> { };
|
||||||
|
|
||||||
|
for (var i = fromTime; i <= toTime; i += timeBetweenDataPoints)
|
||||||
|
{
|
||||||
|
timestampList.Add((i/2 *2).ToString());
|
||||||
|
}
|
||||||
|
await GrabFiles(bucketName,timestampList);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task GrabFiles(String bucketName, List<String> timestampList)
|
||||||
|
{
|
||||||
|
var last = timestampList.Last();
|
||||||
|
var fileCsv = await S3Access.Admin.GetFileText(bucketName, last + ".csv");
|
||||||
|
var dataKeys = fileCsv
|
||||||
|
.Select(l => l.Split(";"))
|
||||||
|
.Select(l => l[0])
|
||||||
|
.Prepend("Timestamp")
|
||||||
|
.JoinWith(";")
|
||||||
|
.WriteLine();
|
||||||
|
|
||||||
|
foreach (var timestamp in timestampList)
|
||||||
|
{
|
||||||
|
|
||||||
|
fileCsv = await S3Access.Admin.GetFileText(bucketName,timestamp + ".csv");
|
||||||
|
|
||||||
|
fileCsv.Select(l => l.Split(";"))
|
||||||
|
.Select(l => l[1])
|
||||||
|
.Prepend(timestamp)
|
||||||
|
.JoinWith(";")
|
||||||
|
.WriteLine();
|
||||||
|
|
||||||
|
// Parse csv, build csv
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<String> ListAllFileNamesInBucket(String bucketName)
|
||||||
|
{
|
||||||
|
// Todo refactor S3Access into Lib
|
||||||
|
return S3Access.Admin.ListFilesInBucket(bucketName).Result.Split(',').ToList();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net7.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Backend\Backend.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
|
@ -0,0 +1,283 @@
|
||||||
|
namespace S3Explorer;
|
||||||
|
|
||||||
|
public static class SnakeGameSs
|
||||||
|
{
|
||||||
|
public static async Task PlaySnake()
|
||||||
|
{
|
||||||
|
var tickRate = TimeSpan.FromMilliseconds(100);
|
||||||
|
var snakeGame = new SnakeGame();
|
||||||
|
|
||||||
|
using (var cts = new CancellationTokenSource())
|
||||||
|
{
|
||||||
|
async Task MonitorKeyPresses()
|
||||||
|
{
|
||||||
|
while (!cts.Token.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
if (Console.KeyAvailable)
|
||||||
|
{
|
||||||
|
var key = Console.ReadKey(intercept: true).Key;
|
||||||
|
snakeGame.OnKeyPress(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
await Task.Delay(10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var monitorKeyPresses = MonitorKeyPresses();
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
snakeGame.OnGameTick();
|
||||||
|
snakeGame.Render();
|
||||||
|
await Task.Delay(tickRate);
|
||||||
|
} while (!snakeGame.GameOver);
|
||||||
|
|
||||||
|
// Allow time for user to weep before application exits.
|
||||||
|
for (var i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
Console.Clear();
|
||||||
|
await Task.Delay(500);
|
||||||
|
snakeGame.Render();
|
||||||
|
await Task.Delay(500);
|
||||||
|
}
|
||||||
|
|
||||||
|
cts.Cancel();
|
||||||
|
await monitorKeyPresses;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Direction
|
||||||
|
{
|
||||||
|
Up,
|
||||||
|
Down,
|
||||||
|
Left,
|
||||||
|
Right
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IRenderable
|
||||||
|
{
|
||||||
|
void Render();
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly struct Position
|
||||||
|
{
|
||||||
|
public Position(int top, int left)
|
||||||
|
{
|
||||||
|
Top = top;
|
||||||
|
Left = left;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Top { get; }
|
||||||
|
public int Left { get; }
|
||||||
|
|
||||||
|
public Position RightBy(int n) => new Position(Top, Left + n);
|
||||||
|
public Position DownBy(int n) => new Position(Top + n, Left);
|
||||||
|
}
|
||||||
|
|
||||||
|
class Apple : IRenderable
|
||||||
|
{
|
||||||
|
public Apple(Position position)
|
||||||
|
{
|
||||||
|
Position = position;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Position Position { get; }
|
||||||
|
|
||||||
|
public void Render()
|
||||||
|
{
|
||||||
|
Console.SetCursorPosition(Position.Left, Position.Top);
|
||||||
|
Console.Write("🍏");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Snake : IRenderable
|
||||||
|
{
|
||||||
|
private List<Position> _body;
|
||||||
|
private int _growthSpurtsRemaining;
|
||||||
|
|
||||||
|
public Snake(Position spawnLocation, int initialSize = 1)
|
||||||
|
{
|
||||||
|
_body = new List<Position> { spawnLocation };
|
||||||
|
_growthSpurtsRemaining = Math.Max(0, initialSize - 1);
|
||||||
|
Dead = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Dead { get; private set; }
|
||||||
|
public Position Head => _body.First();
|
||||||
|
private IEnumerable<Position> Body => _body.Skip(1);
|
||||||
|
|
||||||
|
public void Move(Direction direction)
|
||||||
|
{
|
||||||
|
if (Dead) throw new InvalidOperationException();
|
||||||
|
|
||||||
|
Position newHead;
|
||||||
|
|
||||||
|
switch (direction)
|
||||||
|
{
|
||||||
|
case Direction.Up:
|
||||||
|
newHead = Head.DownBy(-1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Direction.Left:
|
||||||
|
newHead = Head.RightBy(-1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Direction.Down:
|
||||||
|
newHead = Head.DownBy(1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Direction.Right:
|
||||||
|
newHead = Head.RightBy(1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_body.Contains(newHead) || !PositionIsValid(newHead))
|
||||||
|
{
|
||||||
|
Dead = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_body.Insert(0, newHead);
|
||||||
|
|
||||||
|
if (_growthSpurtsRemaining > 0)
|
||||||
|
{
|
||||||
|
_growthSpurtsRemaining--;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_body.RemoveAt(_body.Count - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Grow()
|
||||||
|
{
|
||||||
|
if (Dead) throw new InvalidOperationException();
|
||||||
|
|
||||||
|
_growthSpurtsRemaining++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Render()
|
||||||
|
{
|
||||||
|
Console.SetCursorPosition(Head.Left, Head.Top);
|
||||||
|
Console.Write("◉");
|
||||||
|
|
||||||
|
foreach (var position in Body)
|
||||||
|
{
|
||||||
|
Console.SetCursorPosition(position.Left, position.Top);
|
||||||
|
Console.Write("■");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool PositionIsValid(Position position) =>
|
||||||
|
position.Top >= 0 && position.Left >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
class SnakeGame : IRenderable
|
||||||
|
{
|
||||||
|
private static readonly Position Origin = new Position(0, 0);
|
||||||
|
|
||||||
|
private Direction _currentDirection;
|
||||||
|
private Direction _nextDirection;
|
||||||
|
private Snake _snake;
|
||||||
|
private Apple _apple;
|
||||||
|
|
||||||
|
public SnakeGame()
|
||||||
|
{
|
||||||
|
_snake = new Snake(Origin, initialSize: 5);
|
||||||
|
_apple = CreateApple();
|
||||||
|
_currentDirection = Direction.Right;
|
||||||
|
_nextDirection = Direction.Right;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool GameOver => _snake.Dead;
|
||||||
|
|
||||||
|
public void OnKeyPress(ConsoleKey key)
|
||||||
|
{
|
||||||
|
Direction newDirection;
|
||||||
|
|
||||||
|
switch (key)
|
||||||
|
{
|
||||||
|
case ConsoleKey.W:
|
||||||
|
newDirection = Direction.Up;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ConsoleKey.A:
|
||||||
|
newDirection = Direction.Left;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ConsoleKey.S:
|
||||||
|
newDirection = Direction.Down;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ConsoleKey.D:
|
||||||
|
newDirection = Direction.Right;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Snake cannot turn 180 degrees.
|
||||||
|
if (newDirection == OppositeDirectionTo(_currentDirection))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_nextDirection = newDirection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnGameTick()
|
||||||
|
{
|
||||||
|
if (GameOver) throw new InvalidOperationException();
|
||||||
|
|
||||||
|
_currentDirection = _nextDirection;
|
||||||
|
_snake.Move(_currentDirection);
|
||||||
|
|
||||||
|
// If the snake's head moves to the same position as an apple, the snake
|
||||||
|
// eats it.
|
||||||
|
if (_snake.Head.Equals(_apple.Position))
|
||||||
|
{
|
||||||
|
_snake.Grow();
|
||||||
|
_apple = CreateApple();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Render()
|
||||||
|
{
|
||||||
|
Console.Clear();
|
||||||
|
_snake.Render();
|
||||||
|
_apple.Render();
|
||||||
|
Console.SetCursorPosition(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Direction OppositeDirectionTo(Direction direction)
|
||||||
|
{
|
||||||
|
switch (direction)
|
||||||
|
{
|
||||||
|
case Direction.Up: return Direction.Down;
|
||||||
|
case Direction.Left: return Direction.Right;
|
||||||
|
case Direction.Right: return Direction.Left;
|
||||||
|
case Direction.Down: return Direction.Up;
|
||||||
|
default: throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Apple CreateApple()
|
||||||
|
{
|
||||||
|
// Can be factored elsewhere.
|
||||||
|
const int numberOfRows = 20;
|
||||||
|
const int numberOfColumns = 20;
|
||||||
|
|
||||||
|
var random = new Random();
|
||||||
|
var top = random.Next(0, numberOfRows + 1);
|
||||||
|
var left = random.Next(0, numberOfColumns + 1);
|
||||||
|
var position = new Position(top, left);
|
||||||
|
var apple = new Apple(position);
|
||||||
|
|
||||||
|
return apple;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -77,6 +77,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Adam6360D", "Lib\Devices\Ad
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IEM3kGridMeter", "Lib\Devices\IEM3kGridMeter\IEM3kGridMeter.csproj", "{1391165D-51F1-45B4-8B7F-042A20AA0277}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IEM3kGridMeter", "Lib\Devices\IEM3kGridMeter\IEM3kGridMeter.csproj", "{1391165D-51F1-45B4-8B7F-042A20AA0277}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "S3Explorer", "App\S3Explorer\S3Explorer.csproj", "{EB56EF94-D8A7-4111-A8E7-A87EF13596DA}"
|
||||||
|
EndProject
|
||||||
|
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
@ -196,6 +198,10 @@ Global
|
||||||
{1391165D-51F1-45B4-8B7F-042A20AA0277}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{1391165D-51F1-45B4-8B7F-042A20AA0277}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{1391165D-51F1-45B4-8B7F-042A20AA0277}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{1391165D-51F1-45B4-8B7F-042A20AA0277}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{1391165D-51F1-45B4-8B7F-042A20AA0277}.Release|Any CPU.Build.0 = Release|Any CPU
|
{1391165D-51F1-45B4-8B7F-042A20AA0277}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{EB56EF94-D8A7-4111-A8E7-A87EF13596DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{EB56EF94-D8A7-4111-A8E7-A87EF13596DA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{EB56EF94-D8A7-4111-A8E7-A87EF13596DA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{EB56EF94-D8A7-4111-A8E7-A87EF13596DA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(NestedProjects) = preSolution
|
GlobalSection(NestedProjects) = preSolution
|
||||||
{CF4834CB-91B7-4172-AC13-ECDA8613CD17} = {145597B4-3E30-45E6-9F72-4DD43194539A}
|
{CF4834CB-91B7-4172-AC13-ECDA8613CD17} = {145597B4-3E30-45E6-9F72-4DD43194539A}
|
||||||
|
@ -230,5 +236,6 @@ Global
|
||||||
{1A56992B-CB72-490F-99A4-DF1186BA3A18} = {AD5B98A8-AB7F-4DA2-B66D-5B4E63E7D854}
|
{1A56992B-CB72-490F-99A4-DF1186BA3A18} = {AD5B98A8-AB7F-4DA2-B66D-5B4E63E7D854}
|
||||||
{A3C79247-4CAA-44BE-921E-7285AB39E71F} = {4931A385-24DC-4E78-BFF4-356F8D6D5183}
|
{A3C79247-4CAA-44BE-921E-7285AB39E71F} = {4931A385-24DC-4E78-BFF4-356F8D6D5183}
|
||||||
{1391165D-51F1-45B4-8B7F-042A20AA0277} = {4931A385-24DC-4E78-BFF4-356F8D6D5183}
|
{1391165D-51F1-45B4-8B7F-042A20AA0277} = {4931A385-24DC-4E78-BFF4-356F8D6D5183}
|
||||||
|
{EB56EF94-D8A7-4111-A8E7-A87EF13596DA} = {145597B4-3E30-45E6-9F72-4DD43194539A}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
|
Loading…
Reference in New Issue