rewrite styles

This commit is contained in:
Mateusz Gruszczyński
2026-07-14 12:12:46 +02:00
parent de7153e298
commit 1f4d459a64
4 changed files with 214 additions and 21 deletions
+131 -16
View File
@@ -4,7 +4,7 @@ from decimal import Decimal
from functools import wraps from functools import wraps
from flask import Blueprint, render_template, request, redirect, url_for, flash, jsonify, abort from flask import Blueprint, render_template, request, redirect, url_for, flash, jsonify, abort
from flask_login import login_required, current_user from flask_login import login_required, current_user
from sqlalchemy import extract from sqlalchemy import extract, or_
from sqlalchemy.exc import IntegrityError from sqlalchemy.exc import IntegrityError
from .extensions import db from .extensions import db
from .models import User, Vehicle, FuelEntry, CompanySettings, AppSetting, OrlenPrice, FuelCardPolicy, FuelStationCompany, FuelStationPoint, FuelCard, FuelCardStationRule from .models import User, Vehicle, FuelEntry, CompanySettings, AppSetting, OrlenPrice, FuelCardPolicy, FuelStationCompany, FuelStationPoint, FuelCard, FuelCardStationRule
@@ -212,25 +212,140 @@ def orlen_price():
@login_required @login_required
def orlen_data(): def orlen_data():
settings = CompanySettings.query.first() settings = CompanySettings.query.first()
selected_year = request.args.get("year", type=int) or date.today().year today = date.today()
selected_fuels = [f.upper() for f in request.args.getlist("fuel") if f.upper() in FUEL_TYPES] or ["PB95"] mode = request.args.get("mode", "history")
if mode not in {"history", "compare"}:
mode = "history"
include_vat = request.args.get("gross") == "1" include_vat = request.args.get("gross") == "1"
show_lpg_regions = request.args.get('show_lpg_regions') == '1' available_years = [
year for (year,) in db.session.query(extract("year", OrlenPrice.effective_date))
.distinct().order_by(extract("year", OrlenPrice.effective_date).desc()).all()
if year is not None
]
available_years = [int(year) for year in available_years]
if today.year not in available_years:
available_years.insert(0, today.year)
selected_year = request.args.get("year", type=int) or today.year
selected_fuels = [f.upper() for f in request.args.getlist("fuel") if f.upper() in FUEL_TYPES] or ["PB95"]
selected_regions = [r.lower() for r in request.args.getlist("region") if r.lower() in POLISH_REGIONS] selected_regions = [r.lower() for r in request.args.getlist("region") if r.lower() in POLISH_REGIONS]
if not selected_regions: selected_regions=[settings.region.lower()] if not selected_regions:
if not show_lpg_regions: selected_regions=[settings.region.lower()] selected_regions = [settings.region.lower()]
query = OrlenPrice.query.filter(OrlenPrice.fuel_type.in_(selected_fuels), extract("year", OrlenPrice.effective_date)==selected_year)
rows = query.order_by(OrlenPrice.effective_date, OrlenPrice.fuel_type).all() rows = []
chart = {} chart = {}
for fuel in selected_fuels: comparison_summary = []
fuel_rows = [r for r in rows if r.fuel_type == fuel and (fuel != "LPG" or r.region in selected_regions)] comparison_type = request.args.get("comparison_type", "years")
chart_key = fuel if fuel != "LPG" else None compare_fuel = request.args.get("compare_fuel", "PB95").upper()
if fuel == "LPG": if compare_fuel not in FUEL_TYPES:
for region in selected_regions: compare_fuel = "PB95"
chart[f"LPG · {region}"] = [{"date": r.effective_date.isoformat(), "value": round(float(r.price_per_liter) * (1 + float(effective_vat_rate(settings.vat_rate, r.effective_date)) / 100 if include_vat else 1), 4)} for r in fuel_rows if r.region == region] compare_region = request.args.get("compare_region", settings.region).lower()
if compare_region not in POLISH_REGIONS:
compare_region = settings.region.lower()
compare_year = request.args.get("compare_year", type=int) or (selected_year - 1)
def priced_value(row):
multiplier = 1 + float(effective_vat_rate(settings.vat_rate, row.effective_date)) / 100 if include_vat else 1
return round(float(row.price_per_liter) * multiplier, 4)
def summary(label, source_rows):
values = [priced_value(row) for row in source_rows]
return {
"label": label,
"count": len(values),
"average": round(sum(values) / len(values), 4) if values else None,
"minimum": min(values) if values else None,
"maximum": max(values) if values else None,
"first": values[0] if values else None,
"last": values[-1] if values else None,
"change": round(values[-1] - values[0], 4) if len(values) > 1 else None,
}
if mode == "compare":
if comparison_type not in {"years", "periods"}:
comparison_type = "years"
base_query = OrlenPrice.query.filter_by(fuel_type=compare_fuel)
if compare_fuel == "LPG":
base_query = base_query.filter_by(region=compare_region)
if comparison_type == "years":
year_a, year_b = selected_year, compare_year
all_rows = base_query.filter(extract("year", OrlenPrice.effective_date).in_([year_a, year_b])).order_by(OrlenPrice.effective_date).all()
for year in (year_a, year_b):
period_rows = [row for row in all_rows if row.effective_date.year == year]
chart[str(year)] = [{"date": row.effective_date.strftime("%m-%d"), "value": priced_value(row)} for row in period_rows]
comparison_summary.append(summary(str(year), period_rows))
rows = all_rows
period_a_from = date(year_a, 1, 1)
period_a_to = date(year_a, 12, 31)
period_b_from = date(year_b, 1, 1)
period_b_to = date(year_b, 12, 31)
else: else:
chart[fuel] = [{"date": r.effective_date.isoformat(), "value": round(float(r.price_per_liter) * (1 + float(effective_vat_rate(settings.vat_rate, r.effective_date)) / 100 if include_vat else 1), 4)} for r in fuel_rows] def parse_date_arg(name, fallback):
return render_template("orlen.html", rows=rows, chart=chart, selected_fuels=selected_fuels, selected_year=selected_year, settings=settings, fuel_types=FUEL_TYPES, include_vat=include_vat, regions=POLISH_REGIONS, selected_regions=selected_regions, show_lpg_regions=show_lpg_regions) try:
return date.fromisoformat(request.args.get(name, ""))
except ValueError:
return fallback
period_a_from = parse_date_arg("period_a_from", today.replace(month=1, day=1))
period_a_to = parse_date_arg("period_a_to", today)
previous_year = today.year - 1
period_b_from = parse_date_arg("period_b_from", date(previous_year, period_a_from.month, min(period_a_from.day, 28)))
period_b_to = parse_date_arg("period_b_to", date(previous_year, period_a_to.month, min(period_a_to.day, 28)))
if period_a_from > period_a_to:
period_a_from, period_a_to = period_a_to, period_a_from
if period_b_from > period_b_to:
period_b_from, period_b_to = period_b_to, period_b_from
ranges = [
("Okres A", period_a_from, period_a_to),
("Okres B", period_b_from, period_b_to),
]
all_rows = base_query.filter(
or_(
OrlenPrice.effective_date.between(period_a_from, period_a_to),
OrlenPrice.effective_date.between(period_b_from, period_b_to),
)
).order_by(OrlenPrice.effective_date).all()
for label, start, end in ranges:
period_rows = [row for row in all_rows if start <= row.effective_date <= end]
chart[label] = [{"date": f"Dzień {(row.effective_date - start).days + 1:03d}", "value": priced_value(row)} for row in period_rows]
comparison_summary.append(summary(f"{label}: {start.isoformat()} {end.isoformat()}", period_rows))
rows = all_rows
else:
query = OrlenPrice.query.filter(OrlenPrice.fuel_type.in_(selected_fuels), extract("year", OrlenPrice.effective_date) == selected_year)
rows = query.order_by(OrlenPrice.effective_date, OrlenPrice.fuel_type).all()
for fuel in selected_fuels:
fuel_rows = [row for row in rows if row.fuel_type == fuel and (fuel != "LPG" or row.region in selected_regions)]
if fuel == "LPG":
for region in selected_regions:
chart[f"LPG · {region}"] = [{"date": row.effective_date.isoformat(), "value": priced_value(row)} for row in fuel_rows if row.region == region]
else:
chart[fuel] = [{"date": row.effective_date.isoformat(), "value": priced_value(row)} for row in fuel_rows]
period_a_from = period_a_to = period_b_from = period_b_to = None
summary_difference = None
if len(comparison_summary) == 2 and comparison_summary[0]["average"] is not None and comparison_summary[1]["average"] is not None:
first_average = comparison_summary[0]["average"]
second_average = comparison_summary[1]["average"]
summary_difference = {
"value": round(first_average - second_average, 4),
"percent": round((first_average - second_average) / second_average * 100, 2) if second_average else None,
}
return render_template(
"orlen.html", rows=rows, chart=chart, mode=mode,
selected_fuels=selected_fuels, selected_year=selected_year,
compare_year=compare_year, compare_fuel=compare_fuel,
comparison_type=comparison_type, comparison_summary=comparison_summary,
summary_difference=summary_difference, compare_region=compare_region,
period_a_from=period_a_from, period_a_to=period_a_to,
period_b_from=period_b_from, period_b_to=period_b_to,
settings=settings, fuel_types=FUEL_TYPES, include_vat=include_vat,
regions=POLISH_REGIONS, selected_regions=selected_regions,
available_years=available_years,
)
@main.post("/orlen/sync") @main.post("/orlen/sync")
@login_required @login_required
+1
View File
@@ -1 +1,2 @@
(()=>{const F=FuelTrack;F.bindLpgRegionPicker=()=>{const select=F.qs('#lpg-region-select');if(!select||select.dataset.choicesBound)return;select.dataset.choicesBound='1';const choices=new Choices(select,{removeItemButton:true,searchEnabled:true,shouldSort:false,position:'bottom',placeholder:true,placeholderValue:'Wybierz województwa',noResultsText:'Brak województwa',noChoicesText:'Brak opcji',itemSelectText:'Wybierz'});const modal=F.qs('#lpgRegionsModal');modal?.addEventListener('hidden.bs.modal',()=>{const count=F.qsa('option:checked',select).length;const badge=F.qs('#lpg-region-count');if(badge)badge.textContent=count?`${count} wybrano`:'Domyślne województwo'})};F.bindOrlenSync=()=>{const btn=F.qs('#orlen-sync-btn');if(!btn||btn.dataset.bound)return;btn.dataset.bound='1';btn.addEventListener('click',async()=>{const form=F.qs('#orlen-filter-form'),fuels=F.qsa('[name=fuel]:checked',form).map(x=>x.value),year=F.qs('[name=year]',form).value,status=F.qs('#orlen-sync-status');if(!fuels.length){status.textContent='Wybierz co najmniej jedno paliwo.';return}btn.disabled=true;const old=btn.innerHTML;btn.innerHTML='Pobieranie…';try{const regions=F.qsa('[name=region] option:checked',form).map(x=>x.value);const r=await fetch('/api/orlen/sync',{method:'POST',headers:{'Content-Type':'application/json','X-Requested-With':'XMLHttpRequest'},body:JSON.stringify({fuels,year,regions})}),d=await r.json();if(!r.ok)throw new Error(d.error||'Błąd pobierania');status.textContent=d.message;await F.replaceMain(`/orlen?${new URLSearchParams(new FormData(form))}`,{push:true});F.notify(d.message)}catch(e){status.textContent=`Błąd: ${e.message}`;F.notify(e.message,'danger')}finally{btn.disabled=false;btn.innerHTML=old}})}})(); (()=>{const F=FuelTrack;F.bindLpgRegionPicker=()=>{const select=F.qs('#lpg-region-select');if(!select||select.dataset.choicesBound)return;select.dataset.choicesBound='1';const choices=new Choices(select,{removeItemButton:true,searchEnabled:true,shouldSort:false,position:'bottom',placeholder:true,placeholderValue:'Wybierz województwa',noResultsText:'Brak województwa',noChoicesText:'Brak opcji',itemSelectText:'Wybierz'});const modal=F.qs('#lpgRegionsModal');modal?.addEventListener('hidden.bs.modal',()=>{const count=F.qsa('option:checked',select).length;const badge=F.qs('#lpg-region-count');if(badge)badge.textContent=count?`${count} wybrano`:'Domyślne województwo'})};F.bindOrlenSync=()=>{const btn=F.qs('#orlen-sync-btn');if(!btn||btn.dataset.bound)return;btn.dataset.bound='1';btn.addEventListener('click',async()=>{const form=F.qs('#orlen-filter-form'),fuels=F.qsa('[name=fuel]:checked',form).map(x=>x.value),year=F.qs('[name=year]',form).value,status=F.qs('#orlen-sync-status');if(!fuels.length){status.textContent='Wybierz co najmniej jedno paliwo.';return}btn.disabled=true;const old=btn.innerHTML;btn.innerHTML='Pobieranie…';try{const regions=F.qsa('[name=region] option:checked',form).map(x=>x.value);const r=await fetch('/api/orlen/sync',{method:'POST',headers:{'Content-Type':'application/json','X-Requested-With':'XMLHttpRequest'},body:JSON.stringify({fuels,year,regions})}),d=await r.json();if(!r.ok)throw new Error(d.error||'Błąd pobierania');status.textContent=d.message;await F.replaceMain(`/orlen?${new URLSearchParams(new FormData(form))}`,{push:true});F.notify(d.message)}catch(e){status.textContent=`Błąd: ${e.message}`;F.notify(e.message,'danger')}finally{btn.disabled=false;btn.innerHTML=old}})}})();
;(()=>{const F=FuelTrack;F.bindOrlenComparison=()=>{const type=F.qs('#comparison-type'),fuel=F.qs('#compare-fuel');if(type&&!type.dataset.compareBound){type.dataset.compareBound='1';const update=()=>{F.qs('#year-comparison-fields')?.classList.toggle('d-none',type.value!=='years');F.qs('#period-comparison-fields')?.classList.toggle('d-none',type.value!=='periods')};type.addEventListener('change',update);update()}if(fuel&&!fuel.dataset.compareBound){fuel.dataset.compareBound='1';const update=()=>F.qs('#compare-region-wrap')?.classList.toggle('d-none',fuel.value!=='LPG');fuel.addEventListener('change',update);update()}}})();
+49 -5
View File
@@ -1,8 +1,52 @@
{% extends 'base.html' %}{% block title %}Ceny Orlen{% endblock %}{% block content %} {% extends 'base.html' %}
<div class="mb-4"><h1 class="h2 mb-1">Dane cenowe Orlen</h1><p class="text-body-secondary mb-0">Porównuj kilka paliw na jednym wykresie i zapisuj dane w bazie.</p></div> {% block title %}Ceny Orlen{% endblock %}
<div class="card mb-4"><div class="card-body"><form method="get" id="orlen-filter-form" class="row g-3 align-items-end ajax-nav-form"><div class="col-lg-6"><label class="form-label d-block">Paliwa na wykresie</label><div class="d-flex flex-wrap gap-3">{% for f in fuel_types %}<label class="form-check"><input class="form-check-input" type="checkbox" name="fuel" value="{{f}}" {% if f in selected_fuels %}checked{% endif %}><span class="form-check-label">{{f}}</span></label>{% endfor %}</div></div><div class="col-lg-6"><label class="form-label d-block">Województwa LPG</label><button type="button" class="btn btn-secondary" data-bs-toggle="modal" data-bs-target="#lpgRegionsModal">Wybierz województwa</button> <span id="lpg-region-count" class="small text-body-secondary">{% if selected_regions %}{{selected_regions|length}} wybrano{% else %}Domyślne województwo{% endif %}</span></div><div class="col-md-2"><label class="form-label">Rok</label><input class="form-control" type="number" min="2020" max="2100" name="year" value="{{selected_year}}"></div><div class="col-md-2"><div class="form-check mb-2"><input class="form-check-input" type="checkbox" name="gross" value="1" id="gross-price" {% if include_vat %}checked{% endif %}><label class="form-check-label" for="gross-price">Dolicz VAT</label></div></div><div class="col-md-2"><button class="btn btn-secondary w-100">Pokaż</button></div></form>{% if current_user.role in ['boss','admin'] %}<div class="d-flex align-items-center gap-3 mt-3"><button type="button" id="orlen-sync-btn" class="btn btn-primary">Pobierz z API</button><span id="orlen-sync-status" class="small text-body-secondary" aria-live="polite"></span></div>{% endif %}</div></div> {% block content %}
<div class="page-header">
<div><div class="page-kicker">Analiza cen</div><h1 class="h2 mb-1">Dane cenowe Orlen</h1><p class="text-body-secondary mb-0">Przeglądaj historię i porównuj ceny tego samego paliwa między okresami.</p></div>
</div>
<ul class="nav nav-tabs mb-4" aria-label="Tryb analizy cen">
<li class="nav-item"><a class="nav-link {% if mode == 'history' %}active{% endif %}" href="{{ url_for('main.orlen_data', mode='history', year=selected_year) }}">Przegląd danych</a></li>
<li class="nav-item"><a class="nav-link {% if mode == 'compare' %}active{% endif %}" href="{{ url_for('main.orlen_data', mode='compare', year=selected_year, compare_year=compare_year, compare_fuel=compare_fuel) }}">Porównanie okresów</a></li>
</ul>
{% if mode == 'history' %}
<div class="card mb-4"><div class="card-body">
<form method="get" id="orlen-filter-form" class="row g-3 align-items-end ajax-nav-form">
<input type="hidden" name="mode" value="history">
<div class="col-lg-6"><label class="form-label d-block">Paliwa na wykresie</label><div class="d-flex flex-wrap gap-3">{% for f in fuel_types %}<label class="form-check"><input class="form-check-input" type="checkbox" name="fuel" value="{{f}}" {% if f in selected_fuels %}checked{% endif %}><span class="form-check-label">{{f}}</span></label>{% endfor %}</div></div>
<div class="col-lg-6"><label class="form-label d-block">Województwa LPG</label><button type="button" class="btn btn-secondary" data-bs-toggle="modal" data-bs-target="#lpgRegionsModal">Wybierz województwa</button> <span id="lpg-region-count" class="small text-body-secondary">{% if selected_regions %}{{selected_regions|length}} wybrano{% else %}Domyślne województwo{% endif %}</span></div>
<div class="col-md-3"><label class="form-label">Rok</label><select class="form-select" name="year">{% for year in available_years %}<option value="{{year}}" {% if year == selected_year %}selected{% endif %}>{{year}}</option>{% endfor %}</select></div>
<div class="col-md-3"><div class="form-check mb-2"><input class="form-check-input" type="checkbox" name="gross" value="1" id="gross-price" {% if include_vat %}checked{% endif %}><label class="form-check-label" for="gross-price">Dolicz VAT</label></div></div>
<div class="col-md-3"><button class="btn btn-secondary w-100">Pokaż dane</button></div>
</form>
{% if current_user.role in ['boss','admin'] %}<div class="d-flex align-items-center gap-3 mt-3"><button type="button" id="orlen-sync-btn" class="btn btn-primary">Pobierz z API</button><span id="orlen-sync-status" class="small text-body-secondary" aria-live="polite"></span></div>{% endif %}
</div></div>
<div class="modal fade lpg-region-modal" id="lpgRegionsModal" tabindex="-1" aria-hidden="true"><div class="modal-dialog modal-lg modal-dialog-centered"><div class="modal-content"><div class="modal-header"><h2 class="modal-title fs-5">Województwa LPG</h2><button type="button" class="btn-close" data-bs-dismiss="modal"></button></div><div class="modal-body"><label class="form-label" for="lpg-region-select">Wybierz jedno lub wiele województw</label><select class="form-select" name="region" id="lpg-region-select" form="orlen-filter-form" multiple>{% for r in regions %}<option value="{{r}}" {% if r in selected_regions %}selected{% endif %}>{{r}}</option>{% endfor %}</select></div><div class="modal-footer"><button type="button" class="btn btn-primary" data-bs-dismiss="modal">Gotowe</button></div></div></div></div> <div class="modal fade lpg-region-modal" id="lpgRegionsModal" tabindex="-1" aria-hidden="true"><div class="modal-dialog modal-lg modal-dialog-centered"><div class="modal-content"><div class="modal-header"><h2 class="modal-title fs-5">Województwa LPG</h2><button type="button" class="btn-close" data-bs-dismiss="modal"></button></div><div class="modal-body"><label class="form-label" for="lpg-region-select">Wybierz jedno lub wiele województw</label><select class="form-select" name="region" id="lpg-region-select" form="orlen-filter-form" multiple>{% for r in regions %}<option value="{{r}}" {% if r in selected_regions %}selected{% endif %}>{{r}}</option>{% endfor %}</select></div><div class="modal-footer"><button type="button" class="btn btn-primary" data-bs-dismiss="modal">Gotowe</button></div></div></div></div>
<div class="card mb-4"><div class="card-body"><h2 class="h5">Trend ceny {% if include_vat %}brutto{% else %}netto{% endif %} za litr</h2><div class="chart-wrap"><canvas id="orlen-chart"></canvas><div id="orlen-chart-empty" class="chart-empty d-none">Brak danych dla wybranych filtrów.</div></div></div></div> {% else %}
<div class="card mb-4"><div class="card-body">
<form method="get" id="orlen-filter-form" class="row g-3 align-items-end ajax-nav-form">
<input type="hidden" name="mode" value="compare">
<div class="col-md-4"><label class="form-label">Paliwo</label><select class="form-select" name="compare_fuel" id="compare-fuel">{% for f in fuel_types %}<option value="{{f}}" {% if f == compare_fuel %}selected{% endif %}>{{f}}</option>{% endfor %}</select><div class="form-text">Porównanie zawsze dotyczy jednego rodzaju paliwa.</div></div>
<div class="col-md-4 {% if compare_fuel != 'LPG' %}d-none{% endif %}" id="compare-region-wrap"><label class="form-label">Województwo LPG</label><select class="form-select" name="compare_region">{% for r in regions %}<option value="{{r}}" {% if r == compare_region %}selected{% endif %}>{{r}}</option>{% endfor %}</select></div>
<div class="col-md-4"><label class="form-label">Rodzaj porównania</label><select class="form-select" name="comparison_type" id="comparison-type"><option value="years" {% if comparison_type == 'years' %}selected{% endif %}>Rok do roku</option><option value="periods" {% if comparison_type == 'periods' %}selected{% endif %}>Dowolne okresy</option></select></div>
<div class="col-12"><div id="year-comparison-fields" class="row g-3 {% if comparison_type != 'years' %}d-none{% endif %}"><div class="col-md-4"><label class="form-label">Rok A</label><select class="form-select" name="year">{% for year in available_years %}<option value="{{year}}" {% if year == selected_year %}selected{% endif %}>{{year}}</option>{% endfor %}</select></div><div class="col-md-4"><label class="form-label">Rok B</label><select class="form-select" name="compare_year">{% for year in available_years %}<option value="{{year}}" {% if year == compare_year %}selected{% endif %}>{{year}}</option>{% endfor %}</select></div></div></div>
<div class="col-12"><div id="period-comparison-fields" class="row g-3 {% if comparison_type != 'periods' %}d-none{% endif %}"><div class="col-md-3"><label class="form-label">Okres A od</label><input class="form-control" type="date" name="period_a_from" value="{{period_a_from.isoformat() if period_a_from else ''}}"></div><div class="col-md-3"><label class="form-label">Okres A do</label><input class="form-control" type="date" name="period_a_to" value="{{period_a_to.isoformat() if period_a_to else ''}}"></div><div class="col-md-3"><label class="form-label">Okres B od</label><input class="form-control" type="date" name="period_b_from" value="{{period_b_from.isoformat() if period_b_from else ''}}"></div><div class="col-md-3"><label class="form-label">Okres B do</label><input class="form-control" type="date" name="period_b_to" value="{{period_b_to.isoformat() if period_b_to else ''}}"></div></div></div>
<div class="col-md-3"><div class="form-check mb-2"><input class="form-check-input" type="checkbox" name="gross" value="1" id="gross-price" {% if include_vat %}checked{% endif %}><label class="form-check-label" for="gross-price">Dolicz VAT</label></div></div>
<div class="col-md-3"><button class="btn btn-primary w-100">Porównaj</button></div>
</form>
</div></div>
{% if comparison_summary %}<div class="row g-3 mb-4">{% for item in comparison_summary %}<div class="col-md-6"><div class="card metric h-100"><div class="card-body"><small>{{item.label}}</small><strong>{% if item.average is not none %}{{'%.4f'|format(item.average)}} zł/l{% else %}Brak danych{% endif %}</strong>{% if item.count %}<div class="small text-body-secondary mt-2">Min. {{'%.4f'|format(item.minimum)}} · Maks. {{'%.4f'|format(item.maximum)}} · {{item.count}} pomiarów</div>{% endif %}</div></div></div>{% endfor %}</div>{% endif %}
{% if summary_difference %}<div class="alert {% if summary_difference.value > 0 %}alert-warning{% elif summary_difference.value < 0 %}alert-success{% else %}alert-secondary{% endif %} mb-4">Średnia w pierwszym okresie jest <strong>{% if summary_difference.value > 0 %}wyższa{% elif summary_difference.value < 0 %}niższa{% else %}taka sama{% endif %}</strong> o {{'%.4f'|format(summary_difference.value|abs)}} zł/l{% if summary_difference.percent is not none %} ({{'%.2f'|format(summary_difference.percent|abs)}}%){% endif %}.</div>{% endif %}
{% endif %}
<div class="card mb-4"><div class="card-body"><div class="section-heading"><div><h2 class="h5 mb-1">{% if mode == 'compare' %}Porównanie: {{compare_fuel}}{% else %}Trend ceny{% endif %} {% if include_vat %}brutto{% else %}netto{% endif %}</h2><p class="small text-body-secondary mb-0">Cena za litr w złotych.</p></div></div><div class="chart-wrap"><canvas id="orlen-chart"></canvas><div id="orlen-chart-empty" class="chart-empty d-none">Brak danych dla wybranych filtrów.</div></div></div></div>
<div class="card"><div class="card-body"><div class="table-responsive"><table class="table table-hover align-middle"><thead><tr><th>Data</th><th>Paliwo</th><th>Cena netto zł/l</th><th>Region</th><th>Pobrano</th></tr></thead><tbody>{% for r in rows|reverse %}<tr><td>{{r.effective_date}}</td><td>{{r.product_name}}</td><td>{{'%.4f'|format(r.price_per_liter|float)}}</td><td>{{r.region or '—'}}</td><td>{{r.fetched_at.strftime('%Y-%m-%d %H:%M')}}</td></tr>{% else %}<tr><td colspan="5" class="text-center text-body-secondary py-4">Brak danych w bazie.</td></tr>{% endfor %}</tbody></table></div></div></div> <div class="card"><div class="card-body"><div class="table-responsive"><table class="table table-hover align-middle"><thead><tr><th>Data</th><th>Paliwo</th><th>Cena netto zł/l</th><th>Region</th><th>Pobrano</th></tr></thead><tbody>{% for r in rows|reverse %}<tr><td>{{r.effective_date}}</td><td>{{r.product_name}}</td><td>{{'%.4f'|format(r.price_per_liter|float)}}</td><td>{{r.region or '—'}}</td><td>{{r.fetched_at.strftime('%Y-%m-%d %H:%M')}}</td></tr>{% else %}<tr><td colspan="5" class="text-center text-body-secondary py-4">Brak danych w bazie.</td></tr>{% endfor %}</tbody></table></div></div></div>
{% endblock %}{% block scripts %}<script>FuelTrack.renderMultiLineChart('orlen-chart', {{chart|tojson}}, {{include_vat|tojson}});FuelTrack.bindOrlenSync();</script>{% endblock %} {% endblock %}
{% block scripts %}<script>FuelTrack.renderMultiLineChart('orlen-chart', {{chart|tojson}}, {{include_vat|tojson}});FuelTrack.bindOrlenSync();FuelTrack.bindOrlenComparison();</script>{% endblock %}
+33
View File
@@ -316,3 +316,36 @@ def test_cannot_delete_assigned_fuel_card(tmp_path):
assert response.status_code == 409 assert response.status_code == 409
assert response.json['ok'] is False assert response.json['ok'] is False
assert 'użytkowników' in response.json['message'] assert 'użytkowników' in response.json['message']
def test_orlen_page_has_history_and_comparison_modes(tmp_path):
app = make_app(tmp_path, "orlen-ui.db")
client = app.test_client()
client.post('/login', data={'email':'admin@example.com','password':'admin123!'})
history = client.get('/orlen?mode=history').get_data(as_text=True)
assert 'Przegląd danych' in history
assert 'Porównanie okresów' in history
comparison = client.get('/orlen?mode=compare&compare_fuel=PB98&comparison_type=years&year=2026&compare_year=2025').get_data(as_text=True)
assert 'Porównanie zawsze dotyczy jednego rodzaju paliwa.' in comparison
assert 'Porównanie: PB98' in comparison
assert 'Dowolne okresy' in comparison
def test_orlen_comparison_filters_to_one_fuel(tmp_path):
from datetime import date
from decimal import Decimal
from app.extensions import db
from app.models import OrlenPrice
app = make_app(tmp_path, "orlen-compare.db")
client = app.test_client()
client.post('/login', data={'email':'admin@example.com','password':'admin123!'})
with app.app_context():
db.session.add_all([
OrlenPrice(fuel_type='PB95', effective_date=date(2025,1,2), price_per_liter=Decimal('4.1000'), raw_value=Decimal('4100'), region='', product_name='PB95', source='test'),
OrlenPrice(fuel_type='PB95', effective_date=date(2026,1,2), price_per_liter=Decimal('4.5000'), raw_value=Decimal('4500'), region='', product_name='PB95', source='test'),
OrlenPrice(fuel_type='DIESEL', effective_date=date(2026,1,2), price_per_liter=Decimal('5.0000'), raw_value=Decimal('5000'), region='', product_name='DIESEL', source='test'),
])
db.session.commit()
html = client.get('/orlen?mode=compare&compare_fuel=PB95&comparison_type=years&year=2026&compare_year=2025').get_data(as_text=True)
assert '4.5000' in html
assert '4.1000' in html
assert '>DIESEL</td>' not in html