def review_status(request, project_id=None): """ Return the review status for each skeleton in the request as a value between 0 and 100 (integers). """ skeleton_ids = set(int(v) for k,v in request.POST.iteritems() if k.startswith('skeleton_ids[')) user_ids = set(int(v) for k,v in request.POST.iteritems() if k.startswith('user_ids[')) status = get_review_status(skeleton_ids, user_ids) return HttpResponse(json.dumps(status))
def _evaluate(project_id, user_id, start_date, end_date, max_gap, min_nodes): # Obtain neurons that are fully reviewed at the moment # and to which the user contributed nodes within the date range. # 1. Find out skeleton_ids towards which the user contributed # within the date range ts = Treenode.objects.filter( user_id=user_id, creation_time__range = (start_date, end_date)) \ .values_list('skeleton') \ .annotate(Count('skeleton')) # Pick only skeletons for which the user contributed at least min_nodes skeleton_ids = set(skid for skid, count in ts if count > min_nodes) if not skeleton_ids: return None # Find the subset of fully reviewed (union without evaluated user) skeletons review_status = get_review_status(skeleton_ids) not_fully_reviewed = set() for skid, status in review_status.iteritems(): if status != 100: not_fully_reviewed.add(skid) skeleton_ids = skeleton_ids - not_fully_reviewed if not skeleton_ids: return None # Get review information and organize it by skeleton ID and treenode ID reviews = defaultdict(lambda: defaultdict(list)) for r in Review.objects.filter(skeleton_id__in=skeleton_ids): reviews[r.skeleton_id][r.treenode_id].append(r) # Sort all reviews of all treenodes by review time, most recent first for skid, tid_to_rs in reviews.iteritems(): for tid, rs in tid_to_rs.iteritems(): rs.sort(key=lambda r: r.review_time) rs.reverse() relations = dict(Relation.objects.filter(project_id=project_id, relation_name__in=['presynaptic_to', 'postsynaptic_to']).values_list('relation_name', 'id')) # 2. Load each fully reviewed skeleton one at a time evaluations = {skid: _evaluate_arbor(user_id, skid, tree, reviews[skid], relations, max_gap) \ for skid, tree in lazy_load_trees(skeleton_ids, ('location', 'creation_time', 'user_id', 'editor_id', 'edition_time'))} # 3. Extract evaluations for the user_id over time # Each evaluation contains an instance of EpochOps namedtuple, with members: # 'review_date_range', 'creation_date_range', 'user_node_counts', # 'splits', 'merges', 'appended', 'node_count' # The X axis is the last (user) creation date within the review epoch # The Y axis is multiple, and includes: # * skeleton_id # * reviewer_id # * time of the last node created by the user_id in skeleton_id # * nodes contributed by the user that were reviewed within the epoch # * number of nodes missed by the user (which were added by the reviewer) # * splits onto the user's nodes # * merges onto the user's nodes # * additions by the reviewer onto nodes of this user (another form of merges) # * total number of presynaptic relations of skeleton_id # * total number of postsynaptic relations of skeleton_id # * number of presynaptic_to relations created by the reviewer within the review period onto treenodes created by user_id # * number of postsynaptic_to relations created by the reviewer within the review period onto treenodes created by user_id # * newer_synapses: number of synapses created by someone else onto treenodes created by user_id, after the creation of the treenode d = [] for skid, arbor_epoch_ops in evaluations.iteritems(): for epoch_ops in arbor_epoch_ops: if 0 == epoch_ops.user_node_counts[user_id]: # user did not contribute at all to this chunk continue appended = epoch_ops.appended[user_id] print appended d.append({'skeleton_id': skid, 'reviewer_id': epoch_ops.reviewer_id, 'timepoint': epoch_ops.creation_date_range[user_id]['end'].strftime('%Y-%m-%d'), 'n_created_nodes': epoch_ops.user_node_counts[user_id], 'n_nodes': epoch_ops.node_count, 'n_missed_nodes': sum(appended), 'n_splits': epoch_ops.splits[user_id], 'n_merges': epoch_ops.merges[user_id] + len(appended), 'n_pre': epoch_ops.n_pre, 'n_post': epoch_ops.n_post, 'reviewer_n_pre': epoch_ops.reviewer_n_pre.get(user_id, 0), 'reviewer_n_post': epoch_ops.reviewer_n_post.get(user_id, 0), 'newer_pre': epoch_ops.newer_pre_count.get(user_id, 0), 'newer_post': epoch_ops.newer_post_count.get(user_id, 0)}) return d
def _evaluate(project_id, user_id, start_date, end_date, max_gap, min_nodes) -> Optional[List[Dict]]: # Obtain neurons that are fully reviewed at the moment # and to which the user contributed nodes within the date range. # 1. Find out skeleton_ids towards which the user contributed # within the date range ts = Treenode.objects.filter( user_id=user_id, creation_time__range = (start_date, end_date)) \ .values_list('skeleton') \ .annotate(Count('skeleton')) # Pick only skeletons for which the user contributed at least min_nodes skeleton_ids = set(skid for skid, count in ts if count > min_nodes) if not skeleton_ids: return None # Find the subset of fully reviewed (union without evaluated user) skeletons review_status = get_review_status(skeleton_ids) not_fully_reviewed = set() for skid, status in review_status.items(): if status[0] != status[1]: not_fully_reviewed.add(skid) skeleton_ids = skeleton_ids - not_fully_reviewed if not skeleton_ids: return None # Get review information and organize it by skeleton ID and treenode ID reviews = defaultdict(lambda: defaultdict(list)) # type: DefaultDict for r in Review.objects.filter(skeleton_id__in=skeleton_ids): reviews[r.skeleton_id][r.treenode_id].append(r) # Sort all reviews of all treenodes by review time, most recent first for skid, tid_to_rs in reviews.items(): for tid, rs in tid_to_rs.items(): rs.sort(key=lambda r: r.review_time) rs.reverse() relations = dict( Relation.objects.filter(project_id=project_id, relation_name__in=[ 'presynaptic_to', 'postsynaptic_to' ]).values_list('relation_name', 'id')) # 2. Load each fully reviewed skeleton one at a time evaluations = {skid: _evaluate_arbor(user_id, skid, tree, reviews[skid], relations, max_gap) \ for skid, tree in lazy_load_trees(skeleton_ids, ('location_x', 'location_y', 'location_z', \ 'creation_time', 'user_id', 'editor_id', \ 'edition_time'))} # 3. Extract evaluations for the user_id over time # Each evaluation contains an instance of EpochOps namedtuple, with members: # 'review_date_range', 'creation_date_range', 'user_node_counts', # 'splits', 'merges', 'appended', 'node_count' # The X axis is the last (user) creation date within the review epoch # The Y axis is multiple, and includes: # * skeleton_id # * reviewer_id # * time of the last node created by the user_id in skeleton_id # * nodes contributed by the user that were reviewed within the epoch # * number of nodes missed by the user (which were added by the reviewer) # * splits onto the user's nodes # * merges onto the user's nodes # * additions by the reviewer onto nodes of this user (another form of merges) # * total number of presynaptic relations of skeleton_id # * total number of postsynaptic relations of skeleton_id # * number of presynaptic_to relations created by the reviewer within the review period onto treenodes created by user_id # * number of postsynaptic_to relations created by the reviewer within the review period onto treenodes created by user_id # * newer_synapses: number of synapses created by someone else onto treenodes created by user_id, after the creation of the treenode d = [] for skid, arbor_epoch_ops in evaluations.items(): for epoch_ops in arbor_epoch_ops: if 0 == epoch_ops.user_node_counts[user_id]: # user did not contribute at all to this chunk continue appended = epoch_ops.appended[user_id] d.append({ 'skeleton_id': skid, 'reviewer_id': epoch_ops.reviewer_id, 'timepoint': epoch_ops.creation_date_range[user_id]['end'].strftime( '%Y-%m-%d'), 'n_created_nodes': epoch_ops.user_node_counts[user_id], 'n_nodes': epoch_ops.node_count, 'n_missed_nodes': sum(appended), 'n_splits': epoch_ops.splits[user_id], 'n_merges': epoch_ops.merges[user_id] + len(appended), 'n_pre': epoch_ops.n_pre, 'n_post': epoch_ops.n_post, 'reviewer_n_pre': epoch_ops.reviewer_n_pre.get(user_id, 0), 'reviewer_n_post': epoch_ops.reviewer_n_post.get(user_id, 0), 'newer_pre': epoch_ops.newer_pre_count.get(user_id, 0), 'newer_post': epoch_ops.newer_post_count.get(user_id, 0) }) return d