diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 0000000..98c7611
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,32 @@
+---
+name: Bug report
+about: Create a report to help us improve
+title: ''
+labels: bug
+assignees: ''
+
+---
+
+**Describe the bug**
+A clear and concise description of what the bug is.
+
+**To Reproduce**
+Steps to reproduce the behavior:
+1. Go to '...'
+2. Click on '....'
+3. Scroll down to '....'
+4. See error
+
+**Expected behavior**
+A clear and concise description of what you expected to happen.
+
+**Screenshots**
+If applicable, add screenshots to help explain your problem.
+
+**Your configuration (please complete the following information):**
+ - OS: [e.g. iOS]
+- Python version: [e.g. 3.7.0]
+ - Kosmorro version: [e.g. 1.0.0]
+
+**Additional context**
+Add any other context about the problem here.
diff --git a/.github/ISSUE_TEMPLATE/event-type-proposal.md b/.github/ISSUE_TEMPLATE/event-type-proposal.md
new file mode 100644
index 0000000..2184ff0
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/event-type-proposal.md
@@ -0,0 +1,20 @@
+---
+name: Event type proposal
+about: Suggest a type of event you'd like to see implemented in Kosmorro
+title: ''
+labels: "enhancement, \U0001F320 events"
+assignees: ''
+
+---
+
+**Event type**:
+**Involved objects (check the boxes)**:
+- [ ] Sun
+- [ ] Earth's moon
+- [ ] Planets
+
+**Definition of the event:**
+
+
+
+**Other useful information** (implementation proposal, etc):
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
new file mode 100644
index 0000000..11fc491
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,20 @@
+---
+name: Feature request
+about: Suggest an idea for this project
+title: ''
+labels: enhancement
+assignees: ''
+
+---
+
+**Is your feature request related to a problem? Please describe.**
+A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
+
+**Describe the solution you'd like**
+A clear and concise description of what you want to happen.
+
+**Describe alternatives you've considered**
+A clear and concise description of any alternative solutions or features you've considered.
+
+**Additional context**
+Add any other context or screenshots about the feature request here.
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 0000000..6ad8ee4
--- /dev/null
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,18 @@
+| Q | A
+| -------------- | ---
+| Bug fix? | yes/no
+| New feature? | yes/no
+| Related issues | Fix #...
+| Has BC-break | yes/no
+| License | GNU AGPL-v3
+
+**Checklist:**
+
+- [ ] I have updated the manpages
+
+
+
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000..12bf2e6
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,15 @@
+version: 2
+
+updates:
+- package-ecosystem: pip
+ directory: "/"
+ schedule:
+ interval: daily
+ time: "04:00"
+ open-pull-requests-limit: 10
+ target-branch: master
+ reviewers:
+ - Deuchnord
+ commit-message:
+ prefix: chore
+ include: scope
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
new file mode 100644
index 0000000..6c147c1
--- /dev/null
+++ b/.github/workflows/codeql-analysis.yml
@@ -0,0 +1,66 @@
+# For most projects, this workflow file will not need changing; you simply need
+# to commit it to your repository.
+#
+# You may wish to alter this file to override the set of languages analyzed,
+# or to provide custom queries or build logic.
+name: "CodeQL"
+
+on:
+ push:
+ branches: [master, features]
+ pull_request:
+ branches: [master, features]
+ schedule:
+ - cron: '0 8 * * 0'
+
+jobs:
+ analyze:
+ name: Analyze
+ runs-on: ubuntu-latest
+
+ strategy:
+ fail-fast: false
+ matrix:
+ language: ['python']
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v2
+ with:
+ # We must fetch at least the immediate parents so that if this is
+ # a pull request then we can checkout the head.
+ fetch-depth: 2
+
+ # If this run was triggered by a pull request event, then checkout
+ # the head of the pull request instead of the merge commit.
+ - run: git checkout HEAD^2
+ if: ${{ github.event_name == 'pull_request' }}
+
+ # Initializes the CodeQL tools for scanning.
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@v1
+ with:
+ languages: ${{ matrix.language }}
+ # If you wish to specify custom queries, you can do so here or in a config file.
+ # By default, queries listed here will override any specified in a config file.
+ # Prefix the list here with "+" to use these queries and those in the config file.
+ # queries: ./path/to/local/query, your-org/your-repo/queries@main
+
+ # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
+ # If this step fails, then you should remove it and run the build manually (see below)
+ - name: Autobuild
+ uses: github/codeql-action/autobuild@v1
+
+ # ℹ️ Command-line programs to run using the OS shell.
+ # 📚 https://git.io/JvXDl
+
+ # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
+ # and modify them (or add more) to build your code if your project
+ # uses a compiled language
+
+ #- run: |
+ # make bootstrap
+ # make release
+
+ - name: Perform CodeQL Analysis
+ uses: github/codeql-action/analyze@v1
diff --git a/.github/workflows/commitlint.yml b/.github/workflows/commitlint.yml
new file mode 100644
index 0000000..aa7a46e
--- /dev/null
+++ b/.github/workflows/commitlint.yml
@@ -0,0 +1,20 @@
+name: Commit lint
+
+on:
+ push:
+ branches: [master, features]
+ pull_request:
+ branches: [master, features]
+
+jobs:
+ commitlint:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v2
+ with:
+ fetch-depth: 0
+ - uses: wagoid/commitlint-github-action@v2
+ with:
+ helpURL: 'https://github.com/Kosmorro/kosmorro/blob/master/CONTRIBUTING.md#commiting'
+
diff --git a/.github/workflows/pylint.yml b/.github/workflows/pylint.yml
new file mode 100644
index 0000000..7151b2b
--- /dev/null
+++ b/.github/workflows/pylint.yml
@@ -0,0 +1,26 @@
+name: Python Lint
+
+on:
+ push:
+ branches: [master, features]
+ pull_request:
+ branches: [master, features]
+
+jobs:
+ pylint:
+ runs-on: ubuntu-latest
+
+ name: PyLint
+ steps:
+ - uses: actions/checkout@v1
+ - name: Set up Python
+ uses: actions/setup-python@v1
+ with:
+ python-version: 3.9
+ - name: Install dependencies
+ run: |
+ pip install --upgrade pip pipenv
+ pipenv sync -d
+ - name: Lint
+ run: |
+ pipenv run pylint kosmorro *.py kosmorrolib/*.py
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..52fb17a
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,84 @@
+name: Release Application
+
+on:
+ push:
+ tags: ['v*']
+
+jobs:
+ release:
+ name: Create release
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v2
+ - name: Setup
+ run: |
+ sudo apt update
+ sudo apt install ruby
+ sudo gem install ronn
+ - name: Prepare release
+ id: prepare_release
+ run: |
+ changelog="$(git diff HEAD^ HEAD -- CHANGELOG.md | grep -E '\+[#*]' | sed 's/^+//')"
+ changelog="${changelog//$'%'/'%25'}"
+ changelog="${changelog//$'\n'/'%0A'}"
+ changelog="${changelog//$'\r'/'%0D'}"
+ echo "::set-output name=changelog::$changelog"
+ - name: Build locales
+ env:
+ POEDITOR_API_ACCESS: ${{ secrets.POEDITOR_API_ACCESS }}
+ run: |
+ make POEDITOR_API_ACCESS="${POEDITOR_API_ACCESS}" i18n
+ tar cf locales.tar.gz kosmorrolib/locales
+ - name: Create release
+ id: create_release
+ uses: actions/create-release@v1
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ tag_name: ${{ github.ref }}
+ release_name: Version ${{ github.ref }}
+ draft: true
+ prerelease: false
+ body: |
+ ${{ steps.prepare_release.outputs.changelog }}
+ - name: Upload locales
+ id: upload-locales
+ uses: actions/upload-release-asset@v1
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ upload_url: ${{ steps.create_release.outputs.upload_url }}
+ asset_path: ./locales.tar.gz
+ asset_name: locales.tar.gz
+ asset_content_type: application/x-tar
+
+
+ pip:
+ name: Release to PyPI
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v1
+ - name: Set up Python
+ uses: actions/setup-python@v1
+ with:
+ python-version: '3.7'
+ - name: Setup environment
+ run: |
+ sudo apt update
+ sudo apt install ruby
+ sudo gem install ronn
+ - name: Install dependencies
+ run: |
+ python -m pip install --upgrade pip
+ pip install setuptools wheel twine skyfield numpy tabulate Babel requests
+ - name: Build and publish
+ env:
+ TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
+ TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
+ POEDITOR_API_ACCESS: ${{ secrets.POEDITOR_API_ACCESS }}
+ POEDITOR_PROJECT_ID: 306433
+ run: |
+ make POEDITOR_API_ACCESS="${POEDITOR_API_ACCESS}" POEDITOR_PROJECT_ID="306433" build
+ twine upload dist/*
+
diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml
new file mode 100644
index 0000000..1c3d234
--- /dev/null
+++ b/.github/workflows/stale.yml
@@ -0,0 +1,46 @@
+name: Stale
+
+on:
+ schedule:
+ - cron: '0 12 * * *'
+
+jobs:
+ analyze:
+ name: Analyze
+ runs-on: ubuntu-latest
+
+ strategy:
+ fail-fast: false
+ matrix:
+ language: ['python']
+
+ steps:
+ - name: Close Stale Issues
+ uses: actions/stale@v3
+ with:
+ start-date: 2021-02-01
+
+ days-before-stale: 60
+ days-before-issue-close: 7
+ days-before-pr-close: 14
+
+ stale-label: stale
+ close-issue-label: wontfix
+ close-pr-label: wontmerge
+
+ exempt-issue-label: 'confirmed,enhancement'
+ exempt-pr-label: 'on hold'
+
+ stale-issue-message: >
+ This issue has not been active since a lot of time. If you think it is still valid, feel free to reply with a simple comment.
+ Without any update, I will close it in 7 days.
+ stale-pr-message: >
+ This pull request has not been active since a lot of time. If you're still working on it, please reply with a simple comment.
+ Without any update, I will close it in 14 days.
+
+ close-issue-message: >
+ Closing this issue.
+ If you think it should be considered, feel free to exhume it.
+ close-pr-message: >
+ Closing this PR, since it has not got activity for a while.
+ If you're still working on it, feel free to reopen it.
diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml
new file mode 100644
index 0000000..4ef76c0
--- /dev/null
+++ b/.github/workflows/unit-tests.yml
@@ -0,0 +1,77 @@
+name: Unit tests
+
+on:
+ push:
+ branches: [master, features]
+ pull_request:
+ branches: [master, features]
+
+jobs:
+ legacy-unit-tests:
+ runs-on: ${{ matrix.os }}
+ strategy:
+ fail-fast: false
+ matrix:
+ os:
+ - ubuntu-18.04
+ - ubuntu-20.04
+ - macos-10.15
+ - macos-11.0
+ python-version:
+ - '3.7'
+ - '3.8'
+ - '3.9'
+
+ name: Legacy unit tests (Python ${{ matrix.python-version }} on ${{ matrix.os }})
+ steps:
+ - uses: actions/checkout@v1
+ - name: Set up Python
+ uses: actions/setup-python@v1
+ with:
+ python-version: ${{ matrix.python-version }}
+ - name: Install dependencies
+ run: |
+ pip install --upgrade pip pipenv
+ pipenv sync -d
+ - name: Unit tests
+ env:
+ COVERALLS_PRO_TOKEN: ${{ secrets.COVERALLS_TOKEN }}
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ run: |
+ make test
+ pipenv run coveralls --service=github
+
+ unit-tests:
+ runs-on: ${{ matrix.os }}
+ strategy:
+ fail-fast: false
+ matrix:
+ os:
+ - ubuntu-18.04
+ - ubuntu-20.04
+ - macos-10.15
+ - macos-11.0
+ python-version:
+ - '3.7'
+ - '3.8'
+ - '3.9'
+ - '3.10'
+
+ name: Unit tests (Python ${{ matrix.python-version }} on ${{ matrix.os }})
+ steps:
+ - uses: actions/checkout@v1
+ - name: Set up Python
+ uses: actions/setup-python@v1
+ with:
+ python-version: ${{ matrix.python-version }}
+ - name: Install dependencies
+ run: |
+ pip install --upgrade pip pipenv
+ pipenv sync -d
+ - name: Unit tests
+ env:
+ COVERALLS_PRO_TOKEN: ${{ secrets.COVERALLS_TOKEN }}
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ run: |
+ make test
+ pipenv run coveralls --service=github
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..d8a1bb0
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+__pycache__
+.coverage
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..8438cb2
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,50 @@
+.PHONY: test
+tests: legacy-tests
+ python3 tests.py
+
+legacy-tests:
+ unset KOSMORRO_LATITUDE; \
+ unset KOSMORRO_LONGITUDE; \
+ unset KOSMORRO_TIMEZONE; \
+ LANG=C pipenv run python3 -m coverage run -m unittest tests
+
+.PHONY: build
+build:
+ python3 setup.py sdist bdist_wheel
+
+env:
+ @if [[ "$$RELEASE_NUMBER" == "" ]]; \
+ then echo "Missing environment variable: RELEASE_NUMBER."; \
+ echo 'Example: export RELEASE_NUMBER="1.0.0" (without the leading "v")'; \
+ exit 1; \
+ fi
+
+release: env
+ @echo -e "\e[1mCreating release with version number \e[36m$$RELEASE_NUMBER\e[0m"
+ @echo
+
+ sed "s/^VERSION =.*/VERSION = '$$RELEASE_NUMBER'/g" kosmorrolib/version.py > version.py
+ mv version.py kosmorrolib/version.py
+
+ pipenv run python setup.py extract_messages --output-file=kosmorrolib/locales/messages.pot > /dev/null
+
+ conventional-changelog -p angular -i CHANGELOG.md -s
+ sed "0,/\\[\\]/s/\\[\\]/[v$$RELEASE_NUMBER]/g" CHANGELOG.md > /tmp/CHANGELOG.md
+ sed -e "s/...v)/...v$$RELEASE_NUMBER)/" /tmp/CHANGELOG.md > CHANGELOG.md
+ rm /tmp/CHANGELOG.md
+
+ @echo
+ @echo -e "\e[1mRelease \e[36m$$RELEASE_NUMBER\e[39m is ready to commit."
+ @echo -e "Please review the changes, then invoke \e[33mmake finish-release\e[39m."
+
+finish-release: env
+ git add CHANGELOG.md kosmorrolib/version.py kosmorrolib/locales/messages.pot
+ git commit -m "build: bump version $$RELEASE_NUMBER"
+ git tag "v$$RELEASE_NUMBER"
+ git checkout features
+ git merge master
+ git checkout master
+
+ @echo
+ @echo -e "\e[1mVersion \e[36m$$RELEASE_NUMBER\e[39m successfully tagged!"
+ @echo -e "Invoke \e[33mgit push origin master features v$$RELEASE_NUMBER\e[39m to finish."
diff --git a/Pipfile b/Pipfile
index 17a706b..8bd049e 100644
--- a/Pipfile
+++ b/Pipfile
@@ -9,6 +9,7 @@ pylint = "*"
babel = "*"
unittest-data-provider = "*"
coveralls = "*"
+parameterized = "*"
[packages]
skyfield = ">=1.32.0,<2.0.0"
diff --git a/Pipfile.lock b/Pipfile.lock
index 2267729..a08d36d 100644
--- a/Pipfile.lock
+++ b/Pipfile.lock
@@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
- "sha256": "05fc0ad542c6b81f02f9f1d6c4dbf8d2743ab07678c5c42aa3f7739c9aee7e36"
+ "sha256": "6b2c66b95f3dd68b2c892979187a16d4e60114c973470258e2cf0c091bf75b8c"
},
"pipfile-spec": 6,
"requires": {
@@ -69,39 +69,43 @@
},
"sgp4": {
"hashes": [
- "sha256:0864ba1062418b1b3e23e38e90032dd2c6be1f09b66c5026a18f8171548720a9",
- "sha256:1601f590950cd34ffc54c8c2811a228632d394c1e28a74054373638f7ead4801",
- "sha256:2a75446b7299217f7e66002677226dab4e41b1ad62ce7fb238c0947071655899",
- "sha256:2ad7a1c6dde04591a847c85397151e2adb458d889838a9e10177055bf373ec4a",
- "sha256:2c21fc1f47882136883390497f92afd387d79c35f23a534b652ae44e02da0518",
- "sha256:318309f4efa05361a64498ec0ac8e42a288439efe9cce6f823366c792d645e5c",
- "sha256:39acc3f6a5efb9ccf76e2d3fbe4b56c9c0de10db0d5d4cfdf421b0969bbaa1e6",
- "sha256:41bcb23f9fe020fa41d1729eebdff97144a6c884260ce50065e9201c45a989f8",
- "sha256:71db20e277b639c05287302521e7c56fd9f77d081bb7a5f251294c18af188e43",
- "sha256:7ea354472687dd54a40c0199238e6d22e4b86ab4d5eb356353be8559816f2754",
- "sha256:89b42139eefd741724b6e41b80510cdbd76b0bda8f85d6cb37564051d119c384",
- "sha256:93edb356869cd0c4c8b715523d26555c69bf81cebad23e5a31abdd5623e8819b",
- "sha256:967ac911e1ffbd795df97e89c17f0c07fb2c782ae16f02b2bed2f9ad834cc7f9",
- "sha256:b59509b488c0674c29806c181238a4d4868797122fb8e6681f7a3e5540a25403",
- "sha256:c76a8db1df422a8473a680fcfb3c6b4cfa0318cf0e7fac9ea1b898f898139b8f",
- "sha256:d33c185606eeeb7067ab3626eccdfdc4ea74346093fae646541ce8998c861540",
- "sha256:de3ddc1a8a01a8b9f67008d2da85376d59aebac464a54477e11f459119de5308",
- "sha256:df87ca4b6eac69b7ee2a6c0f7a3a29d9eda9785e21767e320d768f14170dd693",
- "sha256:e303c82a3fc51a73cf0f69ee3215b25c299d84fb766e522a71136ad6f9d3a6c0",
- "sha256:e576d9f4721d6380d6a7b9a9ca4dbc863ba9bd6f9242b4df12c0f68b50282f45",
- "sha256:f45d0a205fb18919ff83ca449d483e6306721365d5ea52d63f86d91b6ca967c0",
- "sha256:f5e6787d59683bfa5c9e1b88210d7bafc36b2fd8799438f30e1effa7da76764c",
- "sha256:f7fc5b55497d79fe638fe203bc634332cea3f466d3f08910a956f97477cdecbb",
- "sha256:fec4b597c4cc3dd330c9fd049c2d88ccea16aac474a57343082fa17fe1f84a05"
+ "sha256:0bcc2e078d04a32f0db1278ecf434b85d51682ab0d6144490940f8a7479305c9",
+ "sha256:15745e778634c7aea8a7d53f0108562ee80d3f9f4e2812ab1884b20f5fb1f995",
+ "sha256:19cd0d8e2c0826819bd8e46ffcf7866d137078b4110603c644a9eb3427347fe6",
+ "sha256:2cb573d4831db7ebfcd80442ba72cd3cbcae99d33bcc8ac298186bb358515cad",
+ "sha256:465dc1ef603aac17f59a1b00716c1f0187b90eaf88ce14ff6b19a40c7dd4ad97",
+ "sha256:6a7b9c109664284df3914e71bc9f773eda574d920763de5aac882e4e02002730",
+ "sha256:786b8e674d23c9efa634b782dc9650970e8662c92bc7ba457296fad84afaf34a",
+ "sha256:7b638875f7072926e57145537e831128a9bc6fed54c52fac8f5248fb775acc3e",
+ "sha256:802db53c0bb3caf58f69e38fbb04d7648c98ec4b34e17b4c3e9f81308352a9e1",
+ "sha256:850e16475a13cb11ec12c69c9b91d03e8f61461b083d9c6232d6119c2777a13f",
+ "sha256:85fb39c28741270e1552bc90269e1d73d65eb1383696848100dd991622a87bf7",
+ "sha256:8777dc09d027b4bd74675c91cf2284d79f800e11a22d0e18acca1611c49ae955",
+ "sha256:968760092038672e6e0686b581090016d491bb373a41416d2898f334bfa831e8",
+ "sha256:96b8359c520e0e66d6d9ec0d775c948401887b8b60ee0775d3b6ad465f13ba6a",
+ "sha256:9a097c1f7f777bd3eb36578cc195253053794396e34e34d67e7e2c7fb6960fda",
+ "sha256:9e6b0a476a9481aec19a23d69292b0009a465962decc15d22407a99aa4ad306a",
+ "sha256:a427bb73931d91dcc5e8b8ca796636b862f172c471ebc000e43eed6d84e2ee72",
+ "sha256:ae4d2401eff33a31737ec15a947a97bebe784701e064bb1d3096e05b068ca5b8",
+ "sha256:b110b2e4cb9223913e48e1f1ed5783d52a29e97556fa89dfdef721d23d42b134",
+ "sha256:bc17cc051246e21b0e63a536d236ddec6e27761107c7c3acfb6cdfe6c61627d8",
+ "sha256:bc443f34a0a3d6c3b02701403c0cffc9af4a3e864f41168cd0fc6b416e27ff58",
+ "sha256:c13f98a9227fb5a242c9ea17bcc701e220deb75c576d240581107d4e9d20ce80",
+ "sha256:c3a17700f19c881cb816bc7998ccb32e07ad5dc1672affff90324e09f3262025",
+ "sha256:c600da1a26257b733df9de5e8a916f06160b21f819f073cd56cbe7f3be709877",
+ "sha256:cb07c686b2f5552f8048ad3cace371dac1e1a530fb1ce20031d1ef46854793c1",
+ "sha256:cff9b03cf770753522face8be7fec52d4ba9eee0fe9d73d324fa0724343a97a5",
+ "sha256:d31cd0347ddd3bbeee2c47867f0c8a8f9ed0d94815f902aa62aab676c740526b",
+ "sha256:dafc9bf8d52d13d82490b27b1d93a7189e408e1b7b83a838db4f39b21718053e"
],
- "version": "==2.17"
+ "version": "==2.18"
},
"six": {
"hashes": [
"sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259",
"sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"
],
- "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'",
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.15.0"
},
"skyfield": {
@@ -115,11 +119,11 @@
"develop": {
"astroid": {
"hashes": [
- "sha256:87ae7f2398b8a0ae5638ddecf9987f081b756e0e9fc071aeebdca525671fc4dc",
- "sha256:b31c92f545517dcc452f284bc9c044050862fbe6d93d2b3de4a215a6b384bf0d"
+ "sha256:21d735aab248253531bb0f1e1e6d068f0ee23533e18ae8a6171ff892b98297cf",
+ "sha256:cfc35498ee64017be059ceffab0a25bedf7548ab76f2bea691c5565896e7128d"
],
"markers": "python_version >= '3.6'",
- "version": "==2.5.0"
+ "version": "==2.5.1"
},
"babel": {
"hashes": [
@@ -146,66 +150,69 @@
},
"coverage": {
"hashes": [
- "sha256:03ed2a641e412e42cc35c244508cf186015c217f0e4d496bf6d7078ebe837ae7",
- "sha256:04b14e45d6a8e159c9767ae57ecb34563ad93440fc1b26516a89ceb5b33c1ad5",
- "sha256:0cdde51bfcf6b6bd862ee9be324521ec619b20590787d1655d005c3fb175005f",
- "sha256:0f48fc7dc82ee14aeaedb986e175a429d24129b7eada1b7e94a864e4f0644dde",
- "sha256:107d327071061fd4f4a2587d14c389a27e4e5c93c7cba5f1f59987181903902f",
- "sha256:1375bb8b88cb050a2d4e0da901001347a44302aeadb8ceb4b6e5aa373b8ea68f",
- "sha256:14a9f1887591684fb59fdba8feef7123a0da2424b0652e1b58dd5b9a7bb1188c",
- "sha256:16baa799ec09cc0dcb43a10680573269d407c159325972dd7114ee7649e56c66",
- "sha256:1b811662ecf72eb2d08872731636aee6559cae21862c36f74703be727b45df90",
- "sha256:1ccae21a076d3d5f471700f6d30eb486da1626c380b23c70ae32ab823e453337",
- "sha256:2f2cf7a42d4b7654c9a67b9d091ec24374f7c58794858bff632a2039cb15984d",
- "sha256:322549b880b2d746a7672bf6ff9ed3f895e9c9f108b714e7360292aa5c5d7cf4",
- "sha256:32ab83016c24c5cf3db2943286b85b0a172dae08c58d0f53875235219b676409",
- "sha256:3fe50f1cac369b02d34ad904dfe0771acc483f82a1b54c5e93632916ba847b37",
- "sha256:4a780807e80479f281d47ee4af2eb2df3e4ccf4723484f77da0bb49d027e40a1",
- "sha256:4a8eb7785bd23565b542b01fb39115a975fefb4a82f23d407503eee2c0106247",
- "sha256:5bee3970617b3d74759b2d2df2f6a327d372f9732f9ccbf03fa591b5f7581e39",
- "sha256:60a3307a84ec60578accd35d7f0c71a3a971430ed7eca6567399d2b50ef37b8c",
- "sha256:6625e52b6f346a283c3d563d1fd8bae8956daafc64bb5bbd2b8f8a07608e3994",
- "sha256:66a5aae8233d766a877c5ef293ec5ab9520929c2578fd2069308a98b7374ea8c",
- "sha256:68fb816a5dd901c6aff352ce49e2a0ffadacdf9b6fae282a69e7a16a02dad5fb",
- "sha256:6b588b5cf51dc0fd1c9e19f622457cc74b7d26fe295432e434525f1c0fae02bc",
- "sha256:6c4d7165a4e8f41eca6b990c12ee7f44fef3932fac48ca32cecb3a1b2223c21f",
- "sha256:6d2e262e5e8da6fa56e774fb8e2643417351427604c2b177f8e8c5f75fc928ca",
- "sha256:6d9c88b787638a451f41f97446a1c9fd416e669b4d9717ae4615bd29de1ac135",
- "sha256:755c56beeacac6a24c8e1074f89f34f4373abce8b662470d3aa719ae304931f3",
- "sha256:7e40d3f8eb472c1509b12ac2a7e24158ec352fc8567b77ab02c0db053927e339",
- "sha256:812eaf4939ef2284d29653bcfee9665f11f013724f07258928f849a2306ea9f9",
- "sha256:84df004223fd0550d0ea7a37882e5c889f3c6d45535c639ce9802293b39cd5c9",
- "sha256:859f0add98707b182b4867359e12bde806b82483fb12a9ae868a77880fc3b7af",
- "sha256:87c4b38288f71acd2106f5d94f575bc2136ea2887fdb5dfe18003c881fa6b370",
- "sha256:89fc12c6371bf963809abc46cced4a01ca4f99cba17be5e7d416ed7ef1245d19",
- "sha256:9564ac7eb1652c3701ac691ca72934dd3009997c81266807aef924012df2f4b3",
- "sha256:9754a5c265f991317de2bac0c70a746efc2b695cf4d49f5d2cddeac36544fb44",
- "sha256:a565f48c4aae72d1d3d3f8e8fb7218f5609c964e9c6f68604608e5958b9c60c3",
- "sha256:a636160680c6e526b84f85d304e2f0bb4e94f8284dd765a1911de9a40450b10a",
- "sha256:a839e25f07e428a87d17d857d9935dd743130e77ff46524abb992b962eb2076c",
- "sha256:b62046592b44263fa7570f1117d372ae3f310222af1fc1407416f037fb3af21b",
- "sha256:b7f7421841f8db443855d2854e25914a79a1ff48ae92f70d0a5c2f8907ab98c9",
- "sha256:ba7ca81b6d60a9f7a0b4b4e175dcc38e8fef4992673d9d6e6879fd6de00dd9b8",
- "sha256:bb32ca14b4d04e172c541c69eec5f385f9a075b38fb22d765d8b0ce3af3a0c22",
- "sha256:c0ff1c1b4d13e2240821ef23c1efb1f009207cb3f56e16986f713c2b0e7cd37f",
- "sha256:c669b440ce46ae3abe9b2d44a913b5fd86bb19eb14a8701e88e3918902ecd345",
- "sha256:c67734cff78383a1f23ceba3b3239c7deefc62ac2b05fa6a47bcd565771e5880",
- "sha256:c6809ebcbf6c1049002b9ac09c127ae43929042ec1f1dbd8bb1615f7cd9f70a0",
- "sha256:cd601187476c6bed26a0398353212684c427e10a903aeafa6da40c63309d438b",
- "sha256:ebfa374067af240d079ef97b8064478f3bf71038b78b017eb6ec93ede1b6bcec",
- "sha256:fbb17c0d0822684b7d6c09915677a32319f16ff1115df5ec05bdcaaee40b35f3",
- "sha256:fff1f3a586246110f34dc762098b5afd2de88de507559e63553d7da643053786"
+ "sha256:004d1880bed2d97151facef49f08e255a20ceb6f9432df75f4eef018fdd5a78c",
+ "sha256:01d84219b5cdbfc8122223b39a954820929497a1cb1422824bb86b07b74594b6",
+ "sha256:040af6c32813fa3eae5305d53f18875bedd079960822ef8ec067a66dd8afcd45",
+ "sha256:06191eb60f8d8a5bc046f3799f8a07a2d7aefb9504b0209aff0b47298333302a",
+ "sha256:13034c4409db851670bc9acd836243aeee299949bd5673e11844befcb0149f03",
+ "sha256:13c4ee887eca0f4c5a247b75398d4114c37882658300e153113dafb1d76de529",
+ "sha256:184a47bbe0aa6400ed2d41d8e9ed868b8205046518c52464fde713ea06e3a74a",
+ "sha256:18ba8bbede96a2c3dde7b868de9dcbd55670690af0988713f0603f037848418a",
+ "sha256:1aa846f56c3d49205c952d8318e76ccc2ae23303351d9270ab220004c580cfe2",
+ "sha256:217658ec7187497e3f3ebd901afdca1af062b42cfe3e0dafea4cced3983739f6",
+ "sha256:24d4a7de75446be83244eabbff746d66b9240ae020ced65d060815fac3423759",
+ "sha256:2910f4d36a6a9b4214bb7038d537f015346f413a975d57ca6b43bf23d6563b53",
+ "sha256:2949cad1c5208b8298d5686d5a85b66aae46d73eec2c3e08c817dd3513e5848a",
+ "sha256:2a3859cb82dcbda1cfd3e6f71c27081d18aa251d20a17d87d26d4cd216fb0af4",
+ "sha256:2cafbbb3af0733db200c9b5f798d18953b1a304d3f86a938367de1567f4b5bff",
+ "sha256:2e0d881ad471768bf6e6c2bf905d183543f10098e3b3640fc029509530091502",
+ "sha256:30c77c1dc9f253283e34c27935fded5015f7d1abe83bc7821680ac444eaf7793",
+ "sha256:3487286bc29a5aa4b93a072e9592f22254291ce96a9fbc5251f566b6b7343cdb",
+ "sha256:372da284cfd642d8e08ef606917846fa2ee350f64994bebfbd3afb0040436905",
+ "sha256:41179b8a845742d1eb60449bdb2992196e211341818565abded11cfa90efb821",
+ "sha256:44d654437b8ddd9eee7d1eaee28b7219bec228520ff809af170488fd2fed3e2b",
+ "sha256:4a7697d8cb0f27399b0e393c0b90f0f1e40c82023ea4d45d22bce7032a5d7b81",
+ "sha256:51cb9476a3987c8967ebab3f0fe144819781fca264f57f89760037a2ea191cb0",
+ "sha256:52596d3d0e8bdf3af43db3e9ba8dcdaac724ba7b5ca3f6358529d56f7a166f8b",
+ "sha256:53194af30d5bad77fcba80e23a1441c71abfb3e01192034f8246e0d8f99528f3",
+ "sha256:5fec2d43a2cc6965edc0bb9e83e1e4b557f76f843a77a2496cbe719583ce8184",
+ "sha256:6c90e11318f0d3c436a42409f2749ee1a115cd8b067d7f14c148f1ce5574d701",
+ "sha256:74d881fc777ebb11c63736622b60cb9e4aee5cace591ce274fb69e582a12a61a",
+ "sha256:7501140f755b725495941b43347ba8a2777407fc7f250d4f5a7d2a1050ba8e82",
+ "sha256:796c9c3c79747146ebd278dbe1e5c5c05dd6b10cc3bcb8389dfdf844f3ead638",
+ "sha256:869a64f53488f40fa5b5b9dcb9e9b2962a66a87dab37790f3fcfb5144b996ef5",
+ "sha256:8963a499849a1fc54b35b1c9f162f4108017b2e6db2c46c1bed93a72262ed083",
+ "sha256:8d0a0725ad7c1a0bcd8d1b437e191107d457e2ec1084b9f190630a4fb1af78e6",
+ "sha256:900fbf7759501bc7807fd6638c947d7a831fc9fdf742dc10f02956ff7220fa90",
+ "sha256:92b017ce34b68a7d67bd6d117e6d443a9bf63a2ecf8567bb3d8c6c7bc5014465",
+ "sha256:970284a88b99673ccb2e4e334cfb38a10aab7cd44f7457564d11898a74b62d0a",
+ "sha256:972c85d205b51e30e59525694670de6a8a89691186012535f9d7dbaa230e42c3",
+ "sha256:9a1ef3b66e38ef8618ce5fdc7bea3d9f45f3624e2a66295eea5e57966c85909e",
+ "sha256:af0e781009aaf59e25c5a678122391cb0f345ac0ec272c7961dc5455e1c40066",
+ "sha256:b6d534e4b2ab35c9f93f46229363e17f63c53ad01330df9f2d6bd1187e5eaacf",
+ "sha256:b7895207b4c843c76a25ab8c1e866261bcfe27bfaa20c192de5190121770672b",
+ "sha256:c0891a6a97b09c1f3e073a890514d5012eb256845c451bd48f7968ef939bf4ae",
+ "sha256:c2723d347ab06e7ddad1a58b2a821218239249a9e4365eaff6649d31180c1669",
+ "sha256:d1f8bf7b90ba55699b3a5e44930e93ff0189aa27186e96071fac7dd0d06a1873",
+ "sha256:d1f9ce122f83b2305592c11d64f181b87153fc2c2bbd3bb4a3dde8303cfb1a6b",
+ "sha256:d314ed732c25d29775e84a960c3c60808b682c08d86602ec2c3008e1202e3bb6",
+ "sha256:d636598c8305e1f90b439dbf4f66437de4a5e3c31fdf47ad29542478c8508bbb",
+ "sha256:deee1077aae10d8fa88cb02c845cfba9b62c55e1183f52f6ae6a2df6a2187160",
+ "sha256:ebe78fe9a0e874362175b02371bdfbee64d8edc42a044253ddf4ee7d3c15212c",
+ "sha256:f030f8873312a16414c0d8e1a1ddff2d3235655a2174e3648b4fa66b3f2f1079",
+ "sha256:f0b278ce10936db1a37e6954e15a3730bea96a0997c26d7fee88e6c396c2086d",
+ "sha256:f11642dddbb0253cc8853254301b51390ba0081750a8ac03f20ea8103f0c56b6"
],
- "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'",
- "version": "==5.4"
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4.0'",
+ "version": "==5.5"
},
"coveralls": {
"hashes": [
- "sha256:5399c0565ab822a70a477f7031f6c88a9dd196b3de2877b3facb43b51bd13434",
- "sha256:f8384968c57dee4b7133ae701ecdad88e85e30597d496dcba0d7fbb470dca41f"
+ "sha256:7bd173b3425733661ba3063c88f180127cc2b20e9740686f86d2622b31b41385",
+ "sha256:cbb942ae5ef3d2b55388cb5b43e93a269544911535f1e750e1c656aef019ce60"
],
"index": "pypi",
- "version": "==3.0.0"
+ "version": "==3.0.1"
},
"docopt": {
"hashes": [
@@ -226,7 +233,7 @@
"sha256:c729845434366216d320e936b8ad6f9d681aab72dc7cbc2d51bedc3582f3ad1e",
"sha256:fff4f0c04e1825522ce6949973e83110a6e907750cd92d128b0d14aaaadbffdc"
],
- "markers": "python_version >= '3.6' and python_version < '4'",
+ "markers": "python_version >= '3.6' and python_version < '4.0'",
"version": "==5.7.0"
},
"lazy-object-proxy": {
@@ -266,13 +273,21 @@
],
"version": "==0.6.1"
},
+ "parameterized": {
+ "hashes": [
+ "sha256:41bbff37d6186430f77f900d777e5bb6a24928a1c46fb1de692f8b52b8833b5c",
+ "sha256:9cbb0b69a03e8695d68b3399a8a5825200976536fe1cb79db60ed6a4c8c9efe9"
+ ],
+ "index": "pypi",
+ "version": "==0.8.1"
+ },
"pylint": {
"hashes": [
- "sha256:81ce108f6342421169ea039ff1f528208c99d2e5a9c4ca95cfc5291be6dfd982",
- "sha256:a251b238db462b71d25948f940568bb5b3ae0e37dbaa05e10523f54f83e6cc7e"
+ "sha256:0e21d3b80b96740909d77206d741aa3ce0b06b41be375d92e1f3244a274c1f8a",
+ "sha256:d09b0b07ba06bcdff463958f53f23df25e740ecd81895f7d2699ec04bbd8dc3b"
],
"index": "pypi",
- "version": "==2.7.1"
+ "version": "==2.7.2"
},
"pylintfileheader": {
"hashes": [
@@ -302,7 +317,7 @@
"sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b",
"sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"
],
- "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2'",
+ "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==0.10.2"
},
"unittest-data-provider": {
@@ -317,7 +332,7 @@
"sha256:1b465e494e3e0d8939b50680403e3aedaa2bc434b7d5af64dfd3c958d7f5ae80",
"sha256:de3eedaad74a2683334e282005cd8d7f22f4d55fa690a2a1020a416cb0a47e73"
],
- "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'",
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4.0'",
"version": "==1.26.3"
},
"wrapt": {
diff --git a/kosmorrolib/ephemerides.py b/kosmorrolib/ephemerides.py
index 08c34b0..2ddc578 100644
--- a/kosmorrolib/ephemerides.py
+++ b/kosmorrolib/ephemerides.py
@@ -33,7 +33,7 @@ from .exceptions import OutOfRangeDateError
RISEN_ANGLE = -0.8333
-def _get_skyfield_to_moon_phase(times: [Time], vals: [int], now: Time) -> Union[MoonPhaseType, None]:
+def _get_skyfield_to_moon_phase(times: [Time], vals: [int], now: Time) -> Union[MoonPhase, None]:
tomorrow = get_timescale().utc(now.utc_datetime().year, now.utc_datetime().month, now.utc_datetime().day + 1)
phases = list(MoonPhaseType)
@@ -65,12 +65,12 @@ def _get_skyfield_to_moon_phase(times: [Time], vals: [int], now: Time) -> Union[
next_phase_time = times[j]
break
- return MoonPhaseType(current_phase,
+ return MoonPhase(current_phase,
current_phase_time.utc_datetime() if current_phase_time is not None else None,
next_phase_time.utc_datetime() if next_phase_time is not None else None)
-def get_moon_phase(compute_date: datetime.date, timezone: int = 0) -> MoonPhaseType:
+def get_moon_phase(compute_date: datetime.date, timezone: int = 0) -> MoonPhase:
earth = get_skf_objects()['earth']
moon = get_skf_objects()['moon']
sun = get_skf_objects()['sun']
diff --git a/kosmorrolib/events.py b/kosmorrolib/events.py
index 7aa5657..5d4081a 100644
--- a/kosmorrolib/events.py
+++ b/kosmorrolib/events.py
@@ -181,7 +181,7 @@ def get_events(date: date_type, timezone: int = 0) -> [Event]:
"""Calculate and return a list of events for the given date, adjusted to the given timezone if any.
Find events that happen on April 4th, 2020 (show hours in UTC):
-
+
>>> get_events(date_type(2020, 4, 4))
[, ] start=2020-04-04 01:14:39.063308+00:00 end=None details=None />]
diff --git a/tests.py b/tests.py
index 5ed410f..11e17a8 100644
--- a/tests.py
+++ b/tests.py
@@ -3,4 +3,4 @@ import doctest
from kosmorrolib import *
for module in [events]:
- doctest.testmod(module)
+ doctest.testmod(module, verbose=True)
diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100644
index 0000000..01438bc
--- /dev/null
+++ b/tests/__init__.py
@@ -0,0 +1,6 @@
+from .core import *
+from .data import *
+from .ephemerides import *
+from .events import *
+from .testutils import *
+from .dateutil import *
diff --git a/tests/core.py b/tests/core.py
new file mode 100644
index 0000000..34cf8e5
--- /dev/null
+++ b/tests/core.py
@@ -0,0 +1,39 @@
+import unittest
+
+import os
+import kosmorrolib.core as core
+
+from datetime import date
+from dateutil.relativedelta import relativedelta
+
+
+class CoreTestCase(unittest.TestCase):
+ def test_flatten_list(self):
+ self.assertEqual([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], core.flatten_list([0, 1, 2, [3, 4, [5, 6], 7], 8, [9]]))
+
+ def test_get_env(self):
+ self.assertEqual(0, len(core.get_env()))
+
+ os.environ['SOME_RANDOM_VAR'] = 'an awesome value'
+ self.assertEqual(0, len(core.get_env()))
+
+ os.environ['KOSMORRO_GREAT_VARIABLE'] = 'value'
+ env = core.get_env()
+ self.assertEqual(1, len(env))
+ self.assertEqual('value', env.great_variable)
+
+ os.environ['KOSMORRO_ANOTHER_VARIABLE'] = 'another value'
+ env = core.get_env()
+ self.assertEqual(2, len(env))
+ self.assertEqual('value', env.great_variable)
+ self.assertEqual('another value', env.another_variable)
+
+ self.assertEqual("{'great_variable': 'value', 'another_variable': 'another value'}", str(env))
+
+ def test_date_arg_parsing(self):
+ self.assertEqual(core.get_date("+1y 2m3d"), date.today() + relativedelta(years=1, months=2, days=3))
+ self.assertEqual(core.get_date("-1y2d"), date.today() - relativedelta(years=1, days=2))
+ self.assertEqual(core.get_date("1111-11-13"), date(1111, 11, 13))
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/tests/data.py b/tests/data.py
new file mode 100644
index 0000000..d1e2103
--- /dev/null
+++ b/tests/data.py
@@ -0,0 +1,17 @@
+import unittest
+
+from kosmorrolib import data, core
+
+
+class DataTestCase(unittest.TestCase):
+ def test_object_radius_must_be_set_to_get_apparent_radius(self):
+ o = data.Planet('Saturn', 'SATURN')
+
+ with self.assertRaises(ValueError) as context:
+ o.get_apparent_radius(core.get_timescale().now(), core.get_skf_objects()['earth'])
+
+ self.assertEqual(('Missing radius for Saturn object',), context.exception.args)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/tests/dateutil.py b/tests/dateutil.py
new file mode 100644
index 0000000..984add3
--- /dev/null
+++ b/tests/dateutil.py
@@ -0,0 +1,24 @@
+import unittest
+
+from kosmorrolib import dateutil
+from datetime import datetime
+
+
+class DateUtilTestCase(unittest.TestCase):
+ def test_translate_to_timezone(self):
+ date = dateutil.translate_to_timezone(datetime(2020, 6, 9, 4), to_tz=-2)
+ self.assertEqual(2, date.hour)
+
+ date = dateutil.translate_to_timezone(datetime(2020, 6, 9, 0), to_tz=2)
+ self.assertEqual(2, date.hour)
+
+ date = dateutil.translate_to_timezone(datetime(2020, 6, 9, 8), to_tz=2, from_tz=6)
+ self.assertEqual(4, date.hour)
+
+ date = dateutil.translate_to_timezone(datetime(2020, 6, 9, 1), to_tz=0, from_tz=2)
+ self.assertEqual(8, date.day)
+ self.assertEqual(23, date.hour)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/tests/ephemerides.py b/tests/ephemerides.py
new file mode 100644
index 0000000..713a115
--- /dev/null
+++ b/tests/ephemerides.py
@@ -0,0 +1,124 @@
+import unittest
+
+from kosmorrolib.enum import MoonPhaseType
+
+from .testutils import expect_assertions
+from kosmorrolib import ephemerides
+from kosmorrolib.data import EARTH, Position, MoonPhase
+from datetime import date
+from kosmorrolib.exceptions import OutOfRangeDateError
+
+
+class EphemeridesTestCase(unittest.TestCase):
+ def test_get_ephemerides_for_aster_returns_correct_hours(self):
+ position = Position(0, 0, EARTH)
+ eph = ephemerides.get_ephemerides(date=date(2019, 11, 18),
+ position=position)
+
+ @expect_assertions(self.assertRegex, num=3)
+ def do_assertions(assert_regex):
+ for ephemeris in eph:
+ if ephemeris.object.skyfield_name == 'SUN':
+ assert_regex(ephemeris.rise_time.isoformat(), '^2019-11-18T05:41:')
+ assert_regex(ephemeris.culmination_time.isoformat(), '^2019-11-18T11:45:')
+ assert_regex(ephemeris.set_time.isoformat(), '^2019-11-18T17:48:')
+ break
+
+ do_assertions()
+
+ ###################################################################################################################
+ ### MOON PHASE TESTS ###
+ ###################################################################################################################
+
+ def test_moon_phase_new_moon(self):
+ phase = ephemerides.get_moon_phase(date(2019, 11, 25))
+ self.assertEqual(MoonPhaseType.WANING_CRESCENT, phase.phase_type)
+ self.assertIsNone(phase.time)
+ self.assertRegexpMatches(phase.next_phase_date.isoformat(), '^2019-11-26T')
+
+ phase = ephemerides.get_moon_phase(date(2019, 11, 26))
+ self.assertEqual(MoonPhaseType.NEW_MOON, phase.phase_type)
+ self.assertRegexpMatches(phase.next_phase_date.isoformat(), '^2019-12-04T')
+
+ phase = ephemerides.get_moon_phase(date(2019, 11, 27))
+ self.assertEqual(MoonPhaseType.WAXING_CRESCENT, phase.phase_type)
+ self.assertIsNone(phase.time)
+ self.assertRegexpMatches(phase.next_phase_date.isoformat(), '^2019-12-04T')
+
+ def test_moon_phase_first_crescent(self):
+ phase = ephemerides.get_moon_phase(date(2019, 11, 3))
+ self.assertEqual(MoonPhaseType.WAXING_CRESCENT, phase.phase_type)
+ self.assertIsNone(phase.time)
+ self.assertRegexpMatches(phase.next_phase_date.isoformat(), '^2019-11-04T')
+
+ phase = ephemerides.get_moon_phase(date(2019, 11, 4))
+ self.assertEqual(MoonPhaseType.FIRST_QUARTER, phase.phase_type)
+ self.assertRegexpMatches(phase.next_phase_date.isoformat(), '^2019-11-12T')
+
+ phase = ephemerides.get_moon_phase(date(2019, 11, 5))
+ self.assertEqual(MoonPhaseType.WAXING_GIBBOUS, phase.phase_type)
+ self.assertIsNone(phase.time)
+ self.assertRegexpMatches(phase.next_phase_date.isoformat(), '^2019-11-12T')
+
+ def test_moon_phase_full_moon(self):
+ phase = ephemerides.get_moon_phase(date(2019, 11, 11))
+ self.assertEqual(MoonPhaseType.WAXING_GIBBOUS, phase.phase_type)
+ self.assertIsNone(phase.time)
+ self.assertRegexpMatches(phase.next_phase_date.isoformat(), '^2019-11-12T')
+
+ phase = ephemerides.get_moon_phase(date(2019, 11, 12))
+ self.assertEqual(MoonPhaseType.FULL_MOON, phase.phase_type)
+ self.assertRegexpMatches(phase.next_phase_date.isoformat(), '^2019-11-19T')
+
+ phase = ephemerides.get_moon_phase(date(2019, 11, 13))
+ self.assertEqual(MoonPhaseType.WANING_GIBBOUS, phase.phase_type)
+ self.assertIsNone(phase.time)
+ self.assertRegexpMatches(phase.next_phase_date.isoformat(), '^2019-11-19T')
+
+ def test_moon_phase_last_quarter(self):
+ phase = ephemerides.get_moon_phase(date(2019, 11, 18))
+ self.assertEqual(MoonPhaseType.WANING_GIBBOUS, phase.phase_type)
+ self.assertIsNone(phase.time)
+ self.assertRegexpMatches(phase.next_phase_date.isoformat(), '^2019-11-19T')
+
+ phase = ephemerides.get_moon_phase(date(2019, 11, 19))
+ self.assertEqual(MoonPhaseType.LAST_QUARTER, phase.phase_type)
+ self.assertRegexpMatches(phase.next_phase_date.isoformat(), '^2019-11-26T')
+
+ phase = ephemerides.get_moon_phase(date(2019, 11, 20))
+ self.assertEqual(MoonPhaseType.WANING_CRESCENT, phase.phase_type)
+ self.assertIsNone(phase.time)
+ self.assertRegexpMatches(phase.next_phase_date.isoformat(), '^2019-11-26T')
+
+ def test_moon_phase_prediction(self):
+ phase = MoonPhase(MoonPhaseType.NEW_MOON, None, None)
+ self.assertEqual(MoonPhaseType.FIRST_QUARTER, phase.get_next_phase())
+ phase = MoonPhase(MoonPhaseType.WAXING_CRESCENT, None, None)
+ self.assertEqual(MoonPhaseType.FIRST_QUARTER, phase.get_next_phase())
+
+ phase = MoonPhase(MoonPhaseType.FIRST_QUARTER, None, None)
+ self.assertEqual(MoonPhaseType.FULL_MOON, phase.get_next_phase())
+ phase = MoonPhase(MoonPhaseType.WAXING_GIBBOUS, None, None)
+ self.assertEqual(MoonPhaseType.FULL_MOON, phase.get_next_phase())
+
+ phase = MoonPhase(MoonPhaseType.FULL_MOON, None, None)
+ self.assertEqual(MoonPhaseType.LAST_QUARTER, phase.get_next_phase())
+ phase = MoonPhase(MoonPhaseType.WANING_GIBBOUS, None, None)
+ self.assertEqual(MoonPhaseType.LAST_QUARTER, phase.get_next_phase())
+
+ phase = MoonPhase(MoonPhaseType.LAST_QUARTER, None, None)
+ self.assertEqual(MoonPhaseType.NEW_MOON, phase.get_next_phase())
+ phase = MoonPhase(MoonPhaseType.WANING_CRESCENT, None, None)
+ self.assertEqual(MoonPhaseType.NEW_MOON, phase.get_next_phase())
+
+ def test_get_ephemerides_raises_exception_on_out_of_date_range(self):
+ with self.assertRaises(OutOfRangeDateError):
+ ephemerides.get_ephemerides(date(1789, 5, 5), Position(0, 0, EARTH))
+
+ def test_get_moon_phase_raises_exception_on_out_of_date_range(self):
+ with self.assertRaises(OutOfRangeDateError):
+ ephemerides.get_moon_phase(date(1789, 5, 5))
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/tests/events.py b/tests/events.py
new file mode 100644
index 0000000..dd92611
--- /dev/null
+++ b/tests/events.py
@@ -0,0 +1,83 @@
+import unittest
+
+from datetime import date, datetime
+from parameterized import parameterized
+
+from kosmorrolib import events
+from kosmorrolib.data import Event, ASTERS
+from kosmorrolib.enum import EventType
+from kosmorrolib.exceptions import OutOfRangeDateError
+
+EXPECTED_EVENTS = [
+ (date(2020, 2, 7), []),
+
+ (date(2020, 10, 13), [Event(EventType.OPPOSITION, [ASTERS[4]], datetime(2020, 10, 13, 23, 25))]),
+
+ (date(2022, 12, 8),
+ [Event(EventType.CONJUNCTION, [ASTERS[1], ASTERS[4]], datetime(2022, 12, 8, 4, 18)),
+ Event(EventType.OPPOSITION, [ASTERS[4]], datetime(2022, 12, 8, 5, 41))]),
+
+ (date(2025, 1, 16), [Event(EventType.OPPOSITION, [ASTERS[4]], datetime(2025, 1, 16, 2, 38))]),
+
+ (date(2027, 2, 19), [Event(EventType.MOON_PERIGEE, [ASTERS[1]], datetime(2027, 2, 19, 7, 38)),
+ Event(EventType.OPPOSITION, [ASTERS[4]], datetime(2027, 2, 19, 15, 50))]),
+
+ (date(2020, 1, 2), [Event(EventType.MOON_APOGEE, [ASTERS[1]], datetime(2020, 1, 2, 1, 32)),
+ Event(EventType.CONJUNCTION, [ASTERS[2], ASTERS[5]],
+ datetime(2020, 1, 2, 16, 41))]),
+
+ (date(2020, 1, 12),
+ [Event(EventType.CONJUNCTION, [ASTERS[2], ASTERS[6]], datetime(2020, 1, 12, 9, 51)),
+ Event(EventType.CONJUNCTION, [ASTERS[2], ASTERS[9]], datetime(2020, 1, 12, 10, 13)),
+ Event(EventType.CONJUNCTION, [ASTERS[6], ASTERS[9]], datetime(2020, 1, 12, 16, 57))]),
+
+ (date(2020, 2, 10),
+ [Event(EventType.MAXIMAL_ELONGATION, [ASTERS[2]], datetime(2020, 2, 10, 13, 46), details='18.2°'),
+ Event(EventType.MOON_PERIGEE, [ASTERS[1]], datetime(2020, 2, 10, 20, 34))]),
+
+ (date(2020, 3, 24),
+ [Event(EventType.MAXIMAL_ELONGATION, [ASTERS[2]], datetime(2020, 3, 24, 1, 56), details='27.8°'),
+ Event(EventType.MOON_APOGEE, [ASTERS[1]], datetime(2020, 3, 24, 15, 39)),
+ Event(EventType.MAXIMAL_ELONGATION, [ASTERS[3]], datetime(2020, 3, 24, 21, 58),
+ details='46.1°')]),
+
+ (date(2005, 6, 16),
+ [Event(EventType.OCCULTATION, [ASTERS[1], ASTERS[5]], datetime(2005, 6, 16, 6, 31))]),
+
+ (date(2020, 4, 7), [Event(EventType.MOON_PERIGEE, [ASTERS[1]], datetime(2020, 4, 7, 18, 14))]),
+
+ (date(2020, 1, 29), [Event(EventType.MOON_APOGEE, [ASTERS[1]], datetime(2020, 1, 29, 21, 32))])
+]
+
+
+class EventTestCase(unittest.TestCase):
+ def setUp(self) -> None:
+ self.maxDiff = None
+
+ @parameterized.expand(EXPECTED_EVENTS)
+ def test_search_events(self, d: date, expected_events: [Event]):
+ actual_events = events.get_events(d)
+ self.assertEqual(len(expected_events), len(actual_events),
+ 'Expected %d elements, got %d for date %s.\n%s' % (len(expected_events),
+ len(actual_events),
+ d.isoformat(),
+ actual_events))
+
+ for i, expected_event in enumerate(expected_events):
+ actual_event = actual_events[i]
+ # Remove unnecessary precision (seconds and microseconds)
+ actual_event.start_time = datetime(actual_event.start_time.year,
+ actual_event.start_time.month,
+ actual_event.start_time.day,
+ actual_event.start_time.hour,
+ actual_event.start_time.minute)
+
+ self.assertEqual(expected_event.__dict__, actual_event.__dict__)
+
+ def test_get_events_raises_exception_on_out_of_date_range(self):
+ with self.assertRaises(OutOfRangeDateError):
+ events.get_events(date(1789, 5, 5))
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/tests/testutils.py b/tests/testutils.py
new file mode 100644
index 0000000..964a3e1
--- /dev/null
+++ b/tests/testutils.py
@@ -0,0 +1,48 @@
+import functools
+from unittest import mock
+
+
+def expect_assertions(assert_fun, num=1):
+ """Asserts that an assertion function is called as expected.
+
+ This is very useful when the assertions are in loops.
+ To use it, create a nested function in the the tests function.
+ The nested function will receive as parameter the mocked assertion function to use in place of the original one.
+ Finally, run the nested function.
+
+ Example of use:
+
+ >>> # the function we tests:
+ >>> def my_sum_function(n, m):
+ >>> # some code here
+ >>> pass
+
+ >>> # The unit tests:
+ >>> def test_sum(self):
+ >>> @expect_assertions(self.assertEqual, num=10):
+ >>> def make_assertions(assert_equal):
+ >>> for i in range (-5, 5):
+ >>> for j in range(-5, 5):
+ >>> assert_equal(i + j, my_sum_function(i, j)
+ >>>
+ >>> make_assertions() # You don't need to give any parameter, the decorator does it for you.
+
+ :param assert_fun: the assertion function to tests
+ :param num: the number of times the assertion function is expected to be called
+ """
+ assert_fun_mock = mock.Mock(side_effect=assert_fun)
+
+ def fun_decorator(fun):
+ @functools.wraps(fun)
+ def sniff_function():
+ fun(assert_fun_mock)
+
+ count = assert_fun_mock.call_count
+ if count != num:
+ raise AssertionError('Expected %d call(s) to function %s but called %d time(s).' % (num,
+ assert_fun.__name__,
+ count))
+
+ return sniff_function
+
+ return fun_decorator