Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial data model #1

Merged
merged 11 commits into from
Nov 8, 2022
35 changes: 35 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: CI

on:
push:
branches:
- "**"
paths:
- 'undate/**'
- 'tests/**'
pull_request:

jobs:
qa:
runs-on: ubuntu-latest
defaults:
run:
working-directory: .

steps:
- uses: actions/checkout@v3
- name: Set up Python 3.9
uses: actions/setup-python@v4
with:
python-version: 3.9
cache: 'pip'
cache-dependency-path: '**/requirements-dev.txt'
- name: Install deps using Pip
run: pip install -r requirements-dev.txt
if: steps.python-cache.outputs.cache-hit != 'true'
- name: Run black
run: |
black undate --check --diff
- name: Run unit tests
run: |
python -m pytest
35 changes: 35 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
15 changes: 15 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
files: \.py
repos:
- repo: https://github.com/psf/black
rev: 22.10.0
hooks:
- id: black
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.3.0
hooks:
- id: check-case-conflict
- id: check-executables-have-shebangs
- id: debug-statements
- id: end-of-file-fixer
- id: mixed-line-ending
- id: trailing-whitespace
32 changes: 32 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,34 @@
# hackathon-2022

Repository for the DHTech 2022 Hackathon


## Instructions to setup for development

### Clone repo
```
$ git clone git@github.com:dh-tech/hackathon-2022.git
cd hackathon-2022
```

### Set up Python environment
Use a recent version of python 3.x; recommended to use a virtualenv, e.g.
```
python3 -m venv undate
source undate/bin/activate
```

### Install dependencies
```
$ pip install -r requirements-dev.txt
```

### Install pre-commit hooks
```
$ pre-commit install
```

### Run unit tests
```
python -m pytest
```
5 changes: 5 additions & 0 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-r requirements.txt

black==22.10.0
pre-commit==2.20.0
pytest==7.2.0
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

29 changes: 29 additions & 0 deletions tests/test_undate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from undate.undate import Undate, UndateInterval


def test_single_date():
assert str(Undate(2022, 11, 7)) == "2022-11-07"
assert str(Undate(2022, 11)) == "2022-11"
assert str(Undate(2022)) == "2022"
assert str(Undate(month=11, day=7)) == "--11-07"


def test_range():
# 2022 - 2023
assert str(UndateInterval(Undate(2022), Undate(2023))) == "2022/2023"
# 2022 - 2023-05
assert str(UndateInterval(Undate(2022), Undate(2023, 5))) == "2022/2023-05"
# 2022-11-01 to 2022-11-07
assert (
str(UndateInterval(Undate(2022, 11, 1), Undate(2023, 11, 7)))
== "2022-11-01/2023-11-07"
)


def test_open_range():
# 900 -
assert str(UndateInterval(Undate(900))) == "900/"
# - 1900
assert str(UndateInterval(latest=Undate(1900))) == "../1900"
# - 1900-12
assert str(UndateInterval(latest=Undate(1900, 12))) == "../1900-12"
Empty file added undate/__init__.py
Empty file.
62 changes: 62 additions & 0 deletions undate/undate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import datetime
from calendar import monthrange


class Undate:
"""Simple object for representing uncertain, fuzzy or partially unknown dates"""

earliest = None
latest = None
label = None

#: datetime strftime format for known part of date
iso_format = {
"year": "%Y",
"month": "%m",
"day": "%d",
}

def __init__(self, year=None, month=None, day=None):
# for unknowns, assume smallest possible value for earliest and
# largest valid for latest
self.earliest = datetime.date(year or datetime.MINYEAR, month or 1, day or 1)
# if day is unknown but we have year and month, calculate max day
if day is None and year and month:
_, maxday = monthrange(year, month)
else:
maxday = 31 # ???
self.latest = datetime.date(
year or datetime.MAXYEAR, month or 12, day or maxday
)
# keep track of which values are known
self.known_values = {
"year": year is not None,
"month": month is not None,
"day": day is not None,
}

def __str__(self):
# serialize to iso format for simplicity, for now
date_parts = []
# for each part of the date that is known, generate the string format
# then combine
for date_portion, known in self.known_values.items():
if known:
date_parts.append(self.earliest.strftime(self.iso_format[date_portion]))
elif date_portion == "year":
# if not known but this is year, add '-' for --MM-DD unknown year format
date_parts.append("-")
return "-".join(date_parts)


class UndateInterval:
# date range between two uncertain dates

def __init__(self, earliest=None, latest=None):
# for now, assume takes two undate objects
self.earliest = earliest
self.latest = latest

def __str__(self):
# using EDTF syntax for open ranges
return "%s/%s" % (self.earliest or "..", self.latest or "")