Models ====== Abstract base classes --------------------- Three abstract Django models in ``richy.core.models`` are inherited across the apps. They are ``abstract = True`` so they don't produce their own database table -- subclasses do. .. list-table:: :header-rows: 1 :widths: 22 38 40 * - Base - Subclasses - Adds * - :class:`~richy.core.models.UserRelatedModel` - ``UserItem``, :class:`richy.staking.models.Staking`, :class:`richy.transactions.models.Transaction` - ``user`` FK + :class:`~richy.core.models.UserRelatedManager` * - :class:`~richy.core.models.BaseItem` - :class:`~richy.core.models.Item` (concrete parent of ``Coin``, ``Etf``, ``Index``, ``Share``), :class:`~richy.core.models.ItemWithPrices` - ``symbol``, ``type``, ``is_discontinued`` + :class:`~richy.core.models.ItemManager` * - :class:`~richy.core.models.BaseDividend` - ``richy.shares.models.Dividend``, ``richy.etfs.models.Dividend`` - ``ex_date``, ``payment_date``, ``record_date``, ``yield_value``, ``amount_value`` UserRelatedModel ~~~~~~~~~~~~~~~~ Adds a ``user`` foreign key to ``settings.AUTH_USER_MODEL`` and wires in :class:`~richy.core.models.UserRelatedManager` on ``objects``. Inherited by every per-user model in the project. .. autoclass:: richy.core.models.UserRelatedModel :show-inheritance: BaseItem ~~~~~~~~ Common fields shared by every asset type: ``symbol``, ``type`` (``share`` / ``etf`` / ``index`` / ``coin``), ``is_discontinued``, plus :class:`~richy.core.models.ItemManager` on ``objects``. Inherited by :class:`~richy.core.models.Item` (which itself is the concrete parent of ``Share``, ``Etf``, ``Index``, ``Coin``) and :class:`~richy.core.models.ItemWithPrices`. .. autoclass:: richy.core.models.BaseItem :show-inheritance: BaseDividend ~~~~~~~~~~~~ Common fields for dividend records: ``ex_date``, ``payment_date``, ``record_date``, ``yield_value``, ``amount_value``. Inherited by ``richy.shares.models.Dividend`` and ``richy.etfs.models.Dividend``. See :doc:`/modules/shares/models` (or :doc:`/modules/etfs/models`) for the user-facing semantics of each field. .. autoclass:: richy.core.models.BaseDividend :show-inheritance: User ---- The application uses a custom user model (``AUTH_USER_MODEL = "core.User"``) based on ``AbstractBaseUser`` and ``PermissionsMixin``. The login identifier is ``email`` -- there is no separate ``username`` field. .. autoclass:: richy.core.models.User :show-inheritance: Convenience accessors used across the project: .. automethod:: richy.core.models.User.get_full_name .. automethod:: richy.core.models.User.get_short_name .. automethod:: richy.core.models.User.get_items .. automethod:: richy.core.models.User.get_owned_items .. automethod:: richy.core.models.User.get_traded_currencies Meta ---- Application-wide key/value metadata. Each row stores a typed key (``type``, currently only ``LAST_PRICE_UPDATE``) and a Python value serialized via ``PickledObjectField``. The ``LAST_PRICE_UPDATE`` entry holds the last datetime when share / coin prices were refreshed -- it's surfaced as the tooltip on the application name in the top bar. .. autoclass:: richy.core.models.Meta :show-inheritance: NavigationTreeItem ------------------ Custom site-tree item used by `django-sitetree `_ for the application's main navigation. The only difference from the upstream ``TreeItemBase`` is an extra ``icon`` field consumed by the menu template tags. The setting ``SITETREE_MODEL_TREE_ITEM = "core.NavigationTreeItem"`` in :doc:`/architecture/settings` plugs this model into sitetree. .. autoclass:: richy.core.models.NavigationTreeItem :show-inheritance: Item ---- ``Item`` is concrete (not abstract) and serves as the polymorphic parent of every asset-type model. Each concrete asset type lives in its own Django app and inherits from ``Item`` via Django's multi-table inheritance -- so each subclass has its own table with an implicit one-to-one back to ``core_item``. .. code-block:: text BaseItem (abstract) ├── Item (concrete; multi-table parent) │ ├── richy.coins.models.Coin │ ├── richy.etfs.models.Etf │ ├── richy.indexes.models.Index │ └── richy.shares.models.Share └── ItemWithPrices (concrete; denormalized price cache) Percentage change ~~~~~~~~~~~~~~~~~ Percentage change from day 0 to day 1 -- and also from day 0 to day 5 -- can be calculated with ``get_last_days_change(percents=True)``. The method seeks for the last known price and a price that suits the "days ago" criterion. If a price for "days ago" is missing (e.g. because that date was a weekend), the method falls back to the nearest prior price. The result is :doc:`cached `. Compounded change ^^^^^^^^^^^^^^^^^ The same method can also calculate the compounded change based on all open deposit transactions. The calculation method is a `weighted average `_. .. automethod:: richy.core.models.Item.get_last_days_change ItemData ~~~~~~~~ A model where additional Item-related data is stored. Data is stored as a JSON object under specific keys: - ``basic_info`` -- basic info for all items (displayed on the item detail page) - ``holdings`` -- list of ETF holdings (ETFs only) .. warning:: ``ItemData`` should never be accessed directly. Use the accessors on ``Item`` and its subclasses, e.g.: - ``Item.set_basic_info()`` - ``Item.get_basic_info()`` ItemWithPrices -------------- A read-only model backed by a **PostgreSQL materialized view** (``core_itemwithprices``). It denormalizes the most recent price plus historical price snapshots (1 day / 5 / 7 / 30 / 90 / 180 / 365 days ago, plus YTD) and the corresponding percentage changes onto a single row per item -- so list views can render rich tables without joining ``Price`` for each row. Because it's a materialized view, ``ItemWithPrices`` is read-only (``Meta.managed = False``) and must be **refreshed** whenever underlying prices change: .. automethod:: richy.core.models.ItemWithPrices.refresh For batch loading the view rows alongside ``UserItem`` instances: .. automethod:: richy.core.models.ItemWithPrices.fetch_into_user_items .. autoclass:: richy.core.models.ItemWithPrices :show-inheritance: UserItem -------- A per-user view of an :class:`~richy.core.models.Item`. Inherits from :class:`~richy.core.models.UserRelatedModel` and adds display preferences (``show_in_overview``, ``show_in_news``, ``show_on_dashboard``), an archive flag (``is_archived``), a free-form ``note``, and a many-to-many to ``indexes.Index`` so users can group items into custom indexes. Uniqueness is enforced on ``(user, item)`` -- a user has at most one ``UserItem`` row per ``Item``. .. autoclass:: richy.core.models.UserItem :show-inheritance: Price ----- A single historical price record for an :class:`~richy.core.models.Item`. Each row stores ``(item, datetime, price)`` and is uniquely keyed on ``(item, datetime)``. The model uses :class:`~richy.core.models.PandasManager` so query sets can be loaded directly into a pandas dataframe via ``.to_pandas(...)``. .. autoclass:: richy.core.models.Price :show-inheritance: Asset ----- Assets are generated or downloaded artefacts saved for further use. The ``core.Asset`` model handles the data; each row can be related to an ``Item`` or a ``Transaction``. Asset types: * ``EARNINGS_CHART`` -- earnings chart downloaded from Yahoo! * ``FINANCIALS_CHART`` -- financial chart downloaded from Yahoo! * ``TRENDS_CHART`` -- trends chart downloaded from Yahoo! * ``PERFORMANCE_CHART`` -- performance chart, generated * ``RECOMMENDATIONS_CHART`` -- recommendations chart, generated * ``TRENDS_TWITTER_CHART`` -- Twitter trends chart * ``TRANSACTION_GRAPH`` -- transaction (dot) graph, generated Manual uploading ~~~~~~~~~~~~~~~~ The ``Asset`` model has a convenient static method ``upload()`` for new asset creation: .. automethod:: richy.core.models.Asset.upload Managers and querysets ---------------------- Custom Django ORM managers and querysets. Each pair adds project-wide behaviour on top of the standard manager: dataframe export, user scoping, item filtering, and -- for ``ItemWithPrices`` -- a small identifier annotation used by batch-loading code. The :class:`~richy.staking.models.StakingManager` / :class:`~richy.staking.models.StakingQuerySet` pair follows the same pattern and is documented in :doc:`/modules/staking/models`. PandasManager / PandasQuerySet ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Adds a ``.to_pandas(*fields)`` method that returns the queryset as a ``pandas.DataFrame``. Used wherever the application reads data into pandas for further analysis (charts, performance calculations, historical pricing). .. autoclass:: richy.core.models.PandasQuerySet :show-inheritance: .. autoclass:: richy.core.models.PandasManager :show-inheritance: UserRelatedManager / UserRelatedQuerySet ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Adds ``.by_user(user)`` for filtering rows owned by a specific user. Wired in on every model inheriting from :class:`~richy.core.models.UserRelatedModel`. .. autoclass:: richy.core.models.UserRelatedQuerySet :show-inheritance: .. autoclass:: richy.core.models.UserRelatedManager :show-inheritance: ItemManager / ItemQuerySet ~~~~~~~~~~~~~~~~~~~~~~~~~~ Adds ``.by_user(user)`` for items the user owns (joins through ``UserItem``). Wired in on :class:`~richy.core.models.BaseItem` so all four asset-type models inherit it. .. autoclass:: richy.core.models.ItemQuerySet :show-inheritance: .. autoclass:: richy.core.models.ItemManager :show-inheritance: ItemWithPricesQuerySet ~~~~~~~~~~~~~~~~~~~~~~ Adds ``.with_identifier()``, an annotation that produces a synthetic ``identifier = "_"`` column. Used by :meth:`~richy.core.models.ItemWithPrices.fetch_into_user_items` to join the materialized-view rows against a list of ``UserItem`` keys in a single query. .. autoclass:: richy.core.models.ItemWithPricesQuerySet :show-inheritance: .. note:: The corresponding manager (``ItemWithPricesManager``) is built at runtime via ``models.Manager.from_queryset(ItemWithPricesQuerySet)`` -- there is no standalone class to autodoc. CustomUserManager ~~~~~~~~~~~~~~~~~ Custom manager for :class:`~richy.core.models.User`. Provides ``create_user()`` and ``create_superuser()`` matching the ``email``-as-username convention used by the custom user model. .. autoclass:: richy.core.models.CustomUserManager :show-inheritance: Utilities --------- Non-model helpers that live in ``richy.core.models`` for historical reasons. Listed here so the page covers everything in the file, even though they aren't models themselves. JSON encoder ~~~~~~~~~~~~ Project-wide JSON encoder used by views that hand-roll JSON responses. Extends ``DjangoJSONEncoder`` with extra type handling. .. autoclass:: richy.core.models.RichyJSONEncoder :show-inheritance: Highcharts helpers ~~~~~~~~~~~~~~~~~~ Two small functions used when serializing data for Highcharts. .. autofunction:: richy.core.models.df_to_highcharts .. autofunction:: richy.core.models.date_to_highcharts_timestamp