Compare commits
9 Commits
0a5f7be8a0
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| c08bdc3384 | |||
| d481b8ee0e | |||
| 945ad3baf8 | |||
| b4f39381e3 | |||
| a953bce90e | |||
| ed23bd8868 | |||
| 7eb26ad173 | |||
| c453f617e8 | |||
| 0b979cfada |
@@ -0,0 +1,4 @@
|
|||||||
|
venv
|
||||||
|
.venv
|
||||||
|
__pycache__
|
||||||
|
.idea
|
||||||
+63
@@ -0,0 +1,63 @@
|
|||||||
|
# Use the official Python runtime image
|
||||||
|
FROM python:3.13-alpine
|
||||||
|
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
|
||||||
|
|
||||||
|
RUN apk add --no-cache sqlite
|
||||||
|
|
||||||
|
# Set the working directory inside the container
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
|
||||||
|
# Create a non-privileged user that the app will run under.
|
||||||
|
# See https://docs.docker.com/go/dockerfile-user-best-practices/
|
||||||
|
ARG UID=10001
|
||||||
|
RUN adduser \
|
||||||
|
--disabled-password \
|
||||||
|
--shell "/sbin/nologin" \
|
||||||
|
--uid "${UID}" \
|
||||||
|
appuser
|
||||||
|
|
||||||
|
# Set environment variables
|
||||||
|
|
||||||
|
## Python
|
||||||
|
# Prevents Python from writing pyc files to disk
|
||||||
|
ENV PYTHONDONTWRITEBYTECODE=1
|
||||||
|
# Prevents Python from buffering stdout and stderr
|
||||||
|
ENV PYTHONUNBUFFERED=1
|
||||||
|
# Ignore pip warning about running as root
|
||||||
|
ENV PIP_ROOT_USER_ACTION=ignore
|
||||||
|
|
||||||
|
## uv
|
||||||
|
# Enable bytecode compilation
|
||||||
|
ENV UV_COMPILE_BYTECODE=1
|
||||||
|
# Copy from the cache instead of linking since it's a mounted volume
|
||||||
|
ENV UV_LINK_MODE=copy
|
||||||
|
# Disable development dependencies
|
||||||
|
ENV UV_NO_DEV=1
|
||||||
|
|
||||||
|
# copy project settings
|
||||||
|
COPY pyproject.toml uv.lock /app/
|
||||||
|
|
||||||
|
# Install the project's dependencies using the lockfile and settings
|
||||||
|
RUN --mount=type=cache,target=/root/.cache/uv \
|
||||||
|
uv sync --locked --no-install-project
|
||||||
|
|
||||||
|
RUN chown appuser:appuser /app
|
||||||
|
|
||||||
|
# Copy the Django project files to the container
|
||||||
|
COPY --chown=appuser:appuser excel_mimic /app/excel_mimic
|
||||||
|
COPY --chown=appuser:appuser sheets /app/sheets
|
||||||
|
COPY --chown=appuser:appuser manage.py db.sqlite3 /app/
|
||||||
|
|
||||||
|
# Expose the Django port
|
||||||
|
EXPOSE 8000
|
||||||
|
|
||||||
|
|
||||||
|
# Switch to the non-privileged user to run the application.
|
||||||
|
USER appuser
|
||||||
|
|
||||||
|
# Allow all hosts
|
||||||
|
ENV DJANGO_ALLOWED_HOSTS=*
|
||||||
|
|
||||||
|
# Run Django’s development server
|
||||||
|
CMD ["uv", "run", "manage.py", "runserver", "0.0.0.0:8000"]
|
||||||
@@ -1,3 +1,14 @@
|
|||||||
|
# Build new container
|
||||||
|
podman build . -t he-database
|
||||||
|
podman tag he-database gitea.pkm.physik.tu-darmstadt.de/markusro/he-database:latest
|
||||||
|
podman push gitea.pkm.physik.tu-darmstadt.de/markusro/he-database:latest
|
||||||
|
|
||||||
|
# Run and update container
|
||||||
|
Also check documentation from [RedHat](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux_atomic_host/7/html/managing_containers/running_containers_as_systemd_services_with_podman).
|
||||||
|
|
||||||
|
podman pull gitea.pkm.physik.tu-darmstadt.de/markusro/he-database:latest
|
||||||
|
podman run --replace --detach --name he-database -p 8000:8000 he-database:latest
|
||||||
|
|
||||||
# Create DB Schema in chartdb.io
|
# Create DB Schema in chartdb.io
|
||||||
|
|
||||||
1. go to https://app.chartdb.io
|
1. go to https://app.chartdb.io
|
||||||
|
|||||||
BIN
Binary file not shown.
@@ -23,9 +23,9 @@ BASE_DIR = Path(__file__).resolve().parent.parent
|
|||||||
SECRET_KEY = 'django-insecure-qsqw7#kmvlyc1ou5emdh8^_bqnvp+hui(4w#1yy4ui82+p=%p*'
|
SECRET_KEY = 'django-insecure-qsqw7#kmvlyc1ou5emdh8^_bqnvp+hui(4w#1yy4ui82+p=%p*'
|
||||||
|
|
||||||
# SECURITY WARNING: don't run with debug turned on in production!
|
# SECURITY WARNING: don't run with debug turned on in production!
|
||||||
DEBUG = True
|
DEBUG = False
|
||||||
|
|
||||||
ALLOWED_HOSTS = []
|
ALLOWED_HOSTS = ["*"]
|
||||||
|
|
||||||
|
|
||||||
# Application definition
|
# Application definition
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
[project]
|
||||||
|
name = "he-database"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Add your description here"
|
||||||
|
readme = "README.md"
|
||||||
|
requires-python = ">=3.12"
|
||||||
|
dependencies = [
|
||||||
|
"django==6.0.4",
|
||||||
|
]
|
||||||
@@ -146,7 +146,7 @@
|
|||||||
<body>
|
<body>
|
||||||
|
|
||||||
<a href="{% url 'clients_list' %}" class="btn btn-outline-primary">
|
<a href="{% url 'clients_list' %}" class="btn btn-outline-primary">
|
||||||
⇦ Go to Clients
|
⇦ Zur Startseite
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<h2>Betriebskosten</h2>
|
<h2>Betriebskosten</h2>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<h3>Allgemeines 6-Monats-Intervall</h3>
|
<h3>Allgemeines 6-Monats-Intervall</h3>
|
||||||
|
|
||||||
<label>Year:</label>
|
<label>Jahr:</label>
|
||||||
<select name="year">
|
<select name="year">
|
||||||
{% for y in available_years %}
|
{% for y in available_years %}
|
||||||
<option value="{{ y }}" {% if y == interval_year %}selected{% endif %}>
|
<option value="{{ y }}" {% if y == interval_year %}selected{% endif %}>
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<label>Start Month:</label>
|
<label>Startmonat:</label>
|
||||||
<select name="start_month">
|
<select name="start_month">
|
||||||
<option value="1" {% if interval_start_month == 1 %}selected{% endif %}>01</option>
|
<option value="1" {% if interval_start_month == 1 %}selected{% endif %}>01</option>
|
||||||
<option value="2" {% if interval_start_month == 2 %}selected{% endif %}>02</option>
|
<option value="2" {% if interval_start_month == 2 %}selected{% endif %}>02</option>
|
||||||
@@ -33,13 +33,13 @@
|
|||||||
<option value="12" {% if interval_start_month == 12 %}selected{% endif %}>12</option>
|
<option value="12" {% if interval_start_month == 12 %}selected{% endif %}>12</option>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<button type="submit">Apply Interval</button>
|
<button type="submit">Intervall anwenden</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
|
||||||
<!-- Year Filter -->
|
<!-- Year Filter -->
|
||||||
<div class="year-filter">
|
<div class="year-filter">
|
||||||
<label for="year-select">Year:</label>
|
<label for="year-select">Jahr:</label>
|
||||||
<select id="year-select" onchange="window.location.href='?year='+this.value">
|
<select id="year-select" onchange="window.location.href='?year='+this.value">
|
||||||
{% for year in available_years %}
|
{% for year in available_years %}
|
||||||
<option value="{{ year }}" {% if year == current_year %}selected{% endif %}>
|
<option value="{{ year }}" {% if year == current_year %}selected{% endif %}>
|
||||||
@@ -73,7 +73,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<tr class="total">
|
<tr class="total">
|
||||||
<td><strong>Σ (all clients, selected 6-month interval)</strong></td>
|
<td><strong>Σ (alle Kunden, ausgewähltes 6-Monats-Intervall)</strong></td>
|
||||||
|
|
||||||
{% for month in months %}
|
{% for month in months %}
|
||||||
<td>-</td>
|
<td>-</td>
|
||||||
@@ -86,12 +86,12 @@
|
|||||||
|
|
||||||
<!-- Navigation Buttons -->
|
<!-- Navigation Buttons -->
|
||||||
<div class="navigation-buttons">
|
<div class="navigation-buttons">
|
||||||
<a href="{% url 'table_one' %}" class="nav-button">Go to Helium Input</a>
|
<a href="{% url 'table_one' %}" class="nav-button">Heliumrückgabe</a>
|
||||||
<a href="{% url 'table_two' %}" class="nav-button">Go to Helium Output</a>
|
<a href="{% url 'table_two' %}" class="nav-button">Heliumausgabe</a>
|
||||||
<a href="/admin/" class="nav-button admin-button">Go to Admin Panel</a>
|
<a href="/admin/" class="nav-button admin-button">Admin</a>
|
||||||
<a href="{% url 'betriebskosten_list' %}" class="nav-button">Betriebskosten</a>
|
<a href="{% url 'betriebskosten_list' %}" class="nav-button">Betriebskosten</a>
|
||||||
|
|
||||||
<a href="{% url 'monthly_sheet' year=2024 month=1 %}" class="nav-button">Monthly Sheets</a>
|
<a href="{% url 'monthly_sheet' year=2024 month=1 %}" class="nav-button">Monatsbilanz</a>
|
||||||
<a href="{% url 'halfyear_balance' %}" class="nav-button">Halbjahres Bilanz</a>
|
<a href="{% url 'halfyear_balance' %}" class="nav-button">Halbjahres Bilanz</a>
|
||||||
|
|
||||||
<!-- ✅ NEW -->
|
<!-- ✅ NEW -->
|
||||||
|
|||||||
@@ -4,27 +4,43 @@
|
|||||||
<div class="spreadsheet-container">
|
<div class="spreadsheet-container">
|
||||||
<!-- Navigation Header -->
|
<!-- Navigation Header -->
|
||||||
<div class="sheet-navigation">
|
<div class="sheet-navigation">
|
||||||
{# Previous month link #}
|
<div class="nav-left">
|
||||||
{% with pm=prev_month %}
|
<a href="{% url 'clients_list' %}" class="main-page-link">🏠 Hauptseite</a>
|
||||||
{% if pm.year and pm.month %}
|
|
||||||
<a href="{% url 'monthly_sheet' pm.year pm.month %}">← Previous</a>
|
|
||||||
{% else %}
|
|
||||||
<span class="disabled-link">← Previous</span>
|
|
||||||
{% endif %}
|
|
||||||
{% endwith %}
|
|
||||||
|
|
||||||
<h2>{{ year }} - {{ month_name }} (Sheet {{ month }}/6)</h2>
|
{% with pm=prev_month %}
|
||||||
|
{% if pm.year and pm.month %}
|
||||||
|
<a href="{% url 'monthly_sheet' pm.year pm.month %}">← Vorherigen Monat</a>
|
||||||
|
{% else %}
|
||||||
|
<span class="disabled-link">← Vorherigen Monat</span>
|
||||||
|
{% endif %}
|
||||||
|
{% endwith %}
|
||||||
|
</div>
|
||||||
|
|
||||||
{# Next month link #}
|
<div class="nav-center">
|
||||||
{% with nm=next_month %}
|
<h2>{{ year }} - {{ month_name }} (Sheet {{ month }}/6)</h2>
|
||||||
{% if nm.year and nm.month %}
|
</div>
|
||||||
<a href="{% url 'monthly_sheet' nm.year nm.month %}">Next →</a>
|
|
||||||
{% else %}
|
|
||||||
<span class="disabled-link">Next →</span>
|
|
||||||
{% endif %}
|
|
||||||
{% endwith %}
|
|
||||||
|
|
||||||
<a href="{% url 'summary_sheet' year month %}">Go to Summary</a>
|
<div class="nav-right">
|
||||||
|
{% with nm=next_month %}
|
||||||
|
{% if nm.year and nm.month %}
|
||||||
|
<a href="{% url 'monthly_sheet' nm.year nm.month %}">Nächsten Monat →</a>
|
||||||
|
{% else %}
|
||||||
|
<span class="disabled-link">Nächsten Monat →</span>
|
||||||
|
{% endif %}
|
||||||
|
{% endwith %}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class="sheet-jump-box">
|
||||||
|
<label for="jump-year">Jahr:</label>
|
||||||
|
<input type="number" id="jump-year" min="2000" max="2100" value="{{ year }}">
|
||||||
|
|
||||||
|
<label for="jump-month">Monat:</label>
|
||||||
|
<input type="number" id="jump-month" min="1" max="12" value="{{ month }}">
|
||||||
|
|
||||||
|
<button type="button" id="jump-sheet-btn" class="btn btn-primary">Anwenden</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Top Tables Container -->
|
<!-- Top Tables Container -->
|
||||||
@@ -32,11 +48,11 @@
|
|||||||
<div class="top-tables">
|
<div class="top-tables">
|
||||||
<!-- Left Table (18 rows × clients) -->
|
<!-- Left Table (18 rows × clients) -->
|
||||||
<div class="table-container top-left-table">
|
<div class="table-container top-left-table">
|
||||||
<h3>Table 1: Top Left</h3>
|
<h3>Table 1: Oben Links</h3>
|
||||||
<table class="spreadsheet-table">
|
<table class="spreadsheet-table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Row Label</th>
|
<th>Zeilenbezeichnung</th>
|
||||||
{% for header in top_left_headers %}
|
{% for header in top_left_headers %}
|
||||||
<th>{{ header }}</th>
|
<th>{{ header }}</th>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
@@ -129,11 +145,11 @@
|
|||||||
<!-- Right Table (24 rows × 6 clients) -->
|
<!-- Right Table (24 rows × 6 clients) -->
|
||||||
<!-- Update the top-right table section in monthly_sheet.html -->
|
<!-- Update the top-right table section in monthly_sheet.html -->
|
||||||
<div class="table-container top-right-table">
|
<div class="table-container top-right-table">
|
||||||
<h3>Table 2: Top Right</h3>
|
<h3>Table 2: Oben Rechts</h3>
|
||||||
<table class="spreadsheet-table">
|
<table class="spreadsheet-table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Row Label</th>
|
<th>Zeilenbezeichnung</th>
|
||||||
{% for header in top_right_headers %}
|
{% for header in top_right_headers %}
|
||||||
<th>{{ header }}</th>
|
<th>{{ header }}</th>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
@@ -236,11 +252,11 @@
|
|||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<div class="table-container overall-summary-table">
|
<div class="table-container overall-summary-table">
|
||||||
<h3>Gesamtsumme (Top Left + Top Right)</h3>
|
<h3>Gesamtsumme (Oben Links+Rechts)</h3>
|
||||||
<table class="spreadsheet-table">
|
<table class="spreadsheet-table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Row Label</th>
|
<th>Zeilenbezeichnung</th>
|
||||||
<th>Σ</th>
|
<th>Σ</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@@ -264,7 +280,7 @@
|
|||||||
<!-- Bottom Tables -->
|
<!-- Bottom Tables -->
|
||||||
<div class="bottom-tables">
|
<div class="bottom-tables">
|
||||||
<div class="table-container bottom-table-1">
|
<div class="table-container bottom-table-1">
|
||||||
<h3>Bottom Table 1 – Gasspeicher</h3>
|
<h3>Untere Tabelle 1 – Gasspeicher</h3>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -373,7 +389,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<div class="table-container bottom-table-2">
|
<div class="table-container bottom-table-2">
|
||||||
<h3>Bottom Table 2 – Verbraucherbestand L-He</h3>
|
<h3>Untere Tabelle 2 – Verbraucherbestand L-He</h3>
|
||||||
|
|
||||||
<table class="spreadsheet-table">
|
<table class="spreadsheet-table">
|
||||||
<tbody>
|
<tbody>
|
||||||
@@ -450,7 +466,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="table-container bottom-table-3">
|
<div class="table-container bottom-table-3">
|
||||||
<h3>Bottom Table 3 – Bilanz</h3>
|
<h3>Untere Tabelle 3 – Bilanz</h3>
|
||||||
|
|
||||||
<table class="spreadsheet-table">
|
<table class="spreadsheet-table">
|
||||||
<thead>
|
<thead>
|
||||||
@@ -621,17 +637,7 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Save Button -->
|
|
||||||
<div class="action-bar">
|
|
||||||
<button id="save-all-btn" class="btn btn-primary">Save All Cells</button>
|
|
||||||
<div id="save-status"></div>
|
|
||||||
<div class="legend">
|
|
||||||
<small>
|
|
||||||
<span style="background-color: #d1ecf1; padding: 2px 5px;">Saved cells</span>
|
|
||||||
<span style="background-color: #d4edda; padding: 2px 5px;">Calculated cells</span>
|
|
||||||
</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Hidden form for cell data -->
|
<!-- Hidden form for cell data -->
|
||||||
@@ -645,16 +651,48 @@
|
|||||||
padding: 20px;
|
padding: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.nav-left,
|
||||||
|
.nav-center,
|
||||||
|
.nav-right {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-center h2 {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-page-link {
|
||||||
|
font-weight: bold;
|
||||||
|
text-decoration: none;
|
||||||
|
color: #007bff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sheet-jump-box {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sheet-jump-box input {
|
||||||
|
width: 80px;
|
||||||
|
padding: 6px;
|
||||||
|
}
|
||||||
.sheet-navigation {
|
.sheet-navigation {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
gap: 20px;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
background-color: #f8f9fa;
|
background-color: #f8f9fa;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.sheet-navigation h2 {
|
.sheet-navigation h2 {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
@@ -821,6 +859,22 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
|
$('#jump-sheet-btn').on('click', function() {
|
||||||
|
const year = parseInt($('#jump-year').val(), 10);
|
||||||
|
const month = parseInt($('#jump-month').val(), 10);
|
||||||
|
|
||||||
|
if (isNaN(year) || isNaN(month)) {
|
||||||
|
alert('Bitte Jahr und Monat eingeben.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (month < 1 || month > 12) {
|
||||||
|
alert('Monat muss zwischen 1 und 12 liegen.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.location.href = '/sheet/' + year + '/' + month + '/';
|
||||||
|
});
|
||||||
|
|
||||||
// ---------- Helpers ----------
|
// ---------- Helpers ----------
|
||||||
|
|
||||||
|
|||||||
@@ -286,21 +286,21 @@
|
|||||||
|
|
||||||
<!-- This link should ONLY wrap the text below -->
|
<!-- This link should ONLY wrap the text below -->
|
||||||
<a href="{% url 'clients_list' %}" class="btn btn-outline-primary">
|
<a href="{% url 'clients_list' %}" class="btn btn-outline-primary">
|
||||||
⇦ Go to Clients
|
⇦ Zur Startseite
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<!-- 6-Month overview card (OUTSIDE any <a>) -->
|
<!-- 6-Month overview card (OUTSIDE any <a>) -->
|
||||||
<div class="overview-card">
|
<div class="overview-card">
|
||||||
<div class="overview-title">Helium Input – 6 Month Overview</div>
|
<div class="overview-title">Heliumrückgabe– Überblick über 6 Monate</div>
|
||||||
|
|
||||||
{% if overview %}
|
{% if overview %}
|
||||||
<div class="overview-subtitle">
|
<div class="overview-subtitle">
|
||||||
Period:
|
Interval
|
||||||
<strong>
|
<strong>
|
||||||
{{ overview.start_month }}/{{ overview.start_year }}
|
{{ overview.start_month }}/{{ overview.start_year }}
|
||||||
– {{ overview.end_month }}/{{ overview.end_year }}
|
– {{ overview.end_month }}/{{ overview.end_year }}
|
||||||
</strong>
|
</strong>
|
||||||
(selected on the main page)
|
(ausgewählt am Start Seite)
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<table class="overview-table">
|
<table class="overview-table">
|
||||||
@@ -310,7 +310,7 @@
|
|||||||
{% for g in overview.groups %}
|
{% for g in overview.groups %}
|
||||||
<th>{{ g.label }}</th>
|
<th>{{ g.label }}</th>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<th>Month total</th>
|
<th>Monat total</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@@ -342,7 +342,7 @@
|
|||||||
|
|
||||||
<h2>Helium Input</h2>
|
<h2>Helium Input</h2>
|
||||||
<div class="table-container">
|
<div class="table-container">
|
||||||
<button class="add-row-btn" id="add-row-one">Add Row</button>
|
<button class="add-row-btn" id="add-row-one">Eingabe hinzufügen</button>
|
||||||
<table id="table-one">
|
<table id="table-one">
|
||||||
<colgroup>
|
<colgroup>
|
||||||
<col style="width: 3%"> <!-- # -->
|
<col style="width: 3%"> <!-- # -->
|
||||||
@@ -367,7 +367,7 @@
|
|||||||
<th>#</th>
|
<th>#</th>
|
||||||
<th>ID</th>
|
<th>ID</th>
|
||||||
<th>Institute</th>
|
<th>Institute</th>
|
||||||
<th>Client</th>
|
<th>Kunde</th>
|
||||||
<th>Druck</th>
|
<th>Druck</th>
|
||||||
<th>Reinheit</th>
|
<th>Reinheit</th>
|
||||||
<th>Druckkorrektur</th>
|
<th>Druckkorrektur</th>
|
||||||
@@ -377,8 +377,8 @@
|
|||||||
<th>L-He</th>
|
<th>L-He</th>
|
||||||
<th>L-He zus.</th>
|
<th>L-He zus.</th>
|
||||||
<th>L-He ges.</th>
|
<th>L-He ges.</th>
|
||||||
<th>Date</th>
|
<th>Datum</th>
|
||||||
<th>Month</th>
|
<th>Monat</th>
|
||||||
<th>Actions</th>
|
<th>Actions</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@@ -409,8 +409,8 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td class="actions">
|
<td class="actions">
|
||||||
<button class="edit-btn-one">Edit</button>
|
<button class="edit-btn-one">Bearbeiten</button>
|
||||||
<button class="delete-btn-one">Delete</button>
|
<button class="delete-btn-one">Löschen</button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
@@ -522,7 +522,7 @@
|
|||||||
<!-- Client Selection -->
|
<!-- Client Selection -->
|
||||||
<label for="edit-client-id">Kunde:</label>
|
<label for="edit-client-id">Kunde:</label>
|
||||||
<select id="edit-client-id" disabled>
|
<select id="edit-client-id" disabled>
|
||||||
<option value="">Select Institute first</option>
|
<option value="">Institut erstmal auswählen</option>
|
||||||
{% for client in clients %}
|
{% for client in clients %}
|
||||||
<option value="{{ client.id }}" data-institute="{{ client.institute.id }}">{{ client.name }}</option>
|
<option value="{{ client.id }}" data-institute="{{ client.institute.id }}">{{ client.name }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
@@ -708,19 +708,19 @@
|
|||||||
const instituteId = $('#add-institute-id').val();
|
const instituteId = $('#add-institute-id').val();
|
||||||
|
|
||||||
if (!instituteId) {
|
if (!instituteId) {
|
||||||
alert('Please select an institute');
|
alert('Institut erstmal auswählen');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!clientId) {
|
if (!clientId) {
|
||||||
alert('Please select a client');
|
alert('Kunde erstmal auswählen');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate date first
|
// Validate date first
|
||||||
let dateInput = $('#add-date').val();
|
let dateInput = $('#add-date').val();
|
||||||
if (!dateInput) {
|
if (!dateInput) {
|
||||||
alert('Please select a date');
|
alert('Datum erstmal auswählen');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -732,17 +732,17 @@
|
|||||||
let gesFlaschInhalt = parseFloat($('#add-constant-300').val()) || 300.0;
|
let gesFlaschInhalt = parseFloat($('#add-constant-300').val()) || 300.0;
|
||||||
|
|
||||||
if (isNaN(pressure) || pressure < 0) {
|
if (isNaN(pressure) || pressure < 0) {
|
||||||
alert('Please enter a valid pressure value');
|
alert('Bitte geben Sie einen gültigen Druckwert ein.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isNaN(purity) || purity < 0 || purity > 100) {
|
if (isNaN(purity) || purity < 0 || purity > 100) {
|
||||||
alert('Please enter a valid purity value (0-100)');
|
alert('Bitte geben Sie einen gültigen Reinheitswert ein (0-100).');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isNaN(druckkorrektur) || druckkorrektur < 0) {
|
if (isNaN(druckkorrektur) || druckkorrektur < 0) {
|
||||||
alert('Please enter a valid Druckkorrektur value');
|
alert('Bitte geben Sie einen gültigen Druckkorrekturwert ein');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -875,12 +875,12 @@
|
|||||||
const instituteId = $('#edit-institute-id').val();
|
const instituteId = $('#edit-institute-id').val();
|
||||||
|
|
||||||
if (!instituteId) {
|
if (!instituteId) {
|
||||||
alert('Please select an institute');
|
alert('Institut erstmal auswählen');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!clientId) {
|
if (!clientId) {
|
||||||
alert('Please select a client');
|
alert('Kunde erstmal auswählen');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -892,17 +892,17 @@
|
|||||||
let gesFlaschInhalt = parseFloat($('#edit-constant-300').val()) || 300.0;
|
let gesFlaschInhalt = parseFloat($('#edit-constant-300').val()) || 300.0;
|
||||||
|
|
||||||
if (isNaN(pressure) || pressure < 0) {
|
if (isNaN(pressure) || pressure < 0) {
|
||||||
alert('Please enter a valid pressure value');
|
alert('Bitte geben Sie einen gültigen Druckwert ein.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isNaN(purity) || purity < 0 || purity > 100) {
|
if (isNaN(purity) || purity < 0 || purity > 100) {
|
||||||
alert('Please enter a valid purity value (0-100)');
|
alert('Bitte geben Sie einen gültigen Reinheitswert ein (0-100).');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isNaN(druckkorrektur) || druckkorrektur < 0) {
|
if (isNaN(druckkorrektur) || druckkorrektur < 0) {
|
||||||
alert('Please enter a valid Druckkorrektur value');
|
alert('Bitte geben Sie einen gültigen Druckkorrekturwert ein');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -937,7 +937,7 @@
|
|||||||
|
|
||||||
// Validate inputs
|
// Validate inputs
|
||||||
if (!formData.date) {
|
if (!formData.date) {
|
||||||
alert('Please select a date');
|
alert('Bitte wählen Sie ein Datum.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -993,7 +993,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
error: function () {
|
error: function () {
|
||||||
alert('Failed to delete entry. Please try again.');
|
alert('Eintrag konnte nicht gelöscht werden. Bitte versuchen Sie es erneut.');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -190,28 +190,28 @@
|
|||||||
<div class="d-flex justify-content-start mb-2">
|
<div class="d-flex justify-content-start mb-2">
|
||||||
<!-- "Go to Clients" button at top-left -->
|
<!-- "Go to Clients" button at top-left -->
|
||||||
<a href="{% url 'clients_list' %}" class="btn btn-outline-primary">
|
<a href="{% url 'clients_list' %}" class="btn btn-outline-primary">
|
||||||
⇦ Go to Clients
|
⇦ Zur Startseite
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h2>LHe Dewar Output</h2>
|
<h2>LHe Dewar Output</h2>
|
||||||
|
|
||||||
<div class="table-container">
|
<div class="table-container">
|
||||||
<button class="add-row-btn" id="add-row-two">Add Output</button>
|
<button class="add-row-btn" id="add-row-two">Eingabe hinzufügen</button>
|
||||||
<table id="table-two">
|
<table id="table-two">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>#</th>
|
<th>#</th>
|
||||||
<th>ID</th>
|
<th>ID</th>
|
||||||
<th>Institute</th>
|
<th>Institut</th>
|
||||||
<th>Client</th>
|
<th>Kunde</th>
|
||||||
<th>Date</th>
|
<th>Datum</th>
|
||||||
<th>Warm</th>
|
<th>Warm</th>
|
||||||
<th>LHe Delivery</th>
|
<th>LHe Anlieferung</th>
|
||||||
<th>Vor</th>
|
<th>Vor</th>
|
||||||
<th>Nach</th>
|
<th>Nach</th>
|
||||||
<th>LHe Output</th>
|
<th>LHe Ausgabe</th>
|
||||||
<th>Notes</th>
|
<th>Notizen</th>
|
||||||
<th>Actions</th>
|
<th>Actions</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@@ -230,8 +230,8 @@
|
|||||||
<td>{% if entry.lhe_output is not None %}{{ entry.lhe_output|floatformat:1 }}{% endif %}</td>
|
<td>{% if entry.lhe_output is not None %}{{ entry.lhe_output|floatformat:1 }}{% endif %}</td>
|
||||||
<td>{{ entry.notes }}</td>
|
<td>{{ entry.notes }}</td>
|
||||||
<td class="actions">
|
<td class="actions">
|
||||||
<button class="edit-btn-two">Edit</button>
|
<button class="edit-btn-two">Bearbeiten</button>
|
||||||
<button class="delete-btn-two">Delete</button>
|
<button class="delete-btn-two">Löschen</button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
@@ -245,24 +245,24 @@
|
|||||||
<h3>LHe Dewar Output</h3>
|
<h3>LHe Dewar Output</h3>
|
||||||
|
|
||||||
<!-- Institute Selection -->
|
<!-- Institute Selection -->
|
||||||
<label for="add-institute-id">Institute:</label>
|
<label for="add-institute-id">Institut:</label>
|
||||||
<select id="add-institute-id">
|
<select id="add-institute-id">
|
||||||
<option value="">Select Institute</option>
|
<option value="">Institut auswählen</option>
|
||||||
{% for institute in institutes %}
|
{% for institute in institutes %}
|
||||||
<option value="{{ institute.id }}">{{ institute.name }}</option>
|
<option value="{{ institute.id }}">{{ institute.name }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<!-- Client Selection (will be populated based on institute) -->
|
<!-- Client Selection (will be populated based on institute) -->
|
||||||
<label for="add-client-id">Client:</label>
|
<label for="add-client-id">Kunde:</label>
|
||||||
<select id="add-client-id" disabled>
|
<select id="add-client-id" disabled>
|
||||||
<option value="">Select Institute first</option>
|
<option value="">Institut erstaml auswählen</option>
|
||||||
{% for client in clients %}
|
{% for client in clients %}
|
||||||
<option value="{{ client.id }}" data-institute="{{ client.institute.id }}">{{ client.name }}</option>
|
<option value="{{ client.id }}" data-institute="{{ client.institute.id }}">{{ client.name }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<label for="add-date">Date:</label>
|
<label for="add-date">Datum:</label>
|
||||||
<input type="date" id="add-date">
|
<input type="date" id="add-date">
|
||||||
|
|
||||||
<!-- Changed from checkbox to number input -->
|
<!-- Changed from checkbox to number input -->
|
||||||
@@ -273,7 +273,7 @@
|
|||||||
|
|
||||||
<div class="input-with-label">
|
<div class="input-with-label">
|
||||||
<label for="add-lhe-delivery">LHe Anlieferung:</label>
|
<label for="add-lhe-delivery">LHe Anlieferung:</label>
|
||||||
<input type="text" id="add-lhe-delivery" placeholder="Enter delivery amount">
|
<input type="text" id="add-lhe-delivery" placeholder="Wert eingeben">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="input-with-label">
|
<div class="input-with-label">
|
||||||
@@ -291,8 +291,8 @@
|
|||||||
<input type="number" id="add-lhe-output" readonly>
|
<input type="number" id="add-lhe-output" readonly>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<label for="add-notes">Notes:</label>
|
<label for="add-notes">Notizen:</label>
|
||||||
<textarea id="add-notes" placeholder="Additional notes"></textarea>
|
<textarea id="add-notes" placeholder="Notizen"></textarea>
|
||||||
|
|
||||||
<div class="popup-buttons">
|
<div class="popup-buttons">
|
||||||
<button class="save-btn" id="save-add-two">Save</button>
|
<button class="save-btn" id="save-add-two">Save</button>
|
||||||
@@ -310,16 +310,16 @@
|
|||||||
<!-- Institute Selection -->
|
<!-- Institute Selection -->
|
||||||
<label for="edit-institute-id">Institute:</label>
|
<label for="edit-institute-id">Institute:</label>
|
||||||
<select id="edit-institute-id">
|
<select id="edit-institute-id">
|
||||||
<option value="">Select Institute</option>
|
<option value="">Institut Auswälen</option>
|
||||||
{% for institute in institutes %}
|
{% for institute in institutes %}
|
||||||
<option value="{{ institute.id }}">{{ institute.name }}</option>
|
<option value="{{ institute.id }}">{{ institute.name }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<!-- Client Selection (will be populated based on institute) -->
|
<!-- Client Selection (will be populated based on institute) -->
|
||||||
<label for="edit-client-id">Client:</label>
|
<label for="edit-client-id">Kunde:</label>
|
||||||
<select id="edit-client-id" disabled>
|
<select id="edit-client-id" disabled>
|
||||||
<option value="">Select Institute first</option>
|
<option value="">Institut erstmal Auswälen</option>
|
||||||
{% for client in clients %}
|
{% for client in clients %}
|
||||||
<option value="{{ client.id }}" data-institute="{{ client.institute.id }}">{{ client.name }}</option>
|
<option value="{{ client.id }}" data-institute="{{ client.institute.id }}">{{ client.name }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
@@ -336,7 +336,7 @@
|
|||||||
|
|
||||||
<div class="input-with-label">
|
<div class="input-with-label">
|
||||||
<label for="edit-lhe-delivery">LHe Anlieferung:</label>
|
<label for="edit-lhe-delivery">LHe Anlieferung:</label>
|
||||||
<input type="text" id="edit-lhe-delivery" placeholder="Enter delivery amount">
|
<input type="text" id="edit-lhe-delivery" placeholder="Wert eingeben">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="input-with-label">
|
<div class="input-with-label">
|
||||||
@@ -399,7 +399,7 @@
|
|||||||
function filterClients(instituteId, targetSelect, allOptions) {
|
function filterClients(instituteId, targetSelect, allOptions) {
|
||||||
if (!instituteId) {
|
if (!instituteId) {
|
||||||
// Show only the default option if no institute selected
|
// Show only the default option if no institute selected
|
||||||
targetSelect.html('<option value="">Select Institute first</option>');
|
targetSelect.html('<option value="">erstmal Institut auswählen</option>');
|
||||||
targetSelect.prop('disabled', true);
|
targetSelect.prop('disabled', true);
|
||||||
} else {
|
} else {
|
||||||
// Restore all options first
|
// Restore all options first
|
||||||
@@ -412,7 +412,7 @@
|
|||||||
targetSelect.find('option').hide();
|
targetSelect.find('option').hide();
|
||||||
|
|
||||||
// Always show the "Select Client" option
|
// Always show the "Select Client" option
|
||||||
targetSelect.find('option[value=""]').show().text('Select Client');
|
targetSelect.find('option[value=""]').show().text('Kunde Auswählen');
|
||||||
|
|
||||||
// Show only clients from selected institute
|
// Show only clients from selected institute
|
||||||
const clientsFromInstitute = targetSelect.find(`option[data-institute="${instituteId}"]`);
|
const clientsFromInstitute = targetSelect.find(`option[data-institute="${instituteId}"]`);
|
||||||
@@ -473,7 +473,7 @@
|
|||||||
$('#add-row-two').on('click', function () {
|
$('#add-row-two').on('click', function () {
|
||||||
// Reset form
|
// Reset form
|
||||||
$('#add-institute-id').val('');
|
$('#add-institute-id').val('');
|
||||||
$('#add-client-id').html('<option value="">Select Institute first</option>');
|
$('#add-client-id').html('<option value="">Erstaml institut auswählen</option>');
|
||||||
$('#add-client-id').prop('disabled', true);
|
$('#add-client-id').prop('disabled', true);
|
||||||
$('#add-date').val('');
|
$('#add-date').val('');
|
||||||
$('#add-is-warm').val('0');
|
$('#add-is-warm').val('0');
|
||||||
@@ -645,12 +645,12 @@
|
|||||||
const instituteId = $('#edit-institute-id').val();
|
const instituteId = $('#edit-institute-id').val();
|
||||||
|
|
||||||
if (!instituteId) {
|
if (!instituteId) {
|
||||||
alert('Please select an institute');
|
alert('Institut erstmal auswählen');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!clientId) {
|
if (!clientId) {
|
||||||
alert('Please select a client');
|
alert('Kunde erstmal auswählen');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
version = 1
|
||||||
|
revision = 3
|
||||||
|
requires-python = ">=3.12"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "asgiref"
|
||||||
|
version = "3.11.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/63/40/f03da1264ae8f7cfdbf9146542e5e7e8100a4c66ab48e791df9a03d3f6c0/asgiref-3.11.1.tar.gz", hash = "sha256:5f184dc43b7e763efe848065441eac62229c9f7b0475f41f80e207a114eda4ce", size = 38550, upload-time = "2026-02-03T13:30:14.33Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/5c/0a/a72d10ed65068e115044937873362e6e32fab1b7dce0046aeb224682c989/asgiref-3.11.1-py3-none-any.whl", hash = "sha256:e8667a091e69529631969fd45dc268fa79b99c92c5fcdda727757e52146ec133", size = 24345, upload-time = "2026-02-03T13:30:13.039Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "django"
|
||||||
|
version = "6.0.4"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "asgiref" },
|
||||||
|
{ name = "sqlparse" },
|
||||||
|
{ name = "tzdata", marker = "sys_platform == 'win32'" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/60/b9/4155091ad1788b38563bd77a7258c0834e8c12a7f56f6975deaf54f8b61d/django-6.0.4.tar.gz", hash = "sha256:8cfa2572b3f2768b2e84983cf3c4811877a01edb64e817986ec5d60751c113ac", size = 10907407, upload-time = "2026-04-07T13:55:44.961Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e9/47/3d61d611609764aa71a37f7037b870e7bfb22937366974c4fd46cada7bab/django-6.0.4-py3-none-any.whl", hash = "sha256:14359c809fc16e8f81fd2b59d7d348e4d2d799da6840b10522b6edf7b8afc1da", size = 8368342, upload-time = "2026-04-07T13:55:37.999Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "he-database"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = { virtual = "." }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "django" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.metadata]
|
||||||
|
requires-dist = [{ name = "django", specifier = "==6.0.4" }]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sqlparse"
|
||||||
|
version = "0.5.5"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/90/76/437d71068094df0726366574cf3432a4ed754217b436eb7429415cf2d480/sqlparse-0.5.5.tar.gz", hash = "sha256:e20d4a9b0b8585fdf63b10d30066c7c94c5d7a7ec47c889a2d83a3caa93ff28e", size = 120815, upload-time = "2025-12-19T07:17:45.073Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/49/4b/359f28a903c13438ef59ebeee215fb25da53066db67b305c125f1c6d2a25/sqlparse-0.5.5-py3-none-any.whl", hash = "sha256:12a08b3bf3eec877c519589833aed092e2444e68240a3577e8e26148acc7b1ba", size = 46138, upload-time = "2025-12-19T07:17:46.573Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tzdata"
|
||||||
|
version = "2026.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/19/f5/cd531b2d15a671a40c0f66cf06bc3570a12cd56eef98960068ebbad1bf5a/tzdata-2026.1.tar.gz", hash = "sha256:67658a1903c75917309e753fdc349ac0efd8c27db7a0cb406a25be4840f87f98", size = 197639, upload-time = "2026-04-03T11:25:22.002Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b0/70/d460bd685a170790ec89317e9bd33047988e4bce507b831f5db771e142de/tzdata-2026.1-py2.py3-none-any.whl", hash = "sha256:4b1d2be7ac37ceafd7327b961aa3a54e467efbdb563a23655fbfe0d39cfc42a9", size = 348952, upload-time = "2026-04-03T11:25:20.313Z" },
|
||||||
|
]
|
||||||
Reference in New Issue
Block a user