mirror of
https://gitlab.com/tildes/tildes.git
synced 2026-04-16 06:18:34 +02:00
Show donation meter on financials page
This commit is contained in:
@@ -5,8 +5,9 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
max-width: $paragraph-max-width;
|
||||
|
||||
padding: 0.4rem 0.4rem 0;
|
||||
padding: 0.4rem;
|
||||
margin: 1rem 0;
|
||||
border: 1px solid;
|
||||
border-color: inherit;
|
||||
|
||||
@@ -2,10 +2,6 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
#sidebar {
|
||||
p {
|
||||
margin-bottom: 0.4rem;
|
||||
}
|
||||
|
||||
.btn {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
{% extends 'base_no_sidebar.jinja2' %}
|
||||
|
||||
{% from "macros/utils.jinja2" import format_money %}
|
||||
{% from "macros/donation_goal.jinja2" import donation_goal %}
|
||||
|
||||
{% block title %}Tildes financials{% endblock %}
|
||||
|
||||
@@ -12,6 +13,7 @@
|
||||
{% block main_heading %}Tildes financials{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<p>This page is a view into Tildes's financials: operating expenses, income from the various donation methods, and the overall goal for monthly donations. Currently, it only contains data for {{ current_time.strftime("%B %Y") }}, but more historical data will be available eventually.</p>
|
||||
|
||||
<p>Amounts on this page are in USD unless otherwise noted. Even though Tildes is a Canadian non-profit, many of its costs and donations are in USD. People from other parts of the world are also generally most familiar with the relative value of USD, so using it makes this info more understandable to everyone.</p>
|
||||
@@ -22,6 +24,10 @@
|
||||
|
||||
<p><strong>The current donation goal is {{ format_money(entries["goal"]|sum(attribute="amount")) }} per month.</strong></p>
|
||||
|
||||
{% if financial_data %}
|
||||
{{ donation_goal(financial_data, current_time) }}
|
||||
{% endif %}
|
||||
|
||||
<p>The actual <em>costs</em> solely to keep Tildes running are much lower than this (see table below), but this represents the amount that I believe will make Tildes truly independently sustainable. It will cover all of the operating costs and also allow me (<a href="/user/Deimos">Deimos</a>) to pay myself a somewhat respectable (but low) salary of about $35,000/year. This goal may not be achievable in the near term, but it is the point where I will be comfortable focusing on Tildes without still needing to find additional outside income.</p>
|
||||
|
||||
<p><a href="https://docs.tildes.net/donate">Please donate—any amount will help get us closer to the goal!</a></p>
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
{% extends 'topic_listing.jinja2' %}
|
||||
|
||||
{% from 'macros/forms.jinja2' import search_form %}
|
||||
{% from 'macros/donation_goal.jinja2' import donation_goal %}
|
||||
|
||||
{% block title_full %}Tildes{% endblock %}
|
||||
|
||||
@@ -109,35 +110,3 @@
|
||||
</ul>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% macro donation_goal(financial_data, current_time) %}
|
||||
<div class="donation-goal">
|
||||
<header>Tildes's progress to sustainability</header>
|
||||
|
||||
<div class="donation-goal-progress">
|
||||
<meter
|
||||
{% if financial_data["goal_percentage"] <= 100 %}
|
||||
class="donation-goal-meter"
|
||||
max="{{ financial_data["goal"] }}"
|
||||
low="{{ financial_data["expense"] }}"
|
||||
high="{{ financial_data["goal"] // 4 * 3 }}" {# 3/4 of the goal #}
|
||||
optimum="{{ financial_data["goal"] - 1 }}" {# just needs to be between high and max #}
|
||||
value="{{ financial_data["income"] }}"
|
||||
{% else %}
|
||||
{# Above 100%, this creates an effect of "extra" bar being added to the end #}
|
||||
class="donation-goal-meter donation-goal-meter-over-goal"
|
||||
max="{{ financial_data["income"] }}"
|
||||
value="{{ financial_data["goal"] }}"
|
||||
{% endif %}
|
||||
title="${{ financial_data["income"] }} of ${{ financial_data["goal"] }} goal (USD)"
|
||||
></meter>
|
||||
<span class="donation-goal-percentage">{{ financial_data["goal_percentage"] }}%</span>
|
||||
</div>
|
||||
|
||||
<p class="text-secondary">{{ current_time.strftime("%B %Y") }} donations</p>
|
||||
|
||||
<p>Tildes is a non-profit site with no ads or investors, funded entirely by donations.</p>
|
||||
|
||||
<p><a href="https://docs.tildes.net/donate">Please donate</a> to support its continued development! (<a href="/financials">more details</a>)</p>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
34
tildes/tildes/templates/macros/donation_goal.jinja2
Normal file
34
tildes/tildes/templates/macros/donation_goal.jinja2
Normal file
@@ -0,0 +1,34 @@
|
||||
{# Copyright (c) 2020 Tildes contributors <code@tildes.net> #}
|
||||
{# SPDX-License-Identifier: AGPL-3.0-or-later #}
|
||||
|
||||
{% macro donation_goal(financial_data, current_time) %}
|
||||
<div class="donation-goal">
|
||||
<header>Tildes's progress to sustainability</header>
|
||||
|
||||
<div class="donation-goal-progress">
|
||||
<meter
|
||||
{% if financial_data["goal_percentage"] <= 100 %}
|
||||
class="donation-goal-meter"
|
||||
max="{{ financial_data["goal"] }}"
|
||||
low="{{ financial_data["expense"] }}"
|
||||
high="{{ financial_data["goal"] // 4 * 3 }}" {# 3/4 of the goal #}
|
||||
optimum="{{ financial_data["goal"] - 1 }}" {# just needs to be between high and max #}
|
||||
value="{{ financial_data["income"] }}"
|
||||
{% else %}
|
||||
{# Above 100%, this creates an effect of "extra" bar being added to the end #}
|
||||
class="donation-goal-meter donation-goal-meter-over-goal"
|
||||
max="{{ financial_data["income"] }}"
|
||||
value="{{ financial_data["goal"] }}"
|
||||
{% endif %}
|
||||
title="${{ financial_data["income"] }} of ${{ financial_data["goal"] }} goal (USD)"
|
||||
></meter>
|
||||
<span class="donation-goal-percentage">{{ financial_data["goal_percentage"] }}%</span>
|
||||
</div>
|
||||
|
||||
<p class="text-secondary">{{ current_time.strftime("%B %Y") }} donations</p>
|
||||
|
||||
<p>Tildes is a non-profit site with no ads or investors, funded entirely by donations.</p>
|
||||
|
||||
<p><a href="https://docs.tildes.net/donate">Please donate</a> to support its continued development! (<a href="/financials">more details</a>)</p>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
@@ -4,10 +4,13 @@
|
||||
"""The view for displaying entries in the financials table."""
|
||||
|
||||
from collections import defaultdict
|
||||
from typing import Dict, List
|
||||
from decimal import Decimal
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
from pyramid.request import Request
|
||||
from pyramid.view import view_config
|
||||
from sqlalchemy import func
|
||||
from sqlalchemy.orm.session import Session
|
||||
from sqlalchemy.sql.expression import text
|
||||
|
||||
from tildes.lib.datetime import utc_now
|
||||
@@ -31,4 +34,33 @@ def get_financials(request: Request) -> dict:
|
||||
for entry in financial_entries:
|
||||
entries[entry.entry_type.name.lower()].append(entry)
|
||||
|
||||
return {"entries": entries, "current_time": utc_now()}
|
||||
financial_data = get_financial_data(request.db_session)
|
||||
|
||||
return {
|
||||
"entries": entries,
|
||||
"current_time": utc_now(),
|
||||
"financial_data": financial_data,
|
||||
}
|
||||
|
||||
|
||||
def get_financial_data(db_session: Session) -> Optional[Dict[str, Decimal]]:
|
||||
"""Return financial data used to render the donation goal box."""
|
||||
# get the total sum for each entry type in the financials table relevant to today
|
||||
financial_totals = (
|
||||
db_session.query(Financials.entry_type, func.sum(Financials.amount))
|
||||
.filter(Financials.date_range.op("@>")(text("CURRENT_DATE")))
|
||||
.group_by(Financials.entry_type)
|
||||
.all()
|
||||
)
|
||||
|
||||
financial_data = {entry[0].name.lower(): entry[1] for entry in financial_totals}
|
||||
|
||||
# if any of the entry types were missing, the data won't be usable
|
||||
if any(key not in financial_data for key in ("expense", "goal", "income")):
|
||||
return None
|
||||
|
||||
financial_data["goal_percentage"] = round(
|
||||
financial_data["income"] / financial_data["goal"] * 100
|
||||
)
|
||||
|
||||
return financial_data
|
||||
|
||||
@@ -4,9 +4,8 @@
|
||||
"""Views related to posting/viewing topics and comments on them."""
|
||||
|
||||
from collections import namedtuple
|
||||
from decimal import Decimal
|
||||
from difflib import SequenceMatcher
|
||||
from typing import Any, Dict, Optional, Union
|
||||
from typing import Any, Optional, Union
|
||||
|
||||
from marshmallow import missing, ValidationError
|
||||
from marshmallow.fields import Boolean, String
|
||||
@@ -15,8 +14,7 @@ from pyramid.renderers import render_to_response
|
||||
from pyramid.request import Request
|
||||
from pyramid.response import Response
|
||||
from pyramid.view import view_config
|
||||
from sqlalchemy import cast, func, or_
|
||||
from sqlalchemy.orm.session import Session
|
||||
from sqlalchemy import cast, or_
|
||||
from sqlalchemy.sql.expression import any_, desc, text
|
||||
from sqlalchemy_utils import Ltree
|
||||
from webargs.pyramidparser import use_kwargs
|
||||
@@ -31,7 +29,6 @@ from tildes.enums import (
|
||||
from tildes.lib.database import TagList
|
||||
from tildes.lib.datetime import SimpleHoursPeriod, utc_now
|
||||
from tildes.models.comment import Comment, CommentNotification, CommentTree
|
||||
from tildes.models.financials import Financials
|
||||
from tildes.models.group import Group, GroupWikiPage
|
||||
from tildes.models.log import LogComment, LogTopic
|
||||
from tildes.models.topic import Topic, TopicSchedule, TopicVisit
|
||||
@@ -41,6 +38,7 @@ from tildes.schemas.fields import Enum, ShortTimePeriod
|
||||
from tildes.schemas.listing import TopicListingSchema
|
||||
from tildes.schemas.topic import TopicSchema
|
||||
from tildes.views.decorators import rate_limit_view
|
||||
from tildes.views.financials import get_financial_data
|
||||
|
||||
|
||||
DefaultSettings = namedtuple("DefaultSettings", ["order", "period"])
|
||||
@@ -307,7 +305,7 @@ def get_group_topics( # noqa
|
||||
most_recent_scheduled_topics = None
|
||||
|
||||
if is_home_page:
|
||||
financial_data = _get_financial_data(request.db_session)
|
||||
financial_data = get_financial_data(request.db_session)
|
||||
else:
|
||||
financial_data = None
|
||||
|
||||
@@ -560,26 +558,3 @@ def _get_default_settings(
|
||||
default_period = None
|
||||
|
||||
return DefaultSettings(order=default_order, period=default_period)
|
||||
|
||||
|
||||
def _get_financial_data(db_session: Session) -> Optional[Dict[str, Decimal]]:
|
||||
"""Return financial data used to render the donation goal box."""
|
||||
# get the total sum for each entry type in the financials table relevant to today
|
||||
financial_totals = (
|
||||
db_session.query(Financials.entry_type, func.sum(Financials.amount))
|
||||
.filter(Financials.date_range.op("@>")(text("CURRENT_DATE")))
|
||||
.group_by(Financials.entry_type)
|
||||
.all()
|
||||
)
|
||||
|
||||
financial_data = {entry[0].name.lower(): entry[1] for entry in financial_totals}
|
||||
|
||||
# if any of the entry types were missing, the data won't be usable
|
||||
if any(key not in financial_data for key in ("expense", "goal", "income")):
|
||||
return None
|
||||
|
||||
financial_data["goal_percentage"] = round(
|
||||
financial_data["income"] / financial_data["goal"] * 100
|
||||
)
|
||||
|
||||
return financial_data
|
||||
|
||||
Reference in New Issue
Block a user