Skip to content

Commit

Permalink
fix: duplicate reports when run for past years
Browse files Browse the repository at this point in the history
  • Loading branch information
aaxelb committed Nov 15, 2024
1 parent aa98eeb commit 1e639b0
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 8 deletions.
22 changes: 14 additions & 8 deletions osf/metrics/reports.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
assert 'report_date' in cls.UNIQUE_TOGETHER_FIELDS, f'DailyReport subclasses must have "report_date" in UNIQUE_TOGETHER_FIELDS (on {cls.__qualname__}, got {cls.UNIQUE_TOGETHER_FIELDS})'

def save(self, *args, **kwargs):
if self.timestamp is None:
self.timestamp = self.report_date
super().save(*args, **kwargs)

class Meta:
abstract = True
dynamic = metrics.MetaField('strict')
Expand All @@ -41,19 +46,15 @@ def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs, format='strict_year_month')

def deserialize(self, data):
if isinstance(data, YearMonth):
return data
elif isinstance(data, str):
return YearMonth.from_str(data)
elif isinstance(data, (datetime.datetime, datetime.date)):
return YearMonth.from_date(data)
elif isinstance(data, int):
if isinstance(data, int):
# elasticsearch stores dates in milliseconds since the unix epoch
_as_datetime = datetime.datetime.fromtimestamp(data // 1000)
return YearMonth.from_date(_as_datetime)
elif data is None:
return None
else:
try:
return YearMonth.from_any(data)
except ValueError:
raise ValueError(f'unsure how to deserialize "{data}" (of type {type(data)}) to YearMonth')

def serialize(self, data):
Expand Down Expand Up @@ -102,6 +103,11 @@ def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
assert 'report_yearmonth' in cls.UNIQUE_TOGETHER_FIELDS, f'MonthlyReport subclasses must have "report_yearmonth" in UNIQUE_TOGETHER_FIELDS (on {cls.__qualname__}, got {cls.UNIQUE_TOGETHER_FIELDS})'

def save(self, *args, **kwargs):
if self.timestamp is None:
self.timestamp = YearMonth.from_any(self.report_yearmonth).month_start()
super().save(*args, **kwargs)


@receiver(metrics_pre_save)
def set_report_id(sender, instance, **kwargs):
Expand Down
10 changes: 10 additions & 0 deletions osf/metrics/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,16 @@ def from_str(cls, input_str: str) -> YearMonth:
else:
raise ValueError(f'expected YYYY-MM format, got "{input_str}"')

@classmethod
def from_any(cls, data) -> YearMonth:
if isinstance(data, YearMonth):
return data
elif isinstance(data, str):
return YearMonth.from_str(data)
elif isinstance(data, (datetime.datetime, datetime.date)):
return YearMonth.from_date(data)
raise ValueError(f'cannot coerce {data} into YearMonth')

def __str__(self):
"""convert to string of "YYYY-MM" format"""
return f'{self.year}-{self.month:0>2}'
Expand Down
18 changes: 18 additions & 0 deletions osf_tests/metrics/test_yearmonth.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,24 @@ YearMonth(year=1974, month=3)
>>> YearMonth.from_str('2000-12')
YearMonth(year=2000, month=12)

`from_any` constructor, accepts YearMonth, "YYYY-MM", or date/datetime
>>> YearMonth.from_any('2000-12')
YearMonth(year=2000, month=12)
>>> YearMonth.from_any(_) is _
True
>>> YearMonth.from_any(datetime.date(1973, 1, 1))
YearMonth(year=1973, month=1)
>>> YearMonth.from_any(datetime.datetime(1974, 3, 2))
YearMonth(year=1974, month=3)
>>> YearMonth.from_any(None)
Traceback (most recent call last):
...
ValueError: cannot coerce None into YearMonth
>>> YearMonth.from_any(7)
Traceback (most recent call last):
...
ValueError: cannot coerce 7 into YearMonth

`__str__` method gives "YYYY-MM" format:
>>> str(YearMonth(1491, 7))
'1491-07'
Expand Down

0 comments on commit 1e639b0

Please sign in to comment.