mirror of
https://github.com/zulip/zulip.git
synced 2025-11-14 10:57:58 +00:00
analytics: Use Python 3 syntax for typing (part 2).
This commit is contained in:
@@ -31,8 +31,8 @@ class CountStat:
|
|||||||
DAY = 'day'
|
DAY = 'day'
|
||||||
FREQUENCIES = frozenset([HOUR, DAY])
|
FREQUENCIES = frozenset([HOUR, DAY])
|
||||||
|
|
||||||
def __init__(self, property, data_collector, frequency, interval=None):
|
def __init__(self, property: str, data_collector: 'DataCollector', frequency: str,
|
||||||
# type: (str, DataCollector, str, Optional[timedelta]) -> None
|
interval: Optional[timedelta]=None) -> None:
|
||||||
self.property = property
|
self.property = property
|
||||||
self.data_collector = data_collector
|
self.data_collector = data_collector
|
||||||
# might have to do something different for bitfields
|
# might have to do something different for bitfields
|
||||||
@@ -46,31 +46,28 @@ class CountStat:
|
|||||||
else: # frequency == CountStat.DAY
|
else: # frequency == CountStat.DAY
|
||||||
self.interval = timedelta(days=1)
|
self.interval = timedelta(days=1)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self) -> Text:
|
||||||
# type: () -> Text
|
|
||||||
return "<CountStat: %s>" % (self.property,)
|
return "<CountStat: %s>" % (self.property,)
|
||||||
|
|
||||||
class LoggingCountStat(CountStat):
|
class LoggingCountStat(CountStat):
|
||||||
def __init__(self, property, output_table, frequency):
|
def __init__(self, property: str, output_table: Type[BaseCount], frequency: str) -> None:
|
||||||
# type: (str, Type[BaseCount], str) -> None
|
|
||||||
CountStat.__init__(self, property, DataCollector(output_table, None), frequency)
|
CountStat.__init__(self, property, DataCollector(output_table, None), frequency)
|
||||||
|
|
||||||
class DependentCountStat(CountStat):
|
class DependentCountStat(CountStat):
|
||||||
def __init__(self, property, data_collector, frequency, interval=None, dependencies=[]):
|
def __init__(self, property: str, data_collector: 'DataCollector', frequency: str,
|
||||||
# type: (str, DataCollector, str, Optional[timedelta], List[str]) -> None
|
interval: Optional[timedelta]=None, dependencies: List[str]=[]) -> None:
|
||||||
CountStat.__init__(self, property, data_collector, frequency, interval=interval)
|
CountStat.__init__(self, property, data_collector, frequency, interval=interval)
|
||||||
self.dependencies = dependencies
|
self.dependencies = dependencies
|
||||||
|
|
||||||
class DataCollector:
|
class DataCollector:
|
||||||
def __init__(self, output_table, pull_function):
|
def __init__(self, output_table: Type[BaseCount],
|
||||||
# type: (Type[BaseCount], Optional[Callable[[str, datetime, datetime], int]]) -> None
|
pull_function: Optional[Callable[[str, datetime, datetime], int]]) -> None:
|
||||||
self.output_table = output_table
|
self.output_table = output_table
|
||||||
self.pull_function = pull_function
|
self.pull_function = pull_function
|
||||||
|
|
||||||
## CountStat-level operations ##
|
## CountStat-level operations ##
|
||||||
|
|
||||||
def process_count_stat(stat, fill_to_time):
|
def process_count_stat(stat: CountStat, fill_to_time: datetime) -> None:
|
||||||
# type: (CountStat, datetime) -> None
|
|
||||||
if stat.frequency == CountStat.HOUR:
|
if stat.frequency == CountStat.HOUR:
|
||||||
time_increment = timedelta(hours=1)
|
time_increment = timedelta(hours=1)
|
||||||
elif stat.frequency == CountStat.DAY:
|
elif stat.frequency == CountStat.DAY:
|
||||||
@@ -120,16 +117,14 @@ def process_count_stat(stat, fill_to_time):
|
|||||||
currently_filled = currently_filled + time_increment
|
currently_filled = currently_filled + time_increment
|
||||||
logger.info("DONE %s (%dms)" % (stat.property, (end-start)*1000))
|
logger.info("DONE %s (%dms)" % (stat.property, (end-start)*1000))
|
||||||
|
|
||||||
def do_update_fill_state(fill_state, end_time, state):
|
def do_update_fill_state(fill_state: FillState, end_time: datetime, state: int) -> None:
|
||||||
# type: (FillState, datetime, int) -> None
|
|
||||||
fill_state.end_time = end_time
|
fill_state.end_time = end_time
|
||||||
fill_state.state = state
|
fill_state.state = state
|
||||||
fill_state.save()
|
fill_state.save()
|
||||||
|
|
||||||
# We assume end_time is valid (e.g. is on a day or hour boundary as appropriate)
|
# We assume end_time is valid (e.g. is on a day or hour boundary as appropriate)
|
||||||
# and is timezone aware. It is the caller's responsibility to enforce this!
|
# and is timezone aware. It is the caller's responsibility to enforce this!
|
||||||
def do_fill_count_stat_at_hour(stat, end_time):
|
def do_fill_count_stat_at_hour(stat: CountStat, end_time: datetime) -> None:
|
||||||
# type: (CountStat, datetime) -> None
|
|
||||||
start_time = end_time - stat.interval
|
start_time = end_time - stat.interval
|
||||||
if not isinstance(stat, LoggingCountStat):
|
if not isinstance(stat, LoggingCountStat):
|
||||||
timer = time.time()
|
timer = time.time()
|
||||||
@@ -139,8 +134,7 @@ def do_fill_count_stat_at_hour(stat, end_time):
|
|||||||
(stat.property, (time.time()-timer)*1000, rows_added))
|
(stat.property, (time.time()-timer)*1000, rows_added))
|
||||||
do_aggregate_to_summary_table(stat, end_time)
|
do_aggregate_to_summary_table(stat, end_time)
|
||||||
|
|
||||||
def do_delete_counts_at_hour(stat, end_time):
|
def do_delete_counts_at_hour(stat: CountStat, end_time: datetime) -> None:
|
||||||
# type: (CountStat, datetime) -> None
|
|
||||||
if isinstance(stat, LoggingCountStat):
|
if isinstance(stat, LoggingCountStat):
|
||||||
InstallationCount.objects.filter(property=stat.property, end_time=end_time).delete()
|
InstallationCount.objects.filter(property=stat.property, end_time=end_time).delete()
|
||||||
if stat.data_collector.output_table in [UserCount, StreamCount]:
|
if stat.data_collector.output_table in [UserCount, StreamCount]:
|
||||||
@@ -151,8 +145,7 @@ def do_delete_counts_at_hour(stat, end_time):
|
|||||||
RealmCount.objects.filter(property=stat.property, end_time=end_time).delete()
|
RealmCount.objects.filter(property=stat.property, end_time=end_time).delete()
|
||||||
InstallationCount.objects.filter(property=stat.property, end_time=end_time).delete()
|
InstallationCount.objects.filter(property=stat.property, end_time=end_time).delete()
|
||||||
|
|
||||||
def do_aggregate_to_summary_table(stat, end_time):
|
def do_aggregate_to_summary_table(stat: CountStat, end_time: datetime) -> None:
|
||||||
# type: (CountStat, datetime) -> None
|
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
|
|
||||||
# Aggregate into RealmCount
|
# Aggregate into RealmCount
|
||||||
@@ -202,8 +195,9 @@ def do_aggregate_to_summary_table(stat, end_time):
|
|||||||
## Utility functions called from outside counts.py ##
|
## Utility functions called from outside counts.py ##
|
||||||
|
|
||||||
# called from zerver/lib/actions.py; should not throw any errors
|
# called from zerver/lib/actions.py; should not throw any errors
|
||||||
def do_increment_logging_stat(zerver_object, stat, subgroup, event_time, increment=1):
|
def do_increment_logging_stat(zerver_object: Union[Realm, UserProfile, Stream], stat: CountStat,
|
||||||
# type: (Union[Realm, UserProfile, Stream], CountStat, Optional[Union[str, int, bool]], datetime, int) -> None
|
subgroup: Optional[Union[str, int, bool]], event_time: datetime,
|
||||||
|
increment: int=1) -> None:
|
||||||
table = stat.data_collector.output_table
|
table = stat.data_collector.output_table
|
||||||
if table == RealmCount:
|
if table == RealmCount:
|
||||||
id_args = {'realm': zerver_object}
|
id_args = {'realm': zerver_object}
|
||||||
@@ -224,8 +218,7 @@ def do_increment_logging_stat(zerver_object, stat, subgroup, event_time, increme
|
|||||||
row.value = F('value') + increment
|
row.value = F('value') + increment
|
||||||
row.save(update_fields=['value'])
|
row.save(update_fields=['value'])
|
||||||
|
|
||||||
def do_drop_all_analytics_tables():
|
def do_drop_all_analytics_tables() -> None:
|
||||||
# type: () -> None
|
|
||||||
UserCount.objects.all().delete()
|
UserCount.objects.all().delete()
|
||||||
StreamCount.objects.all().delete()
|
StreamCount.objects.all().delete()
|
||||||
RealmCount.objects.all().delete()
|
RealmCount.objects.all().delete()
|
||||||
@@ -233,8 +226,7 @@ def do_drop_all_analytics_tables():
|
|||||||
FillState.objects.all().delete()
|
FillState.objects.all().delete()
|
||||||
Anomaly.objects.all().delete()
|
Anomaly.objects.all().delete()
|
||||||
|
|
||||||
def do_drop_single_stat(property):
|
def do_drop_single_stat(property: str) -> None:
|
||||||
# type: (str) -> None
|
|
||||||
UserCount.objects.filter(property=property).delete()
|
UserCount.objects.filter(property=property).delete()
|
||||||
StreamCount.objects.filter(property=property).delete()
|
StreamCount.objects.filter(property=property).delete()
|
||||||
RealmCount.objects.filter(property=property).delete()
|
RealmCount.objects.filter(property=property).delete()
|
||||||
@@ -243,8 +235,8 @@ def do_drop_single_stat(property):
|
|||||||
|
|
||||||
## DataCollector-level operations ##
|
## DataCollector-level operations ##
|
||||||
|
|
||||||
def do_pull_by_sql_query(property, start_time, end_time, query, group_by):
|
def do_pull_by_sql_query(property: str, start_time: datetime, end_time: datetime, query: str,
|
||||||
# type: (str, datetime, datetime, str, Optional[Tuple[models.Model, str]]) -> int
|
group_by: Optional[Tuple[models.Model, str]]) -> int:
|
||||||
if group_by is None:
|
if group_by is None:
|
||||||
subgroup = 'NULL'
|
subgroup = 'NULL'
|
||||||
group_by_clause = ''
|
group_by_clause = ''
|
||||||
@@ -264,15 +256,13 @@ def do_pull_by_sql_query(property, start_time, end_time, query, group_by):
|
|||||||
cursor.close()
|
cursor.close()
|
||||||
return rowcount
|
return rowcount
|
||||||
|
|
||||||
def sql_data_collector(output_table, query, group_by):
|
def sql_data_collector(output_table: Type[BaseCount], query: str,
|
||||||
# type: (Type[BaseCount], str, Optional[Tuple[models.Model, str]]) -> DataCollector
|
group_by: Optional[Tuple[models.Model, str]]) -> DataCollector:
|
||||||
def pull_function(property, start_time, end_time):
|
def pull_function(property: str, start_time: datetime, end_time: datetime) -> int:
|
||||||
# type: (str, datetime, datetime) -> int
|
|
||||||
return do_pull_by_sql_query(property, start_time, end_time, query, group_by)
|
return do_pull_by_sql_query(property, start_time, end_time, query, group_by)
|
||||||
return DataCollector(output_table, pull_function)
|
return DataCollector(output_table, pull_function)
|
||||||
|
|
||||||
def do_pull_minutes_active(property, start_time, end_time):
|
def do_pull_minutes_active(property: str, start_time: datetime, end_time: datetime) -> int:
|
||||||
# type: (str, datetime, datetime) -> int
|
|
||||||
user_activity_intervals = UserActivityInterval.objects.filter(
|
user_activity_intervals = UserActivityInterval.objects.filter(
|
||||||
end__gt=start_time, start__lt=end_time
|
end__gt=start_time, start__lt=end_time
|
||||||
).select_related(
|
).select_related(
|
||||||
|
|||||||
@@ -17,19 +17,16 @@ class FillState(models.Model):
|
|||||||
|
|
||||||
last_modified = models.DateTimeField(auto_now=True) # type: datetime.datetime
|
last_modified = models.DateTimeField(auto_now=True) # type: datetime.datetime
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self) -> Text:
|
||||||
# type: () -> Text
|
|
||||||
return "<FillState: %s %s %s>" % (self.property, self.end_time, self.state)
|
return "<FillState: %s %s %s>" % (self.property, self.end_time, self.state)
|
||||||
|
|
||||||
# The earliest/starting end_time in FillState
|
# The earliest/starting end_time in FillState
|
||||||
# We assume there is at least one realm
|
# We assume there is at least one realm
|
||||||
def installation_epoch():
|
def installation_epoch() -> datetime.datetime:
|
||||||
# type: () -> datetime.datetime
|
|
||||||
earliest_realm_creation = Realm.objects.aggregate(models.Min('date_created'))['date_created__min']
|
earliest_realm_creation = Realm.objects.aggregate(models.Min('date_created'))['date_created__min']
|
||||||
return floor_to_day(earliest_realm_creation)
|
return floor_to_day(earliest_realm_creation)
|
||||||
|
|
||||||
def last_successful_fill(property):
|
def last_successful_fill(property: str) -> Optional[datetime.datetime]:
|
||||||
# type: (str) -> Optional[datetime.datetime]
|
|
||||||
fillstate = FillState.objects.filter(property=property).first()
|
fillstate = FillState.objects.filter(property=property).first()
|
||||||
if fillstate is None:
|
if fillstate is None:
|
||||||
return None
|
return None
|
||||||
@@ -41,8 +38,7 @@ def last_successful_fill(property):
|
|||||||
class Anomaly(models.Model):
|
class Anomaly(models.Model):
|
||||||
info = models.CharField(max_length=1000) # type: Text
|
info = models.CharField(max_length=1000) # type: Text
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self) -> Text:
|
||||||
# type: () -> Text
|
|
||||||
return "<Anomaly: %s... %s>" % (self.info, self.id)
|
return "<Anomaly: %s... %s>" % (self.info, self.id)
|
||||||
|
|
||||||
class BaseCount(models.Model):
|
class BaseCount(models.Model):
|
||||||
@@ -63,8 +59,7 @@ class InstallationCount(BaseCount):
|
|||||||
class Meta:
|
class Meta:
|
||||||
unique_together = ("property", "subgroup", "end_time")
|
unique_together = ("property", "subgroup", "end_time")
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self) -> Text:
|
||||||
# type: () -> Text
|
|
||||||
return "<InstallationCount: %s %s %s>" % (self.property, self.subgroup, self.value)
|
return "<InstallationCount: %s %s %s>" % (self.property, self.subgroup, self.value)
|
||||||
|
|
||||||
class RealmCount(BaseCount):
|
class RealmCount(BaseCount):
|
||||||
@@ -74,8 +69,7 @@ class RealmCount(BaseCount):
|
|||||||
unique_together = ("realm", "property", "subgroup", "end_time")
|
unique_together = ("realm", "property", "subgroup", "end_time")
|
||||||
index_together = ["property", "end_time"]
|
index_together = ["property", "end_time"]
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self) -> Text:
|
||||||
# type: () -> Text
|
|
||||||
return "<RealmCount: %s %s %s %s>" % (self.realm, self.property, self.subgroup, self.value)
|
return "<RealmCount: %s %s %s %s>" % (self.realm, self.property, self.subgroup, self.value)
|
||||||
|
|
||||||
class UserCount(BaseCount):
|
class UserCount(BaseCount):
|
||||||
@@ -88,8 +82,7 @@ class UserCount(BaseCount):
|
|||||||
# aggregating from users to realms
|
# aggregating from users to realms
|
||||||
index_together = ["property", "realm", "end_time"]
|
index_together = ["property", "realm", "end_time"]
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self) -> Text:
|
||||||
# type: () -> Text
|
|
||||||
return "<UserCount: %s %s %s %s>" % (self.user, self.property, self.subgroup, self.value)
|
return "<UserCount: %s %s %s %s>" % (self.user, self.property, self.subgroup, self.value)
|
||||||
|
|
||||||
class StreamCount(BaseCount):
|
class StreamCount(BaseCount):
|
||||||
@@ -102,7 +95,6 @@ class StreamCount(BaseCount):
|
|||||||
# aggregating from streams to realms
|
# aggregating from streams to realms
|
||||||
index_together = ["property", "realm", "end_time"]
|
index_together = ["property", "realm", "end_time"]
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self) -> Text:
|
||||||
# type: () -> Text
|
|
||||||
return "<StreamCount: %s %s %s %s %s>" % (
|
return "<StreamCount: %s %s %s %s %s>" % (
|
||||||
self.stream, self.property, self.subgroup, self.value, self.id)
|
self.stream, self.property, self.subgroup, self.value, self.id)
|
||||||
|
|||||||
Reference in New Issue
Block a user