1818
1919from polar .enums import SubscriptionRecurringInterval
2020from polar .kit .time_queries import TimeInterval
21- from polar .models import Checkout , CustomerSeat , Subscription
21+ from polar .models import Checkout , CustomerSeat , Order , Subscription
2222from polar .models .checkout import CheckoutStatus
2323
2424from .queries import MetricQuery
@@ -450,7 +450,7 @@ def get_cumulative(cls, periods: Iterable["MetricsPeriod"]) -> float:
450450
451451class TotalSeatsMetric (SQLMetric ):
452452 slug = "seats_total"
453- display_name = "Total Seats "
453+ display_name = "Seat Count "
454454 type = MetricType .scalar
455455 query = MetricQuery .seats
456456
@@ -513,6 +513,97 @@ def get_cumulative(cls, periods: Iterable["MetricsPeriod"]) -> int | float:
513513 return cumulative_last (periods , cls .slug )
514514
515515
516+ class SeatCustomersMetric (SQLMetric ):
517+ slug = "seat_customers"
518+ display_name = "Active Customers"
519+ type = MetricType .scalar
520+ query = MetricQuery .seats
521+
522+ @classmethod
523+ def get_sql_expression (
524+ cls , t : ColumnElement [datetime ], i : TimeInterval , now : datetime
525+ ) -> ColumnElement [int ]:
526+ return func .count (
527+ func .distinct (
528+ func .coalesce (
529+ CustomerSeat .customer_id ,
530+ Subscription .customer_id ,
531+ Order .customer_id ,
532+ )
533+ )
534+ )
535+
536+ @classmethod
537+ def get_cumulative (cls , periods : Iterable ["MetricsPeriod" ]) -> int | float :
538+ return cumulative_last (periods , cls .slug )
539+
540+
541+ class NewSeatCustomersMetric (SQLMetric ):
542+ slug = "new_seat_customers"
543+ display_name = "New Customers"
544+ type = MetricType .scalar
545+ query = MetricQuery .seats
546+
547+ @classmethod
548+ def get_sql_expression (
549+ cls , t : ColumnElement [datetime ], i : TimeInterval , now : datetime
550+ ) -> ColumnElement [int ]:
551+ raise NotImplementedError ("Computed directly in get_seats_cte" )
552+
553+ @classmethod
554+ def get_cumulative (cls , periods : Iterable ["MetricsPeriod" ]) -> int | float :
555+ return cumulative_sum (periods , cls .slug )
556+
557+
558+ class ChurnedSeatCustomersMetric (SQLMetric ):
559+ slug = "churned_seat_customers"
560+ display_name = "Churned Customers"
561+ type = MetricType .scalar
562+ query = MetricQuery .seats
563+
564+ @classmethod
565+ def get_sql_expression (
566+ cls , t : ColumnElement [datetime ], i : TimeInterval , now : datetime
567+ ) -> ColumnElement [int ]:
568+ raise NotImplementedError ("Computed directly in get_seats_cte" )
569+
570+ @classmethod
571+ def get_cumulative (cls , periods : Iterable ["MetricsPeriod" ]) -> int | float :
572+ return cumulative_sum (periods , cls .slug )
573+
574+
575+ class AverageSeatsPerCustomerMetric (MetaMetric ):
576+ slug = "average_seats_per_customer"
577+ display_name = "Average Seats per Customer"
578+ type = MetricType .scalar
579+
580+ @classmethod
581+ def compute_from_period (cls , period : "MetricsPeriod" ) -> float :
582+ total = period .seats_total or 0
583+ customers = period .seat_customers or 0
584+ return total / customers if customers > 0 else 0.0
585+
586+ @classmethod
587+ def get_cumulative (cls , periods : Iterable ["MetricsPeriod" ]) -> float :
588+ return float (cumulative_last (periods , cls .slug ))
589+
590+
591+ class SeatUtilizationRateMetric (MetaMetric ):
592+ slug = "seat_utilization_rate"
593+ display_name = "Seat Utilization Rate"
594+ type = MetricType .percentage
595+
596+ @classmethod
597+ def compute_from_period (cls , period : "MetricsPeriod" ) -> float :
598+ claimed = period .seats_claimed or 0
599+ total = period .seats_total or 0
600+ return claimed / total if total > 0 else 0.0
601+
602+ @classmethod
603+ def get_cumulative (cls , periods : Iterable ["MetricsPeriod" ]) -> float :
604+ return float (cumulative_last (periods , cls .slug ))
605+
606+
516607class LTVMetric (MetaMetric ):
517608 slug = "ltv"
518609 display_name = "Lifetime Value"
@@ -913,6 +1004,9 @@ def get_cumulative(cls, periods: Iterable["MetricsPeriod"]) -> int | float:
9131004 TotalSeatsMetric ,
9141005 ClaimedSeatsMetric ,
9151006 PendingSeatsMetric ,
1007+ SeatCustomersMetric ,
1008+ NewSeatCustomersMetric ,
1009+ ChurnedSeatCustomersMetric ,
9161010]
9171011
9181012METRICS_POST_COMPUTE : list [type [MetaMetric ]] = [
@@ -921,6 +1015,8 @@ def get_cumulative(cls, periods: Iterable["MetricsPeriod"]) -> int | float:
9211015 GrossMarginMetric ,
9221016 GrossMarginPercentageMetric ,
9231017 CashflowMetric ,
1018+ AverageSeatsPerCustomerMetric ,
1019+ SeatUtilizationRateMetric ,
9241020]
9251021
9261022METRICS : list [type [Metric ]] = [
0 commit comments