Compare commits
18 Commits
4de264f767
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4b2041d2bf | ||
|
|
60c8f9b34e | ||
|
|
7b3c0998a0 | ||
|
|
8da7888933 | ||
|
|
9e34e45748 | ||
|
|
f531bf54af | ||
|
|
b85d0d025d | ||
|
|
e4081cc078 | ||
|
|
d287367283 | ||
|
|
37b5e96742 | ||
|
|
ae2a04e83b | ||
|
|
eeeadd0ff2 | ||
|
|
9295dc2e98 | ||
|
|
53fd6ed875 | ||
|
|
3e6366071f | ||
|
|
22dc00ee8b | ||
|
|
b75928e73d | ||
|
|
163ee7beea |
@@ -21,8 +21,8 @@ steps:
|
|||||||
commands:
|
commands:
|
||||||
- export DOCKER_BUILDKIT=1
|
- export DOCKER_BUILDKIT=1
|
||||||
- docker buildx create --use --driver docker-container || true
|
- docker buildx create --use --driver docker-container || true
|
||||||
- echo "$GITEA_TOKEN" | docker login 192.168.1.9:3000 -u "$GITEA_USERNAME" --password-stdin
|
- echo "$GITEA_TOKEN" | docker login gitea.zcz.se -u "$GITEA_USERNAME" --password-stdin
|
||||||
- docker buildx build --builder default --tag 192.168.1.9:3000/tai/aberwyn/aberwyn:latest --tag 192.168.1.9:3000/tai/aberwyn/aberwyn:${DRONE_COMMIT_SHA:0:7} --cache-from=type=registry,ref=192.168.1.9:3000/tai/aberwyn/aberwyn:buildcache --push -f Aberwyn/Dockerfile .
|
- docker buildx build --builder default --tag gitea.zcz.se/tai/aberwyn/aberwyn:latest --tag gitea.zcz.se/tai/aberwyn/aberwyn:${DRONE_COMMIT_SHA:0:7} --cache-from=type=registry,ref=gitea.zcz.se/tai/aberwyn/aberwyn:buildcache --push -f Aberwyn/Dockerfile .
|
||||||
|
|
||||||
- name: notify-result
|
- name: notify-result
|
||||||
image: alpine
|
image: alpine
|
||||||
@@ -34,7 +34,7 @@ steps:
|
|||||||
- apk add --no-cache curl
|
- apk add --no-cache curl
|
||||||
- |
|
- |
|
||||||
if [ "$DRONE_BUILD_STATUS" = "success" ]; then
|
if [ "$DRONE_BUILD_STATUS" = "success" ]; then
|
||||||
curl -X POST http://192.168.1.196:8123/api/webhook/aberwyn_update_success
|
curl -X POST https://ha.zcz.se/api/webhook/aberwyn_update_success
|
||||||
else
|
else
|
||||||
curl -X POST http://192.168.1.196:8123/api/webhook/aberwyn_update_failed
|
curl -X POST https://ha.zcz.se/api/webhook/aberwyn_update_failed
|
||||||
fi
|
fi
|
||||||
|
|||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -366,3 +366,5 @@ FodyWeavers.xsd
|
|||||||
infrastructure/setup.json
|
infrastructure/setup.json
|
||||||
Aberwyn/Infrastructure/setup.json
|
Aberwyn/Infrastructure/setup.json
|
||||||
/Aberwyn/Data/infrastructure/setup2.json
|
/Aberwyn/Data/infrastructure/setup2.json
|
||||||
|
/Aberwyn/Data/infrastructure/setup.json
|
||||||
|
/Aberwyn/Infrastructure/setup.json
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
# Visual Studio Version 17
|
# Visual Studio Version 18
|
||||||
VisualStudioVersion = 17.2.32616.157
|
VisualStudioVersion = 18.3.11426.168 d18.3
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aberwyn", "Aberwyn\Aberwyn.csproj", "{F5586986-B726-4E05-B31B-2E24CA5B2B89}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aberwyn", "Aberwyn\Aberwyn.csproj", "{F5586986-B726-4E05-B31B-2E24CA5B2B89}"
|
||||||
EndProject
|
EndProject
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ steps:
|
|||||||
- name: build-docker
|
- name: build-docker
|
||||||
image: plugins/docker
|
image: plugins/docker
|
||||||
settings:
|
settings:
|
||||||
registry: 192.168.1.9:3000
|
registry: gitea.zcz.se
|
||||||
repo: 192.168.1.9:3000/tai/aberwyn/aberwyn
|
repo: gitea.zcz.se/tai/aberwyn/aberwyn
|
||||||
username:
|
username:
|
||||||
from_secret: gitea_username
|
from_secret: gitea_username
|
||||||
password:
|
password:
|
||||||
@@ -33,9 +33,9 @@ steps:
|
|||||||
- apk add --no-cache curl
|
- apk add --no-cache curl
|
||||||
- |
|
- |
|
||||||
if [ "$DRONE_BUILD_STATUS" = "success" ]; then
|
if [ "$DRONE_BUILD_STATUS" = "success" ]; then
|
||||||
curl -X POST http://192.168.1.196:8123/api/webhook/aberwyn_update_success
|
curl -X POST https://ha.zcz.se/api/webhook/aberwyn_update_success
|
||||||
else
|
else
|
||||||
curl -X POST http://192.168.1.196:8123/api/webhook/aberwyn_update_failed
|
curl -X POST https://ha.zcz.se/api/webhook/aberwyn_update_failed
|
||||||
fi
|
fi
|
||||||
|
|
||||||
trigger:
|
trigger:
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
using Aberwyn.Models;
|
using Aberwyn.Data;
|
||||||
|
using Aberwyn.Models;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using System.Globalization;
|
||||||
|
|
||||||
namespace Aberwyn.Controllers
|
namespace Aberwyn.Controllers
|
||||||
{
|
{
|
||||||
@@ -12,6 +14,9 @@ namespace Aberwyn.Controllers
|
|||||||
{
|
{
|
||||||
ViewBag.Year = year;
|
ViewBag.Year = year;
|
||||||
ViewBag.Month = month;
|
ViewBag.Month = month;
|
||||||
|
DateTime payDate = SalaryDateService.GetSalaryPayDate(year, month -1);
|
||||||
|
ViewBag.Paydate = payDate.ToString("d MMMM", new CultureInfo("sv-SE"));
|
||||||
|
|
||||||
return View();
|
return View();
|
||||||
}
|
}
|
||||||
[Route("budget/list")]
|
[Route("budget/list")]
|
||||||
@@ -20,6 +25,7 @@ namespace Aberwyn.Controllers
|
|||||||
return View();
|
return View();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[Route("budget/{name}")]
|
[Route("budget/{name}")]
|
||||||
public IActionResult Index(string name)
|
public IActionResult Index(string name)
|
||||||
{
|
{
|
||||||
@@ -39,9 +45,19 @@ namespace Aberwyn.Controllers
|
|||||||
public IActionResult Index()
|
public IActionResult Index()
|
||||||
{
|
{
|
||||||
var now = DateTime.Now;
|
var now = DateTime.Now;
|
||||||
return RedirectToAction("Index", new { year = now.Year, month = now.Month });
|
return RedirectToAction("Index", new { year = now.Year, month = now.Month + 1 });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpGet("api/budget/{year:int}/{month:int}/salary-date")]
|
||||||
|
public IActionResult SalaryDate(int year, int month)
|
||||||
|
{
|
||||||
|
var date = SalaryDateService.GetSalaryPayDate(year, month);
|
||||||
|
|
||||||
|
return Ok(new
|
||||||
|
{
|
||||||
|
iso = date.ToString("yyyy-MM-dd"),
|
||||||
|
display = date.ToString("d MMMM yyyy", new CultureInfo("sv-SE"))
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Mvc;
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using System.Globalization;
|
||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
|
||||||
@@ -111,11 +112,75 @@ namespace Aberwyn.Controllers
|
|||||||
|
|
||||||
#region Skolmat
|
#region Skolmat
|
||||||
[HttpGet("skolmat")]
|
[HttpGet("skolmat")]
|
||||||
public async Task<IActionResult> GetSkolmat(int week, [FromQuery] string sensor = "sensor.engelbrektsskolan")
|
public async Task<IActionResult> GetSkolmat(int week, [FromQuery] string sensor = "sensor.engelbrektsskolan")
|
||||||
|
{
|
||||||
|
var token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiI3M2Q5ODIyYzU4ZWI0MjM4OWEyMGQ2MWQ2MWVhOWYzYyIsImlhdCI6MTc0OTE1MzY1MCwiZXhwIjoyMDY0NTEzNjUwfQ.8C_dKm7P1BbFVJKc_wT76YnQqiZxkP9EzrsLbfD0Ml8";
|
||||||
|
var client = new HttpClient();
|
||||||
|
client.BaseAddress = new Uri("https://ha.zcz.se");
|
||||||
|
client.DefaultRequestHeaders.Authorization =
|
||||||
|
new AuthenticationHeaderValue("Bearer", token);
|
||||||
|
|
||||||
|
var res = await client.GetAsync($"/api/states/{sensor}");
|
||||||
|
|
||||||
|
if (!res.IsSuccessStatusCode)
|
||||||
|
return StatusCode((int)res.StatusCode, $"Kunde inte hämta data för {sensor}");
|
||||||
|
|
||||||
|
var json = await res.Content.ReadAsStringAsync();
|
||||||
|
using var doc = JsonDocument.Parse(json);
|
||||||
|
|
||||||
|
var attr = doc.RootElement.GetProperty("attributes");
|
||||||
|
|
||||||
|
if (!attr.TryGetProperty("calendar", out var calendar))
|
||||||
|
return NotFound("Ingen kalender hittades i attributen");
|
||||||
|
|
||||||
|
var result = new List<SchoolMealDto>();
|
||||||
|
var culture = CultureInfo.InvariantCulture;
|
||||||
|
var calendarSystem = culture.Calendar;
|
||||||
|
|
||||||
|
foreach (var property in calendar.EnumerateObject())
|
||||||
|
{
|
||||||
|
var dateString = property.Name; // t.ex 2026-02-09
|
||||||
|
if (!DateTime.TryParse(dateString, out var date))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
int weekNumber = calendarSystem.GetWeekOfYear(
|
||||||
|
date,
|
||||||
|
CalendarWeekRule.FirstFourDayWeek,
|
||||||
|
DayOfWeek.Monday);
|
||||||
|
|
||||||
|
if (weekNumber != week)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var dishes = property.Value.EnumerateArray()
|
||||||
|
.Select(x => x.GetProperty("dish").GetString())
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
result.Add(new SchoolMealDto
|
||||||
|
{
|
||||||
|
Weekday = date.ToString("dddd", new CultureInfo("sv-SE")),
|
||||||
|
Date = date.ToString("yyyy-MM-dd"),
|
||||||
|
Courses = dishes
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!result.Any())
|
||||||
|
return NotFound("Ingen skolmat för vecka " + week);
|
||||||
|
|
||||||
|
return Ok(result.OrderBy(x => x.Date));
|
||||||
|
}
|
||||||
|
public class SchoolMealDto
|
||||||
|
{
|
||||||
|
public string Weekday { get; set; }
|
||||||
|
public string Date { get; set; }
|
||||||
|
public List<string> Courses { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IActionResult> GetSkolmatv0(int week, [FromQuery] string sensor = "sensor.engelbrektsskolan")
|
||||||
{
|
{
|
||||||
var token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiI3M2Q5ODIyYzU4ZWI0MjM4OWEyMGQ2MWQ2MWVhOWYzYyIsImlhdCI6MTc0OTE1MzY1MCwiZXhwIjoyMDY0NTEzNjUwfQ.8C_dKm7P1BbFVJKc_wT76YnQqiZxkP9EzrsLbfD0Ml8";
|
var token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiI3M2Q5ODIyYzU4ZWI0MjM4OWEyMGQ2MWQ2MWVhOWYzYyIsImlhdCI6MTc0OTE1MzY1MCwiZXhwIjoyMDY0NTEzNjUwfQ.8C_dKm7P1BbFVJKc_wT76YnQqiZxkP9EzrsLbfD0Ml8";
|
||||||
var client = new HttpClient();
|
var client = new HttpClient();
|
||||||
client.BaseAddress = new Uri("http://192.168.1.196:8123/");
|
client.BaseAddress = new Uri("https://ha.zcz.se");
|
||||||
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
|
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
|
||||||
|
|
||||||
var res = await client.GetAsync($"/api/states/{sensor}");
|
var res = await client.GetAsync($"/api/states/{sensor}");
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using Aberwyn.Data;
|
using Aberwyn.Data;
|
||||||
using BencodeNET.Torrents;
|
using BencodeNET.Torrents;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
@@ -24,7 +25,7 @@ public class RssController : Controller
|
|||||||
_movieMetadataService = movieMetadataService;
|
_movieMetadataService = movieMetadataService;
|
||||||
_context = context;
|
_context = context;
|
||||||
}
|
}
|
||||||
|
[Authorize(Roles = "Admin")]
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
public async Task<IActionResult> Index(int page = 1, string sort = "date", string range = "all")
|
public async Task<IActionResult> Index(int page = 1, string sort = "date", string range = "all")
|
||||||
{
|
{
|
||||||
@@ -105,13 +106,13 @@ public class RssController : Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Authorize(Roles = "Admin")]
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
public async Task<IActionResult> Add(string torrentUrl)
|
public async Task<IActionResult> Add(string torrentUrl)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (await _deluge.LoginAsync("deluge1"))
|
if (await _deluge.LoginAsync("deluge1", "http://192.168.10.7:8112"))
|
||||||
{
|
{
|
||||||
var success = await _deluge.AddTorrentUrlAsync(torrentUrl);
|
var success = await _deluge.AddTorrentUrlAsync(torrentUrl);
|
||||||
|
|
||||||
@@ -159,12 +160,12 @@ public class RssController : Controller
|
|||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.LogError(ex, "Fel vid tillägg av torrent");
|
_logger.LogError(ex, "Fel vid tillägg av torrent");
|
||||||
return Json(new { success = false, message = "Ett fel uppstod vid tillägg av torrent." });
|
return Json(new { success = false, message = "Ett fel uppstod vid tillägg av torrent. Meddelande: " + ex.Message });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Authorize(Roles = "Admin")]
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
[ValidateAntiForgeryToken]
|
[ValidateAntiForgeryToken]
|
||||||
public async Task<IActionResult> Upload(TorrentUploadViewModel model)
|
public async Task<IActionResult> Upload(TorrentUploadViewModel model)
|
||||||
@@ -211,7 +212,7 @@ public class RssController : Controller
|
|||||||
return View("Index", model);
|
return View("Index", model);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
[Authorize(Roles = "Admin")]
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
[ValidateAntiForgeryToken]
|
[ValidateAntiForgeryToken]
|
||||||
public async Task<IActionResult> RefreshStats(string infoHash, string scrapeUrl)
|
public async Task<IActionResult> RefreshStats(string infoHash, string scrapeUrl)
|
||||||
|
|||||||
@@ -6,16 +6,15 @@ namespace Aberwyn.Data
|
|||||||
public class DelugeClient
|
public class DelugeClient
|
||||||
{
|
{
|
||||||
private readonly HttpClient _http;
|
private readonly HttpClient _http;
|
||||||
private readonly string _url;
|
|
||||||
private string _sessionId;
|
private string _sessionId;
|
||||||
|
private string _url;
|
||||||
public DelugeClient(HttpClient httpClient, string baseUrl = "http://192.168.10.7:8112/json")
|
//should fix settings password
|
||||||
|
public DelugeClient(HttpClient httpClient)
|
||||||
{
|
{
|
||||||
_http = httpClient;
|
_http = httpClient;
|
||||||
_url = baseUrl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> LoginAsync(string password)
|
public async Task<bool> LoginAsync(string password, string url)
|
||||||
{
|
{
|
||||||
var payload = new
|
var payload = new
|
||||||
{
|
{
|
||||||
@@ -23,7 +22,7 @@ namespace Aberwyn.Data
|
|||||||
@params = new object[] { password },
|
@params = new object[] { password },
|
||||||
id = 1
|
id = 1
|
||||||
};
|
};
|
||||||
|
_url = url + "/json";
|
||||||
var response = await _http.PostAsJsonAsync(_url, payload);
|
var response = await _http.PostAsJsonAsync(_url, payload);
|
||||||
var json = await response.Content.ReadFromJsonAsync<JsonElement>();
|
var json = await response.Content.ReadFromJsonAsync<JsonElement>();
|
||||||
|
|
||||||
@@ -38,6 +37,7 @@ namespace Aberwyn.Data
|
|||||||
return json.GetProperty("result").GetBoolean();
|
return json.GetProperty("result").GetBoolean();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
public async Task<bool> AddMagnetAsync(string magnetLink)
|
public async Task<bool> AddMagnetAsync(string magnetLink)
|
||||||
{
|
{
|
||||||
var payload = new
|
var payload = new
|
||||||
@@ -50,7 +50,8 @@ namespace Aberwyn.Data
|
|||||||
var response = await _http.PostAsJsonAsync(_url, payload);
|
var response = await _http.PostAsJsonAsync(_url, payload);
|
||||||
var json = await response.Content.ReadFromJsonAsync<JsonElement>();
|
var json = await response.Content.ReadFromJsonAsync<JsonElement>();
|
||||||
return json.GetProperty("result").ValueKind != JsonValueKind.Null;
|
return json.GetProperty("result").ValueKind != JsonValueKind.Null;
|
||||||
}
|
}*/
|
||||||
|
|
||||||
public async Task<bool> AddTorrentUrlAsync(string torrentUrl)
|
public async Task<bool> AddTorrentUrlAsync(string torrentUrl)
|
||||||
{
|
{
|
||||||
var payload = new
|
var payload = new
|
||||||
@@ -61,7 +62,7 @@ namespace Aberwyn.Data
|
|||||||
torrentUrl,
|
torrentUrl,
|
||||||
new
|
new
|
||||||
{
|
{
|
||||||
download_location = "/download/incomplete",
|
download_location = "/downloads/Incomplete",
|
||||||
move_completed = true,
|
move_completed = true,
|
||||||
move_completed_path = "/media/Movies",
|
move_completed_path = "/media/Movies",
|
||||||
}
|
}
|
||||||
|
|||||||
97
Aberwyn/Data/PayoutService.cs
Normal file
97
Aberwyn/Data/PayoutService.cs
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
using Aberwyn.Data;
|
||||||
|
|
||||||
|
namespace Aberwyn.Data
|
||||||
|
{
|
||||||
|
public class PayoutService
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static class SwedishHolidays
|
||||||
|
{
|
||||||
|
public static bool IsRedDay(DateTime date)
|
||||||
|
{
|
||||||
|
var year = date.Year;
|
||||||
|
|
||||||
|
var fixedHolidays = new[]
|
||||||
|
{
|
||||||
|
new DateTime(year, 1, 1), // Nyårsdagen
|
||||||
|
new DateTime(year, 1, 6), // Trettondedag jul
|
||||||
|
new DateTime(year, 5, 1), // Första maj
|
||||||
|
new DateTime(year, 6, 6), // Nationaldagen
|
||||||
|
new DateTime(year, 12, 25), // Juldagen
|
||||||
|
new DateTime(year, 12, 26), // Annandag jul
|
||||||
|
};
|
||||||
|
|
||||||
|
if (fixedHolidays.Contains(date.Date))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Rörliga helgdagar
|
||||||
|
var easter = GetEasterSunday(year);
|
||||||
|
|
||||||
|
var movable = new[]
|
||||||
|
{
|
||||||
|
easter.AddDays(-2), // Långfredagen
|
||||||
|
easter, // Påskdagen
|
||||||
|
easter.AddDays(1), // Annandag påsk
|
||||||
|
easter.AddDays(39), // Kristi himmelsfärd
|
||||||
|
easter.AddDays(49), // Pingstdagen
|
||||||
|
GetMidsummerDay(year),
|
||||||
|
};
|
||||||
|
|
||||||
|
return movable.Contains(date.Date);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DateTime GetMidsummerDay(int year)
|
||||||
|
{
|
||||||
|
// Lördag mellan 20–26 juni
|
||||||
|
for (int day = 20; day <= 26; day++)
|
||||||
|
{
|
||||||
|
var d = new DateTime(year, 6, day);
|
||||||
|
if (d.DayOfWeek == DayOfWeek.Saturday)
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
throw new Exception("Midsummer not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static DateTime GetEasterSunday(int year)
|
||||||
|
{
|
||||||
|
// Meeus/Jones/Butcher
|
||||||
|
int a = year % 19;
|
||||||
|
int b = year / 100;
|
||||||
|
int c = year % 100;
|
||||||
|
int d = b / 4;
|
||||||
|
int e = b % 4;
|
||||||
|
int f = (b + 8) / 25;
|
||||||
|
int g = (b - f + 1) / 3;
|
||||||
|
int h = (19 * a + b - d - g + 15) % 30;
|
||||||
|
int i = c / 4;
|
||||||
|
int k = c % 4;
|
||||||
|
int l = (32 + 2 * e + 2 * i - h - k) % 7;
|
||||||
|
int m = (a + 11 * h + 22 * l) / 451;
|
||||||
|
int month = (h + l - 7 * m + 114) / 31;
|
||||||
|
int day = ((h + l - 7 * m + 114) % 31) + 1;
|
||||||
|
|
||||||
|
return new DateTime(year, month, day);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
public static class SalaryDateService
|
||||||
|
{
|
||||||
|
public static DateTime GetSalaryPayDate(int year, int month)
|
||||||
|
{
|
||||||
|
var payDate = new DateTime(year, month, 25);
|
||||||
|
|
||||||
|
while (payDate.DayOfWeek == DayOfWeek.Saturday ||
|
||||||
|
payDate.DayOfWeek == DayOfWeek.Sunday ||
|
||||||
|
SwedishHolidays.IsRedDay(payDate))
|
||||||
|
{
|
||||||
|
payDate = payDate.AddDays(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return payDate;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"AdminUsername": "admin",
|
|
||||||
"AdminEmail": "admin@localhost",
|
|
||||||
"AdminPassword": "Admin123!",
|
|
||||||
"IsConfigured": true,
|
|
||||||
"DbHost": "192.168.1.108",
|
|
||||||
"DbPort": 3306,
|
|
||||||
"DbName": "lewel_prod",
|
|
||||||
"DbUser": "lewel",
|
|
||||||
"DbPassword": "W542.Hl;)%ta"
|
|
||||||
}
|
|
||||||
@@ -2,7 +2,7 @@ version: '3.8'
|
|||||||
|
|
||||||
services:
|
services:
|
||||||
aberwyn-app:
|
aberwyn-app:
|
||||||
image: 192.168.1.9:3000/tai/aberwyn/aberwyn:latest
|
image: gitea.zcz.se/tai/aberwyn/aberwyn:latest
|
||||||
container_name: aberwyn-app-prod
|
container_name: aberwyn-app-prod
|
||||||
ports:
|
ports:
|
||||||
- "8080:80"
|
- "8080:80"
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"AdminUsername": "admin",
|
|
||||||
"AdminEmail": "admin@localhost",
|
|
||||||
"AdminPassword": "Admin123!",
|
|
||||||
"IsConfigured": true,
|
|
||||||
"DbHost": "192.168.1.108",
|
|
||||||
"DbPort": 3306,
|
|
||||||
"DbName": "lewel_prod",
|
|
||||||
"DbUser": "lewel",
|
|
||||||
"DbPassword": "W542.Hl;)%ta"
|
|
||||||
}
|
|
||||||
@@ -27,7 +27,6 @@ namespace Aberwyn.Models
|
|||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
[ValidateNever]
|
[ValidateNever]
|
||||||
public BudgetPeriod BudgetPeriod { get; set; }
|
public BudgetPeriod BudgetPeriod { get; set; }
|
||||||
|
|
||||||
public List<BudgetItem> Items { get; set; } = new();
|
public List<BudgetItem> Items { get; set; } = new();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,4 +126,6 @@ namespace Aberwyn.Models
|
|||||||
public string Color { get; set; } // standardfärg, kan modifieras per period
|
public string Color { get; set; } // standardfärg, kan modifieras per period
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
@attribute [Authorize(Roles = "Budget")]
|
@attribute [Authorize(Roles = "Budget")]
|
||||||
@using Microsoft.AspNetCore.Authorization
|
@using Microsoft.AspNetCore.Authorization
|
||||||
|
|
||||||
|
|
||||||
@{
|
@{
|
||||||
ViewData["Title"] = "Budget";
|
ViewData["Title"] = "Budget";
|
||||||
}
|
}
|
||||||
@@ -51,6 +52,7 @@
|
|||||||
<div class="budget-summary-box compact">
|
<div class="budget-summary-box compact">
|
||||||
<h3>Sammanställning</h3>
|
<h3>Sammanställning</h3>
|
||||||
<ul class="summary-list">
|
<ul class="summary-list">
|
||||||
|
<li><span>Utbetalning:</span>{{ salaryPayDate || '–' }}</li>
|
||||||
<li><span>💰 Inkomst:</span> {{ getTotalIncome() | number:0 }} kr</li>
|
<li><span>💰 Inkomst:</span> {{ getTotalIncome() | number:0 }} kr</li>
|
||||||
<li><span>💸 Utgift:</span> {{ getTotalExpense() | number:0 }} kr</li>
|
<li><span>💸 Utgift:</span> {{ getTotalExpense() | number:0 }} kr</li>
|
||||||
<li><span>🏦 Sparande:</span> {{ getTotalSaving() | number:0 }} kr</li>
|
<li><span>🏦 Sparande:</span> {{ getTotalSaving() | number:0 }} kr</li>
|
||||||
|
|||||||
@@ -79,8 +79,8 @@
|
|||||||
<div class="card-container" ng-show="school.expanded">
|
<div class="card-container" ng-show="school.expanded">
|
||||||
<div class="meal-card" ng-repeat="day in school.days">
|
<div class="meal-card" ng-repeat="day in school.days">
|
||||||
<div class="card-content school-meal-card-content">
|
<div class="card-content school-meal-card-content">
|
||||||
<div class="day">{{ day.weekday }}</div>
|
<div class="day">{{ day.Weekday }}</div>
|
||||||
<div class="meal" ng-repeat="meal in day.courses">{{ meal }}</div>
|
<div class="meal" ng-repeat="meal in day.Courses">{{ meal }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -24,12 +24,14 @@ body {
|
|||||||
font-family: 'Segoe UI', sans-serif;
|
font-family: 'Segoe UI', sans-serif;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.budget-page {
|
.budget-page {
|
||||||
padding: 16px 24px;
|
padding: 16px 24px;
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
max-width: unset;
|
max-width: unset;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.budget-header {
|
.budget-header {
|
||||||
@@ -63,6 +65,7 @@ body {
|
|||||||
|
|
||||||
.menu-container {
|
.menu-container {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.month-picker-dropdown select,
|
.month-picker-dropdown select,
|
||||||
@@ -100,7 +103,6 @@ body {
|
|||||||
grid-template-columns: repeat(auto-fit, minmax(220px, 300px));
|
grid-template-columns: repeat(auto-fit, minmax(220px, 300px));
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -307,7 +307,7 @@ h1 {
|
|||||||
|
|
||||||
.school-meal-card-content .meal {
|
.school-meal-card-content .meal {
|
||||||
font-size: clamp(0.55rem, 1.0vw, 0.8rem);
|
font-size: clamp(0.55rem, 1.0vw, 0.8rem);
|
||||||
line-height: 1.2;
|
line-height: 1.3;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
|||||||
@@ -155,6 +155,7 @@ body {
|
|||||||
position: relative;
|
position: relative;
|
||||||
z-index: 0;
|
z-index: 0;
|
||||||
overflow: visible !important;
|
overflow: visible !important;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.main-panel {
|
.main-panel {
|
||||||
@@ -171,6 +172,7 @@ body {
|
|||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
line-height: 1.7;
|
line-height: 1.7;
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
|
padding: 5px 5px 0 5px;
|
||||||
position: static !important;
|
position: static !important;
|
||||||
z-index: auto !important;
|
z-index: auto !important;
|
||||||
}
|
}
|
||||||
@@ -360,6 +362,7 @@ body {
|
|||||||
.main-panel {
|
.main-panel {
|
||||||
position: relative !important;
|
position: relative !important;
|
||||||
z-index: 1 !important;
|
z-index: 1 !important;
|
||||||
|
padding: 0 5px 0 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.main-nav {
|
.main-nav {
|
||||||
|
|||||||
@@ -60,6 +60,15 @@ app.controller('BudgetController', function ($scope, $http) {
|
|||||||
$scope.loadBudget();
|
$scope.loadBudget();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$scope.loadSalaryDate = function () {
|
||||||
|
const year = $scope.selectedYear;
|
||||||
|
const month = $scope.selectedMonth -1;
|
||||||
|
|
||||||
|
$http.get(`/api/budget/${year}/${month}/salary-date`)
|
||||||
|
.then(function (res) {
|
||||||
|
$scope.salaryPayDate = res.data.display;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
$scope.setItemType = function (item, type) {
|
$scope.setItemType = function (item, type) {
|
||||||
if (type === 'expense') {
|
if (type === 'expense') {
|
||||||
@@ -126,7 +135,6 @@ app.controller('BudgetController', function ($scope, $http) {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
$scope.showToast = function (message, isError = false) {
|
$scope.showToast = function (message, isError = false) {
|
||||||
const toast = document.createElement("div");
|
const toast = document.createElement("div");
|
||||||
toast.className = "toast" + (isError ? " error" : "") + " show";
|
toast.className = "toast" + (isError ? " error" : "") + " show";
|
||||||
@@ -216,6 +224,7 @@ app.controller('BudgetController', function ($scope, $http) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.finally(function () {
|
.finally(function () {
|
||||||
|
$scope.loadSalaryDate();
|
||||||
$scope.loading = false;
|
$scope.loading = false;
|
||||||
setTimeout($scope.drawCategoryChart, 0);
|
setTimeout($scope.drawCategoryChart, 0);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -51,9 +51,9 @@ angular.module('mealMenuApp', ['ngSanitize'])
|
|||||||
$scope.schoolMealsBySchool = [];
|
$scope.schoolMealsBySchool = [];
|
||||||
|
|
||||||
$scope.schoolSensors = [
|
$scope.schoolSensors = [
|
||||||
{ name: "William – Engelbrektsskolan", entity: "sensor.engelbrektsskolan" },
|
{ name: "William – Engelbrektsskolan", entity: "sensor.william_lunch" },
|
||||||
{ name: "Louise - Nyeds skolan", entity: "sensor.nyedsskola" },
|
{ name: "Louise - Nyeds skolan", entity: "sensor.louise_lunch" },
|
||||||
{ name: "Ludwig - Skogsgläntan", entity: "sensor.skogsglantan" }
|
{ name: "Ludwig - Skogsgläntan", entity: "sensor.ludwig_lunch" }
|
||||||
];
|
];
|
||||||
|
|
||||||
$scope.schoolMealsBySchool = [];
|
$scope.schoolMealsBySchool = [];
|
||||||
|
|||||||
Reference in New Issue
Block a user