class ChangeMeta(jsonobject.JsonObject): """ Metadata about a change. If available, this will be set on Change.metadata. This is only used in kafka-based pillows. """ _allow_dynamic_properties = False document_id = DefaultProperty(required=True) # Only relevant for Couch documents document_rev = jsonobject.StringProperty() # 'couch' or 'sql' data_source_type = jsonobject.StringProperty(required=True) # couch database name or one of data sources listed in corehq.apps.change_feed.data_sources data_source_name = jsonobject.StringProperty(required=True) # doc_type property of doc or else the topic name document_type = DefaultProperty() document_subtype = jsonobject.StringProperty() domain = jsonobject.StringProperty() is_deletion = jsonobject.BooleanProperty() publish_timestamp = jsonobject.DateTimeProperty(default=datetime.utcnow) # track of retry attempts attempts = jsonobject.IntegerProperty(default=0)
class TaskStatus(jsonobject.StrictJsonObject): # takes on values of soil.progress.STATES state = jsonobject.IntegerProperty() progress = jsonobject.ObjectProperty(lambda: TaskStatusProgress) result = jsonobject.ObjectProperty(lambda: TaskStatusResult) def is_finished(self): return self.state not in (STATES.not_started, STATES.started)
class CaseUploadJSON(jsonobject.StrictJsonObject): domain = jsonobject.StringProperty(required=True) # In user display format, e.g. Dec 08, 2016 19:19 EST created = jsonobject.StringProperty(required=True) upload_id = jsonobject.StringProperty(required=True) task_status = jsonobject.ObjectProperty(lambda: TaskStatus) user_name = jsonobject.StringProperty(required=True) case_type = jsonobject.StringProperty(required=True) comment = jsonobject.StringProperty() upload_file_name = jsonobject.StringProperty() upload_file_length = jsonobject.IntegerProperty() upload_file_download_allowed = jsonobject.BooleanProperty(required=True) upload_comment_edit_allowed = jsonobject.BooleanProperty(required=True)
class PaymentUpdate(jsonobject.JsonObject): id = jsonobject.StringProperty(required=True) status = jsonobject.StringProperty(required=True, choices=[SUCCESS, FAILURE]) amount = jsonobject.IntegerProperty(required=False) paymentDate = FlexibleDateTimeProperty(required=True) comments = jsonobject.StringProperty(required=False) failureDescription = jsonobject.StringProperty(required=False) paymentMode = jsonobject.StringProperty(required=False) checkNumber = jsonobject.StringProperty(required=False) bankName = jsonobject.StringProperty(required=False) @property def case_id(self): return self.id
class MonthlyPerformanceSummary(jsonobject.JsonObject): month = jsonobject.DateProperty() domain = jsonobject.StringProperty() performance_threshold = jsonobject.IntegerProperty() active = jsonobject.IntegerProperty() performing = jsonobject.IntegerProperty() def __init__(self, domain, month, selected_users, active_not_deleted_users, performance_threshold, previous_summary=None, delta_high_performers=0, delta_low_performers=0): self._previous_summary = previous_summary self._next_summary = None self._is_final = None base_queryset = MALTRow.objects.filter( domain_name=domain, month=month, user_type__in=['CommCareUser', 'CommCareUser-Deleted'], user_id__in=active_not_deleted_users, ) if selected_users: base_queryset = base_queryset.filter(user_id__in=selected_users, ) self._user_stat_from_malt = (base_queryset.values( 'user_id', 'username').annotate(total_num_forms=Sum('num_of_forms'))) num_performing_users = (self._user_stat_from_malt.filter( total_num_forms__gte=performance_threshold).count()) num_active_users = self._user_stat_from_malt.count() num_low_performing_user = num_active_users - num_performing_users if self._previous_summary: delta_high_performers = num_performing_users - self._previous_summary.number_of_performing_users delta_low_performers = num_low_performing_user - self._previous_summary.number_of_low_performing_users super(MonthlyPerformanceSummary, self).__init__( month=month, domain=domain, performance_threshold=performance_threshold, active=num_active_users, total_users_by_month=0, percent_active=0, performing=num_performing_users, delta_high_performers=delta_high_performers, delta_low_performers=delta_low_performers, ) def set_next_month_summary(self, next_month_summary): self._next_summary = next_month_summary def set_percent_active(self): self.total_users_by_month = self.inactive + self.number_of_active_users if self.total_users_by_month: self.percent_active = float(self.number_of_active_users) / float( self.total_users_by_month) else: self.percent_active = 0 @property def number_of_performing_users(self): return self.performing @property def number_of_low_performing_users(self): return self.active - self.performing @property def number_of_active_users(self): return self.active @property @memoized def inactive(self): dropouts = self.get_dropouts() return len(dropouts) if dropouts else 0 @property def previous_month(self): prev_year, prev_month = add_months(self.month.year, self.month.month, -1) return datetime.datetime(prev_year, prev_month, 1) @property def delta_high_performing(self): if self._previous_summary: return self.number_of_performing_users - self._previous_summary.number_of_performing_users else: return self.number_of_performing_users @property def delta_high_performing_pct(self): if (self.delta_high_performing and self._previous_summary and self._previous_summary.number_of_performing_users): return float(self.delta_high_performing / float( self._previous_summary.number_of_performing_users)) * 100. @property def delta_low_performing(self): if self._previous_summary: return self.number_of_low_performing_users - self._previous_summary.number_of_low_performing_users else: return self.number_of_low_performing_users @property def delta_low_performing_pct(self): if self.delta_low_performing and self._previous_summary \ and self._previous_summary.number_of_low_performing_users: return float(self.delta_low_performing / float( self._previous_summary.number_of_low_performing_users)) * 100. @property def delta_active(self): return self.active - self._previous_summary.active if self._previous_summary else self.active @property def delta_active_pct(self): if self.delta_active and self._previous_summary and self._previous_summary.active: return float(self.delta_active / float(self._previous_summary.active)) * 100. @property def delta_inactive(self): return self.inactive - self._previous_summary.inactive if self._previous_summary else self.inactive @property def delta_inactive_pct(self): if self.delta_inactive and self._previous_summary: if self._previous_summary.inactive == 0: return self.delta_inactive * 100. return float(self.delta_inactive / float(self._previous_summary.inactive)) * 100. def _get_all_user_stubs(self): return { row['user_id']: UserActivityStub( user_id=row['user_id'], username=raw_username(row['username']), num_forms_submitted=row['total_num_forms'], is_performing=row['total_num_forms'] >= self.performance_threshold, previous_stub=None, next_stub=None, ) for row in self._user_stat_from_malt } def finalize(self): """ Before a summary is "finalized" certain fields can't be accessed. """ self._is_final = True @memoized def _get_all_user_stubs_with_extra_data(self): if not self._is_final: # intentionally fail-hard with developer-facing error raise Exception( "User stubs accessed before finalized. " "Please call finalize() before calling this method.") if self._previous_summary: previous_stubs = self._previous_summary._get_all_user_stubs() next_stubs = self._next_summary._get_all_user_stubs( ) if self._next_summary else {} user_stubs = self._get_all_user_stubs() ret = [] for user_stub in user_stubs.values(): ret.append( UserActivityStub( user_id=user_stub.user_id, username=user_stub.username, num_forms_submitted=user_stub.num_forms_submitted, is_performing=user_stub.is_performing, previous_stub=previous_stubs.get(user_stub.user_id), next_stub=next_stubs.get(user_stub.user_id), )) for missing_user_id in set(previous_stubs.keys()) - set( user_stubs.keys()): previous_stub = previous_stubs[missing_user_id] ret.append( UserActivityStub( user_id=previous_stub.user_id, username=previous_stub.username, num_forms_submitted=0, is_performing=False, previous_stub=previous_stub, next_stub=next_stubs.get(missing_user_id), )) return ret def get_unhealthy_users(self): """ Get a list of unhealthy users - defined as those who were "performing" last month but are not this month (though are still active). """ if self._previous_summary: unhealthy_users = filter( lambda stub: stub.is_active and not stub.is_performing, self._get_all_user_stubs_with_extra_data()) return sorted(unhealthy_users, key=lambda stub: stub.delta_forms) def get_dropouts(self): """ Get a list of dropout users - defined as those who were active last month but are not active this month """ if self._previous_summary: dropouts = filter(lambda stub: not stub.is_active, self._get_all_user_stubs_with_extra_data()) return sorted(dropouts, key=lambda stub: stub.delta_forms) def get_newly_performing(self): """ Get a list of "newly performing" users - defined as those who are "performing" this month after not performing last month. """ if self._previous_summary: dropouts = filter(lambda stub: stub.is_newly_performing, self._get_all_user_stubs_with_extra_data()) return sorted(dropouts, key=lambda stub: -stub.delta_forms)
class AppPart(jsonobject.JsonObject): id = jsonobject.IntegerProperty() names = DefaultProperty( ) # this is almost always a dict, but for user registration it's a string
class TaskStatusResult(jsonobject.StrictJsonObject): match_count = jsonobject.IntegerProperty() created_count = jsonobject.IntegerProperty() too_many_matches = jsonobject.IntegerProperty() num_chunks = jsonobject.IntegerProperty() errors = jsonobject.ListProperty(lambda: TaskStatusResultError)
class TaskStatusProgress(jsonobject.StrictJsonObject): percent = jsonobject.IntegerProperty()