aboutsummaryrefslogtreecommitdiffstats
path: root/97suifangqa/apps/indicator/tools.py
diff options
context:
space:
mode:
Diffstat (limited to '97suifangqa/apps/indicator/tools.py')
-rw-r--r--97suifangqa/apps/indicator/tools.py273
1 files changed, 273 insertions, 0 deletions
diff --git a/97suifangqa/apps/indicator/tools.py b/97suifangqa/apps/indicator/tools.py
new file mode 100644
index 0000000..663ec4f
--- /dev/null
+++ b/97suifangqa/apps/indicator/tools.py
@@ -0,0 +1,273 @@
+# -*- coding: utf-8 -*-
+
+"""
+utils for apps/indicator
+"""
+
+from django.contrib.auth.models import User
+
+from indicator import models as im
+from sciblog import models as sciblogm
+
+import datetime
+
+
+# get_indicator {{{
+def get_indicator(category_id="all", startswith="all"):
+ """
+ 根据指定的 category_id 和 startswith 获取 indicator
+ 返回一个 dict
+ Dict format:
+ dict = {
+ 'a': [ {'pinyin': 'aa', ...}, {'pinyin': 'ab', ...}, ... ],
+ 'b': [ {'pinyin': 'ba', ...}, {'pinyin': 'bb', ...}, ... ],
+ ...
+ }
+ """
+
+ _idict = {}
+ if category_id == 'all':
+ iqueryset = im.Indicator.objects.all()
+ else:
+ try:
+ cid = int(category_id)
+ cate = im.IndicatorCategory.objects.get(id=cid)
+ iqueryset = cate.indicators.all()
+ except ValueError:
+ raise ValueError(u'category_id 不是整数型')
+ return _idict
+ except im.IndicatorCategory.DoesNotExist:
+ raise ValueError(u'id=%s 的 IndicatorCategory 不存在'
+ % cid)
+ return _idict
+
+ if startswith == 'all':
+ starts = map(chr, range(ord('a'), ord('z')+1))
+ else:
+ starts = []
+ _str = startswith.lower()
+ for i in _str:
+ if i >= 'a' and i <= 'z':
+ starts.append(i)
+
+ for l in starts:
+ iq = iqueryset.filter(pinyin__istartswith=l).order_by('pinyin')
+ _idict[l] = [ i.dump() for i in iq ]
+ return _idict
+# }}}
+
+
+# get_followed_indicator {{{
+def get_followed_indicator(user_id, category_id="all", startswith="all"):
+ """
+ 获取已关注的指标
+ 返回 dict, 格式与 get_indicator() 一致
+ """
+
+ u = User.objects.get(id=user_id)
+ ui, created = im.UserIndicator.objects.get_or_create(user=u)
+ _idict = {}
+ iqueryset = ui.followedIndicators.all()
+ if not category_id == 'all':
+ try:
+ cid = int(category_id)
+ iqueryset = iqueryset.filter(categories__id=cid)
+ except ValueError:
+ raise ValueError(u'category_id 不是整数型')
+ return _idict
+
+ if startswith == 'all':
+ starts = map(chr, range(ord('a'), ord('z')+1))
+ else:
+ starts = []
+ _str = startswith.lower()
+ for i in _str:
+ if i >= 'a' and i <= 'z':
+ starts.append(i)
+
+ for l in starts:
+ iq = iqueryset.filter(pinyin__istartswith=l).order_by('pinyin')
+ _idict[l] = [ i.dump() for i in iq ]
+ return _idict
+# }}}
+
+
+# get_unfollowed_indicator {{{
+def get_unfollowed_indicator(user_id, category_id="all", startswith="all"):
+ """
+ 获取未关注的指标
+ 返回 dict, 格式与 get_indicator() 一致
+ """
+
+ u = User.objects.get(id=user_id)
+ ui, created = im.UserIndicator.objects.get_or_create(user=u)
+ _idict = {}
+ iqueryset = im.Indicator.objects.exclude(user_indicators=ui)
+ if not category_id == 'all':
+ try:
+ cid = int(category_id)
+ iqueryset = iqueryset.filter(categories__id=cid)
+ except ValueError:
+ raise ValueError(u'category_id 不是整数型')
+ return _idict
+
+ if startswith == 'all':
+ starts = map(chr, range(ord('a'), ord('z')+1))
+ else:
+ starts = []
+ _str = startswith.lower()
+ for i in _str:
+ if i >= 'a' and i <= 'z':
+ starts.append(i)
+
+ for l in starts:
+ iq = iqueryset.filter(pinyin__istartswith=l).order_by('pinyin')
+ _idict[l] = [ i.dump() for i in iq ]
+ return _idict
+# }}}
+
+
+# get_record {{{
+def get_record(user_id, indicator_id, begin="", end="", std=False):
+ """
+ get_record(user_id, indicator_id, begin="", end="", std=False)
+
+ return a dict with 'date' as key, and 'get_data()' as value.
+ args 'begin' and 'end' to specify the date range.
+ arg 'std=True' to get data in standard unit
+ if 'begin=""', then the earliest date is given;
+ if 'end=""', then the latest date is given.
+
+ return dict format:
+ rdata = {
+ 'date1': [d1r1.get_data(), d1r2.get_data(), ...],
+ 'date2': [d2r1.get_data(), d2r2.get_data(), ...],
+ ...
+ }
+ """
+ uid = int(user_id)
+ indid = int(indicator_id)
+ all_records = im.IndicatorRecord.objects.\
+ filter(user__id=uid, indicator__id=indid).\
+ order_by('date', 'created_at')
+ # check if 'all_records' empty
+ if not all_records:
+ return {}
+ # set 'begin' and 'end'
+ if begin == '':
+ begin = all_records[0].date
+ if end == '':
+ end = all_records.reverse()[0].date
+ # check the validity of given 'begin' and 'end'
+ if (isinstance(begin, datetime.date) and
+ isinstance(end, datetime.date)):
+ records = all_records.filter(date__range=(begin, end))
+ _rdata = {}
+ for r in records:
+ _d = r.date.isoformat()
+ # get data
+ if std:
+ _data = r.get_data_std()
+ else:
+ _data = r.get_data()
+ #
+ if _rdata.has_key(_d):
+ # the date key already exist
+ _rdata[_d] += [_data]
+ else:
+ # the date key not exist
+ _rdata[_d] = [_data]
+ # return
+ return _rdata
+ else:
+ raise ValueError(u"begin='%s' or end='%s' 不是合法的日期" %
+ (begin, end))
+ return {}
+# }}}
+
+
+# get_record_std {{{
+def get_record_std(**kwargs):
+ return get_record(std=True, **kwargs)
+# }}}
+
+
+# calc_indicator_weight {{{
+def calc_indicator_weight(user_id, indicator_id):
+ """
+ calculate the weight of given indicator
+ used by 'recommend_indicator'
+ """
+ ### XXX: weight_type: how to store the weights into database ###
+ weight_annotation = 4.0
+ weight_blog_catched = 3.0
+ weight_blog_collected = 2.0
+ weight_other = 1.0
+ ################################################################
+ # weight = weight_type * relatedindicator.weight
+ user = User.objects.get(id=user_id)
+ ri_qs = im.RelatedIndicator.objects.filter(indicator__id=indicator_id)
+ if not ri_qs:
+ # queryset empty
+ w = 0.0
+ return w
+ # queryset not empty
+ annotation_ri_qs = ri_qs.filter(annotation__collected_by=user)
+ blogcatch_ri_qs = ri_qs.filter(blog__catched_by=user)
+ blogcollect_ri_qs = ri_qs.filter(blog__collected_by=user)
+ weights = []
+ if annotation_ri_qs:
+ # related to annotations collected by user
+ for ri in annotation_ri_qs:
+ w = weight_annotation * ri.weight
+ weights.append(w)
+ elif blogcatch_ri_qs:
+ # related to blogs catched by user
+ for ri in blogcatch_ri_qs:
+ w = weight_blog_catched * ri.weight
+ weights.append(w)
+ elif blogcollect_ri_qs:
+ # related to blogs catched by user
+ for ri in blogcollect_ri_qs:
+ w = weight_blog_collected * ri.weight
+ weights.append(w)
+ else:
+ # other type, use 'ri_qs' here
+ for ri in ri_qs:
+ w = weight_other * ri.weight
+ weights.append(w)
+ # return final result
+ return max(weights)
+# }}}
+
+
+# recommend_indicator {{{
+def recommend_indicator(user_id, number):
+ """
+ recommend unfollowed indicator for user,
+ based on his/her readings and collections.
+
+ return a list with the id's of recommended indicators
+
+ TODO:
+ performance test
+ """
+ user_id = int(user_id)
+ number = int(number)
+ # get unfollowed indicators
+ u = User.objects.get(id=user_id)
+ ui, created = im.UserIndicator.objects.get_or_create(user=u)
+ uf_ind_qs = im.Indicator.objects.exclude(user_indicators=ui)
+ # calc weight for each unfollowed indicator
+ weights = []
+ for ind in uf_ind_qs:
+ w = calc_indicator_weight(user_id, ind.id)
+ weights.append({'id': ind.id, 'weight': w})
+ # sort 'weights' dict list by key 'weight'
+ weights_sorted = sorted(weights, key=lambda item: item['weight'])
+ weights_sorted.reverse()
+ # return results with largest weights
+ return weights_sorted[:number]
+# }}}
+
+