Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 105 additions & 0 deletions hr_birth_astral_chart/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
.. image:: https://odoo-community.org/readme-banner-image
:target: https://odoo-community.org/get-involved?utm_source=readme
:alt: Odoo Community Association

=====================
HR Birth Astral Chart
=====================

..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:7cdfdfdcb177bcfdbc83acf2f37349036ea8006643bfc524ffc77d8ba08c5b8a
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fhr-lightgray.png?logo=github
:target: https://github.com/OCA/hr/tree/19.0/hr_birth_astral_chart
:alt: OCA/hr
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/hr-19-0/hr-19-0-hr_birth_astral_chart
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
:target: https://runboat.odoo-community.org/builds?repo=OCA/hr&target_branch=19.0
:alt: Try me on Runboat

|badge1| |badge2| |badge3| |badge4| |badge5|

View your full Western astrological birth chart directly from your
employee profile.

Depends on ``hr_birth_data`` for the birth time and coordinates fields.

Adds an **Astral Chart** tab to the employee form showing:

- **Sun Sign**, **Moon Sign** and **Ascendant** summary badges
- An SVG zodiac wheel with planetary positions
- A detailed table with degree, minute, sign and house for each planet
(Sun, Moon, Mercury, Venus, Mars, Jupiter, Saturn, Uranus, Neptune,
Pluto, Chiron, Black Moon Lilith, Ceres)
- Astrological houses (Whole Sign system) when birth time and location
are provided
- Current transits biwheel and interpretation

Astronomical calculations use ``pyswisseph`` (Python binding for Swiss
Ephemeris) for high-precision planetary positions.

**Table of contents**

.. contents::
:local:

Bug Tracker
===========

Bugs are tracked on `GitHub Issues <https://github.com/OCA/hr/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
`feedback <https://github.com/OCA/hr/issues/new?body=module:%20hr_birth_astral_chart%0Aversion:%2019.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.

Do not contact contributors directly about support or help with technical issues.

Credits
=======

Authors
-------

* ForgeFlow

Contributors
------------

- Miquel Raïch <miquel.raich@forgeflow.com>

Maintainers
-----------

This module is maintained by the OCA.

.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org

OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.

.. |maintainer-MiquelRForgeFlow| image:: https://github.com/MiquelRForgeFlow.png?size=40px
:target: https://github.com/MiquelRForgeFlow
:alt: MiquelRForgeFlow

Current `maintainer <https://odoo-community.org/page/maintainer-role>`__:

|maintainer-MiquelRForgeFlow|

This module is part of the `OCA/hr <https://github.com/OCA/hr/tree/19.0/hr_birth_astral_chart>`_ project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
4 changes: 4 additions & 0 deletions hr_birth_astral_chart/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Copyright 2026 Forgeflow S.L.
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).

from . import models
26 changes: 26 additions & 0 deletions hr_birth_astral_chart/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Copyright 2026 Forgeflow S.L.
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).

{
"name": "HR Birth Astral Chart",
"version": "19.0.1.0.0",
"category": "Human Resources",
"website": "https://github.com/OCA/hr",
"author": "ForgeFlow, Odoo Community Association (OCA)",
"maintainers": ["MiquelRForgeFlow"],
"license": "AGPL-3",
"installable": True,
"application": False,
"summary": "View your full astrological birth chart from your employee profile",
"external_dependencies": {"python": ["pyswisseph"]},
"depends": ["hr_birth_data"],
"data": [
"views/hr_employee_views.xml",
],
"assets": {
"web.assets_backend": [
"hr_birth_astral_chart/static/src/xml/birth_chart_table.xml",
"hr_birth_astral_chart/static/src/js/birth_chart_table.esm.js",
],
},
}
Binary file added hr_birth_astral_chart/ephe/seas_18.se1
Binary file not shown.
6 changes: 6 additions & 0 deletions hr_birth_astral_chart/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Copyright 2026 Forgeflow S.L.
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).

from . import astro_calc
from . import interpretations
from . import hr_employee
171 changes: 171 additions & 0 deletions hr_birth_astral_chart/models/astro_calc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
# Copyright 2026 Forgeflow S.L.
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
# pylint: disable=W8161

import os

import swisseph as swe

_EPHE_PATH = os.path.join(os.path.dirname(os.path.dirname(__file__)), "ephe")
swe.set_ephe_path(_EPHE_PATH)

SIGNS = [
"Aries",
"Taurus",
"Gemini",
"Cancer",
"Leo",
"Virgo",
"Libra",
"Scorpio",
"Sagittarius",
"Capricorn",
"Aquarius",
"Pisces",
]
SIGN_SYMBOLS = ["♈", "♉", "♊", "♋", "♌", "♍", "♎", "♏", "♐", "♑", "♒", "♓"]
# Keep as plain English — used as dictionary keys in interpretation logic
SIGN_ELEMENTS = ["Fire", "Earth", "Air", "Water"] * 3
SIGN_MODALITIES = ["Cardinal", "Fixed", "Mutable"] * 4
SIGN_POLARITIES = ["Positive", "Negative"] * 6

PLANET_NAMES = [
"Sun",
"Moon",
"Mercury",
"Venus",
"Mars",
"Jupiter",
"Saturn",
"Uranus",
"Neptune",
"Pluto",
"Chiron",
"Lilith",
"Ceres",
]
PLANET_SYMBOLS = ["☉", "☽", "☿", "♀", "♂", "♃", "♄", "⛢", "♆", "♇", "⚷", "⚸", "⚳"]
PLANET_KEYS = [
"sun",
"moon",
"mercury",
"venus",
"mars",
"jupiter",
"saturn",
"uranus",
"neptune",
"pluto",
"chiron",
"lilith",
"ceres",
]

_SWE_IDS = {
"sun": swe.SUN,
"moon": swe.MOON,
"mercury": swe.MERCURY,
"venus": swe.VENUS,
"mars": swe.MARS,
"jupiter": swe.JUPITER,
"saturn": swe.SATURN,
"uranus": swe.URANUS,
"neptune": swe.NEPTUNE,
"pluto": swe.PLUTO,
"chiron": swe.CHIRON,
"lilith": swe.MEAN_APOG,
"ceres": swe.CERES,
}

# Chiron and Ceres require seas_18.se1; all others work via Moshier fallback.
_NEEDS_SE_FILE = {"chiron", "ceres"}
_FLAGS_SE = swe.FLG_SWIEPH
_FLAGS_MOSH = swe.FLG_MOSEPH


def _norm(deg):
return deg % 360.0


def lon_to_sign(lon):
"""Return (sign_index, degrees_in_sign, minutes)."""
idx = int(lon / 30) % 12
deg_in = lon % 30
return idx, int(deg_in), int((deg_in % 1) * 60)


def get_house(lon, houses):
"""Return 1-based house number for a longitude given Whole Sign houses."""
if not houses:
return None
for h_i in range(12):
cusp_start = houses[h_i]
cusp_end = houses[(h_i + 1) % 12]
if cusp_start <= cusp_end:
in_house = cusp_start <= lon < cusp_end
else:
in_house = lon >= cusp_start or lon < cusp_end
if in_house:
return h_i + 1
return None


ASPECT_DEFS = [
("Conjunction", 0, 8, "☌", "#cc3333"),
("Sextile", 60, 5, "⚹", "#44aa44"),
("Square", 90, 7, "□", "#cc6633"),
("Trine", 120, 7, "△", "#4466cc"),
("Opposition", 180, 8, "☍", "#aa3399"),
]


def calc_aspects(natal_planets, transit_planets):
"""Return list of active aspects between transit and natal planets."""
aspects = []
for t_key in PLANET_KEYS:
t_lon = transit_planets[t_key]
for n_key in PLANET_KEYS:
n_lon = natal_planets[n_key]
diff = abs((t_lon - n_lon + 180) % 360 - 180)
for name, angle, orb, symbol, color in ASPECT_DEFS:
if abs(diff - angle) <= orb:
aspects.append(
{
"transit_key": t_key,
"natal_key": n_key,
"aspect": name,
"symbol": symbol,
"color": color,
"orb": round(abs(diff - angle), 1),
}
)
return aspects


def compute_chart(year, month, day, hour=12.0, lat=None, lon=None):
"""Compute full birth chart using Swiss Ephemeris (Moshier fallback)."""
swe.set_ephe_path(_EPHE_PATH)
jd = swe.julday(year, month, day, hour)

planets = {}
for key in PLANET_KEYS:
flags = _FLAGS_SE if key in _NEEDS_SE_FILE else _FLAGS_MOSH
result, _ = swe.calc_ut(jd, _SWE_IDS[key], flags)
planets[key] = _norm(result[0])

nn_result, _ = swe.calc_ut(jd, swe.MEAN_NODE, _FLAGS_MOSH)
north_node = _norm(nn_result[0])

chart = {"planets": planets, "jd": jd, "north_node": north_node}

if lat is not None and lon is not None:
cusps, ascmc = swe.houses(jd, lat, lon, b"W")
chart["ascendant"] = _norm(ascmc[0])
chart["midheaven"] = _norm(ascmc[1])
chart["houses"] = [_norm(c) for c in cusps[0:12]]
else:
chart["ascendant"] = None
chart["midheaven"] = None
chart["houses"] = None

return chart
Loading
Loading