def queue_created_document(params, user_id): created_doc = UserCreatedDocument( merge_two_dicts({"user_id": user_id}, params)) db_session_users.add(created_doc) db_session_users.commit() db_session_users.refresh(created_doc) return {'user_created_document': created_doc.to_dict()}
def handle_request_invoice(user_id, params): plan = params.get('plan', None) plan_from_db = db_session_users.query(Plan).filter_by( stripe_id=plan).first() if not plan_from_db: return 'Invalid plan' plan_name = plan_from_db.name plan_id = plan_from_db.id current_user = db_session_users.query(User).filter_by(id=user_id).first() message = current_user.first_name + ' ' + current_user.last_name + ' is requesting to pay by invoice for the ' + plan_name + ' subscription. ' + current_user.first_name + ', the team at Compliance.ai will respond to your request soon!' try: email_helper.send_email( '*****@*****.**', current_user.email, 'Invoice request from ' + current_user.first_name, template='feedback-inline', vars={ 'feedback': message, 'User_first_name': current_user.first_name, 'User_last_name': current_user.last_name, }, ) except SMTPException as e: return error_response('Could not send invoice email.', code=500) invoice_for_db = { 'user_id': user_id, 'plan_id': plan_id, 'status': 'requested' } invoice_for_db = Invoice(invoice_for_db) db_session_users.add(invoice_for_db) db_session_users.commit() db_session_users.refresh(invoice_for_db) return {'invoice': 'invoice request sent'}
def create_review_for_job(annotation_task_id, annotation_job_id, user_id, params): annotation_job = db_session_users.query(AnnotationJob).filter_by(id=annotation_job_id).first() annotation_job.status = AnnotationJob.COMPLETE_STATUS annotation_job.completed_at = datetime.datetime.now() db_session_users.add(annotation_job) db_session_users.commit() db_session_users.refresh(annotation_job) job = annotation_job.to_dict() doc = None if 'multiple_field' in params: multiple_field = params.get('multiple_field', None) flagged_doc = UserFlaggedDocument({ 'user_id': user_id, 'doc_id': annotation_job.doc_id, 'issue_type': UserFlaggedDocument.CONTRIBUTOR_ISSUE_TYPE, 'multiple_field': multiple_field }) db_session_users.add(flagged_doc) db_session_users.commit() db_session_users.refresh(flagged_doc) doc = flagged_doc.to_dict() return {"annotation_job": job, "flagged_doc": doc}
def create_annotation_task(params): new_annotation_task = AnnotationTask(params) term_sampling_group_ids = params.get("term_sampling_group_ids", None) # FIXME: enforcing default values until the front-end has been updated to provide this explicitly if new_annotation_task.type is None: new_annotation_task.type = AnnotationTask.TOPIC_ANNOTATION_TYPE elif new_annotation_task.type == 'contributor': new_annotation_task.is_contributor_task = True db_session_users.add(new_annotation_task) db_session_users.commit() db_session_users.refresh(new_annotation_task) if term_sampling_group_ids is not None: for term_sampling_group_id in term_sampling_group_ids: attsg = AnnotationTaskTermSamplingGroup({ 'annotation_task_id': new_annotation_task.id, 'term_sampling_group_id': term_sampling_group_id }) db_session_users.add(attsg) db_session_users.commit() task_dict = new_annotation_task.to_dict() term_sampling_group_ids = db_session_users.query(AnnotationTaskTermSamplingGroup.term_sampling_group_id) \ .filter_by(annotation_task_id=new_annotation_task.id) task_dict["term_sampling_group_ids"] = [ t[0] for t in term_sampling_group_ids ] return {"annotation_task": task_dict}
def update_saved_search(user_id, saved_search_id, params): name = params.get('name', None) name_conflict_user = db_session_users.query(UserSavedSearch).filter_by( user_id=user_id, name=name).first() if name_conflict_user: return jsonify({ 'errors': "Saved search name: " + name + " is already being used" }), 409 search_args = params.get('search_args', None) saved_search = db_session_users.query(UserSavedSearch).filter_by( id=saved_search_id, user_id=user_id).first() if not saved_search: return jsonify({"errors": "No saved search for this user and id"}), 404 if name: saved_search.name = name if search_args: saved_search.search_args = search_args db_session_users.add(saved_search) db_session_users.commit() db_session_users.refresh(saved_search) return jsonify({"saved_search": saved_search.to_dict()})
def update_subscription(subscription_id, params): original_subscription = db_session_users.query(Subscription).filter_by( id=subscription_id).first() new_subscription_dict = original_subscription.__dict__ today = datetime.datetime.utcnow() new_subscription_dict['latest'] = True new_subscription_dict['notes'] = None if 'expiration_date' in params: new_exiration_date = params['expiration_date'] new_exiration_date_obj = datetime.datetime.strptime( new_exiration_date, "%Y-%m-%d") new_subscription_dict['expiration_date'] = new_exiration_date_obj # update status of subscription depending on new expiration date if new_exiration_date_obj < today or new_exiration_date_obj.date( ) == today.date(): new_subscription_dict[ 'status_reason'] = Subscription.EXPIRED_STATUS_REASON new_subscription_dict['status'] = Subscription.INACTIVE_STATUS elif new_subscription_dict['status'] != Subscription.ACTIVE_STATUS: new_subscription_dict[ 'status_reason'] = Subscription.REACTIVATED_STATUS_REASON new_subscription_dict['status'] = Subscription.ACTIVE_STATUS if 'plan_id' in params: new_plan_id = params['plan_id'] plan = db_session_users().query(Plan).filter_by(id=new_plan_id).first() if plan: new_subscription_dict['plan_id'] = new_plan_id new_subscription_dict['stripe_id'] = plan.stripe_id new_subscription_dict['start_date'] = datetime.datetime.utcnow() new_subscription_dict[ 'status_reason'] = Subscription.REACTIVATED_STATUS_REASON new_subscription_dict['status'] = Subscription.ACTIVE_STATUS if plan.recurring: new_subscription_dict['expiration_date'] = None else: new_subscription_dict[ 'expiration_date'] = get_default_expiration_date(plan) else: return {'errors': "Plan is not found"} if 'payment_type' in params: new_subscription_dict['payment_type'] = params['payment_type'] if 'notes' in params: new_subscription_dict['notes'] = params['notes'] new_subscription_dict['modified_by_user_id'] = g.user_id new_subscription = Subscription(new_subscription_dict) db_session_users.add(new_subscription) # deactivate old subscription deactivate_subscriptions(new_subscription_dict['user_id']) db_session_users.commit() db_session_users.refresh(new_subscription) return {'new_subscription': new_subscription.to_dict()}
def create_marketing_campaign(user_id, params): marketing_campaign = MarketingCampaign( merge_two_dicts(params, {'created_by_user_id': user_id})) marketing_campaign.gen_token() db_session_users.add(marketing_campaign) db_session_users.commit() db_session_users.refresh(marketing_campaign) return {"marketing_campaign": marketing_campaign.to_dict()}
def handle_payment_event(event): payment_event_for_db = Payment_Event({ 'stripe_id': event.id, 'properties': serializeClass(event) }) db_session_users.add(payment_event_for_db) db_session_users.commit() db_session_users.refresh(payment_event_for_db) return {'event': 'event received'}
def create_annotation_task_group(params): # create annotation_task_group object and add to database new_annotation_task_group = AnnotationTaskTopicGroup(params) db_session_users.add(new_annotation_task_group) db_session_users.commit() db_session_users.refresh(new_annotation_task_group) #TODO: add check here about whether tasks ids in params actually exist task_group_dict = new_annotation_task_group.to_dict() return {"annotation_task_group": task_group_dict}
def deactivate_subscriptions(user_id): # a user should have only 1 current subscription at a time latest_subscriptions = db_session_users.query(Subscription).filter_by( user_id=user_id, latest=True).all() if latest_subscriptions is not None: for latest_subscription in latest_subscriptions: latest_subscription.latest = False latest_subscription.status = Subscription.INACTIVE_STATUS latest_subscription.end_date = datetime.datetime.utcnow() db_session_users.add(latest_subscription) db_session_users.commit() db_session_users.refresh(latest_subscription)
def flag_document(user_id, doc_id, params): issue_severity = params.get('issue_severity', UserFlaggedDocument.REVIEW_SEVERITY) issue_type = params.get('issue_type', None) notes = params.get('notes', None) field = params.get('field', None) user_flagged_document_id = params.get('id', None) status = params.get('status', None) # for creating new user flagged documents if user_flagged_document_id is None: if not issue_type or issue_type not in ValidIssueTypes: return { 'errors': "Issue type must be one of: " + str(ValidIssueTypes) } if issue_severity not in ValidIssueSeverities: return { 'errors': "Issue severity must be one of: " + str(ValidIssueSeverities) } flagged_doc = UserFlaggedDocument({ 'user_id': user_id, 'doc_id': doc_id, 'issue_severity': issue_severity, 'issue_type': issue_type, 'notes': notes, 'field': field, }) # for updating an existing user flagged document (status is the only relevant use-case) else: flagged_doc = db_session_users.query(UserFlaggedDocument).filter_by( id=user_flagged_document_id).first() if status is not None: if status in ValidIssueStatuses: flagged_doc.status = status else: return { 'errors': "Issue status must be one of: " + str(ValidIssueStatuses) } db_session_users.add(flagged_doc) db_session_users.commit() db_session_users.refresh(flagged_doc) return flagged_doc.to_dict()
def start_free_trial(user_id): stripe_id = 'free_trial' free_trial = db_session_users.query(Plan).filter_by( stripe_id=stripe_id).first() subscription_for_db = { 'user_id': user_id, 'stripe_id': free_trial.stripe_id, 'plan_id': free_trial.id, 'latest': True, 'start_date': datetime.datetime.utcnow(), 'expiration_date': get_default_expiration_date(free_trial), 'status': 'active' } subscription_for_db = Subscription(subscription_for_db) deactivate_subscriptions(user_id) db_session_users.add(subscription_for_db) db_session_users.commit() db_session_users.refresh(subscription_for_db)
def create_saved_search(user_id, params): name = params.get('name', None) name_conflict_user = db_session_users.query(UserSavedSearch).filter_by( user_id=user_id, name=name).first() if name_conflict_user: return jsonify({ 'errors': "Saved search name: " + name + " is already being used" }), 409 search_args = params['search_args'] saved_search = UserSavedSearch({ 'user_id': user_id, 'search_args': search_args, 'name': name }) db_session_users.add(saved_search) db_session_users.commit() db_session_users.refresh(saved_search) return jsonify({"saved_search": saved_search.to_dict()})
def create_rated_search(user_id, params): is_relevant = params.get('is_relevant', None) doc_id = params.get('doc_id', None) search_args = params.get('search_args', None) search_entry = find_or_return_new_search_query(search_args, save_and_refresh_if_new=True) # NOTE: Although data is recorded on the UserSearchResultRating table, this method is # also used to keep track of ratings for data outside of a user search (ie. topic buttons relevancy) user_search_result_rating = db_session_users.query(UserSearchResultRating)\ .filter_by(user_id=user_id, doc_id=doc_id, search_query_id=search_entry.id).first() if user_search_result_rating is None: user_search_result_rating = \ UserSearchResultRating({'user_id': user_id, 'search_query_id': search_entry.id, 'doc_id': doc_id}) user_search_result_rating.is_relevant = is_relevant db_session_users.add(user_search_result_rating) db_session_users.commit() db_session_users.refresh(user_search_result_rating) return {}
def update_marketing_campaign(marketing_campaign_id, params): marketing_campaign = db_session_users.query(MarketingCampaign).filter_by( id=marketing_campaign_id).first() if 'start_date' in params: marketing_campaign.start_date = params['start_date'] if 'end_date' in params: marketing_campaign.end_date = params['end_date'] if 'name' in params: marketing_campaign.name = params['name'] if 'notes' in params: marketing_campaign.notes = params['notes'] # n.b. for regenerating the token if 'token' in params: marketing_campaign.gen_token() db_session_users.add(marketing_campaign) db_session_users.commit() db_session_users.refresh(marketing_campaign) return {"marketing_campaign": marketing_campaign.to_dict()}
def pop_annotation_job_from_queue(annotation_task_id, user_id): time_now = datetime.datetime.now() # grabs queued annotation jobs for this task that are assigned to the user (or nobody), # ordered first by whether they are have a user assignment, next by highest priority, # and finally falling back on the oldest created annotation_job = db_session_users.query(AnnotationJob).filter_by(annotation_task_id=annotation_task_id)\ .filter_by(status=AnnotationJob.QUEUED_STATUS)\ .filter(or_(AnnotationJob.user_id == user_id, AnnotationJob.user_id == None))\ .order_by(AnnotationJob.user_id.nullslast(), AnnotationJob.priority.desc(), AnnotationJob.created_at.asc()).first() # if by chance, we are in the period of time between when a task was updated, but before the next queuing run # came around, we want to make sure to look up annotation jobs for older annotation tasks too if annotation_job is None: old_annotation_task_ids = db_session_users.query(AnnotationTask.id).filter_by(active_task_id=annotation_task_id).subquery() annotation_job = db_session_users.query(AnnotationJob)\ .filter(AnnotationJob.annotation_task_id.in_(old_annotation_task_ids)) \ .filter_by(status=AnnotationJob.QUEUED_STATUS) \ .filter(or_(AnnotationJob.user_id == user_id, AnnotationJob.user_id == None)) \ .order_by(AnnotationJob.user_id.nullslast(), AnnotationJob.priority.desc(), AnnotationJob.created_at.asc()).first() if annotation_job is None: return {"annotation_job": None} annotation_job.status = AnnotationJob.ASSIGNED_STATUS annotation_job.user_id = user_id annotation_job.assigned_at = time_now db_session_users.add(annotation_job) db_session_users.commit() db_session_users.refresh(annotation_job) # n.b. mitigation strategy for race condition would look like: while the assigned user_id is not me -> query again # change status to error status if document is not found in index try: doc_dict = jsearch.get_record(annotation_job.doc_id) except NotFoundError: annotation_job.status = AnnotationJob.ERROR_STATUS annotation_job.notes = "Document is not found" db_session_users.add(annotation_job) db_session_users.commit() db_session_users.refresh(annotation_job) return {"errors": "Document is not found. Doc ID: " + str(annotation_job.doc_id)} # if this is training job, return info about correct judgment if annotation_job.is_gold_evaluation: # get gold judgment info to return with annotation_job object topic_group_id_subquery = db_session_users.query(AnnotationTask.annotation_task_topic_group_id)\ .filter_by(id=annotation_job.annotation_task_id)\ .subquery() # should contain just one result gold_judgment_id_subquery = db_session_users.query(AggregatedAnnotations.gold_topic_annotation_id)\ .filter_by(doc_id=annotation_job.doc_id)\ .filter(AggregatedAnnotations.annotation_task_group_id.in_(topic_group_id_subquery))\ .subquery() gold_judgment_object = db_session_users.query(TopicAnnotation.is_positive, TopicAnnotation.admin_notes)\ .filter(TopicAnnotation.id.in_(gold_judgment_id_subquery))\ .first() # this query should return just one object anyway return {'annotation_job': annotation_job.to_dict(), 'document': doc_dict, 'correct_judgment': gold_judgment_object.is_positive, 'correct_judgment_notes': gold_judgment_object.admin_notes} return {'annotation_job': annotation_job.to_dict(), 'document': doc_dict}
def update_document(user_id, doc_id, params): user_flagged_document_id = params.get('user_flagged_document_id', None) fix_contributor_notes = params.get('fix_contributor_notes', None) skip_contributor_notes = params.get('skip_contributor_notes', None) flagged_doc = None if user_flagged_document_id is not None: flagged_doc = db_session_users.query(UserFlaggedDocument).filter_by( id=user_flagged_document_id).first() # Change status without making updates in document # in case admin decides to not update document flagged by contributor if skip_contributor_notes: if flagged_doc is not None: flagged_doc.status = UserFlaggedDocument.PROCESSED_STATUS db_session_users.add(flagged_doc) db_session_users.commit() return {"status_updated": True} notes = params.get('notes', None) changes = {} category = params.get('category', None) if category: changes["category"] = category publication_date = params.get('publication_date', None) if publication_date: changes["publication_date"] = publication_date summary_text = params.get('summary_text', None) if summary_text: changes["summary_text"] = summary_text title = params.get('title', None) if title: changes["title"] = title topics_to_add = params.get('topics_to_add', None) if topics_to_add: changes["topics_to_add"] = topics_to_add topics_to_remove = params.get('topics_to_remove', None) if topics_to_remove: changes["topics_to_remove"] = topics_to_remove if not changes: return { 'errors': "changes submitted to update document must not be empty" } updated_doc = UserDocumentUpdate({ 'user_id': user_id, 'doc_id': doc_id, 'notes': notes, 'changes': changes, }) # Update status of flagged document if flagged_doc is not None: if fix_contributor_notes: flagged_doc.status = UserFlaggedDocument.FIXED_STATUS else: flagged_doc.status = UserFlaggedDocument.PROCESSED_STATUS db_session_users.add(flagged_doc) db_session_users.add(updated_doc) db_session_users.commit() db_session_users.refresh(updated_doc) return updated_doc.to_dict()
def update_annotation_task(annotation_task_id, params): original_annotation_task = db_session_users.query( AnnotationTask).filter_by(id=annotation_task_id).first() num_annotation_jobs = db_session_users.query(AnnotationJob).filter_by( annotation_task_id=annotation_task_id).count() # n.b. updating the topics hash means that we need to swap this task for a new one for # consistency of which annotations were generated against which task, as it is valuable to know # which topics were presented when annotations are created, likewise what the user and other config options # note: there is no need for a new task until this task has jobs added for it, so when the job count is still 0, # we can do a direct update to the existing task instead # note: onboarding mode tasks (with is_training_task=True) also do not create a new task when they date updated if num_annotation_jobs > 0 and not original_annotation_task.is_training_task and \ ('topics' in params or 'user_ids' in params or 'config' in params or 'term_sampling_group_ids' in params or 'is_training_task' in params or 'include_gold_annotations' in params or 'annotation_task_topic_group_id' in params): new_annotation_task_dict = original_annotation_task.__dict__ if 'topics' in params: new_annotation_task_dict['topics'] = params['topics'] # must be done before setting is_training_task due to check in setting is_training_task if 'annotation_task_topic_group_id' in params: new_annotation_task_dict[ 'annotation_task_topic_group_id'] = params[ 'annotation_task_topic_group_id'] # update is_training_task to be False if no longer in annotation_task_topic_group if new_annotation_task_dict[ 'annotation_task_topic_group_id'] is None: new_annotation_task_dict['is_training_task'] = False if 'user_ids' in params: new_annotation_task_dict['user_ids'] = params['user_ids'] # n.b. changing status between active/inactive in effect toggles this task on/off if 'status' in params and params['status'] in VALID_TASK_STATUSES: new_annotation_task_dict['status'] = params['status'] if 'config' in params: new_annotation_task_dict['config'] = merge_two_dicts( new_annotation_task_dict['config'], params['config']) if 'num_touches' in new_annotation_task_dict[ 'config'] and new_annotation_task_dict['config'][ 'num_touches'] == '': del new_annotation_task_dict['config']['num_touches'] if 'name' in params: new_annotation_task_dict['name'] = params['name'] # only allow setting this to True if this annotation_task is in an annotation_task_topic_group # TODO: currently possible to do illegal is_training_task update (with not effect) that creates new task if 'is_training_task' in params and params['is_training_task'] is True: if new_annotation_task_dict[ 'annotation_task_topic_group_id'] is not None: new_annotation_task_dict['is_training_task'] = params[ 'is_training_task'] if 'include_gold_annotations' in params: new_annotation_task_dict['include_gold_annotations'] = params[ 'include_gold_annotations'] if 'is_contributor_task' in params: new_annotation_task_dict['is_contributor_task'] = params[ 'is_contributor_task'] new_annotation_task = AnnotationTask(new_annotation_task_dict) db_session_users.add(new_annotation_task) # n.b. connect the tasks together and mark the old one as inactive original_annotation_task.active_task = new_annotation_task original_annotation_task.status = AnnotationTask.INACTIVE_STATUS db_session_users.add(original_annotation_task) # also deal with any even older annotation tasks and make sure they get their active_task_id updated too for older_annotation_task in \ db_session_users.query(AnnotationTask).filter_by(active_task_id=original_annotation_task.id): older_annotation_task.active_task = new_annotation_task db_session_users.add(older_annotation_task) db_session_users.commit() db_session_users.refresh(new_annotation_task) # update any annotation_task_groups containing original_annotation_task to point to new task all_task_groups = db_session_users.query( AnnotationTaskTopicGroup).all() # get all task groups for group in all_task_groups: if annotation_task_id in group.annotation_task_ids: # update task id list to point to new annotation task id # list is a tuple, so cannot be mutated in-place new_ids = [ id for id in group.annotation_task_ids if id != annotation_task_id ] new_ids.append(new_annotation_task.id) group.annotation_task_ids = new_ids db_session_users.add(group) db_session_users.commit() if 'term_sampling_group_ids' in params: new_term_sampling_ids = params['term_sampling_group_ids'] else: existing_term_sampling_group_ids = [ t[0] for t in db_session_users.query( AnnotationTaskTermSamplingGroup.term_sampling_group_id). filter_by(annotation_task_id=original_annotation_task.id) ] new_term_sampling_ids = existing_term_sampling_group_ids for term_sampling_group_id in new_term_sampling_ids: attsg = AnnotationTaskTermSamplingGroup({ 'annotation_task_id': new_annotation_task.id, 'term_sampling_group_id': term_sampling_group_id }) db_session_users.add(attsg) if len(new_term_sampling_ids) > 0: db_session_users.commit() task_to_return = new_annotation_task # allow basic updates of status/config/name else: # n.b. changing status between active/inactive in effect toggles this task on/off if 'status' in params and params['status'] in VALID_TASK_STATUSES: original_annotation_task.status = params['status'] if 'name' in params: original_annotation_task.name = params['name'] if 'is_contributor_task' in params: original_annotation_task.is_contributor_task = params[ 'is_contributor_task'] # must be done before setting is_training_task due to check in setting is_training_task if 'annotation_task_topic_group_id' in params: original_annotation_task.annotation_task_topic_group_id = params[ 'annotation_task_topic_group_id'] # update is_training_task to be False if no longer in annotation_task_topic_group if original_annotation_task.annotation_task_topic_group_id is None: original_annotation_task.is_training_task = False ## n.b. these all can get updated here in the annotation jobs == 0 use case ## # only allow setting this to True if this annotation_task is in an annotation_task_topic_group if 'is_training_task' in params and params['is_training_task'] is True: if original_annotation_task.annotation_task_topic_group_id is not None: original_annotation_task.is_training_task = params[ 'is_training_task'] if 'include_gold_annotations' in params: original_annotation_task.include_gold_annotations = params[ 'include_gold_annotations'] if 'topics' in params: original_annotation_task.topics = params['topics'] if 'user_ids' in params: original_annotation_task.user_ids = params['user_ids'] if 'config' in params: original_annotation_task.config = merge_two_dicts( original_annotation_task.config, params['config']) if 'num_touches' in original_annotation_task.config and original_annotation_task.config[ 'num_touches'] == '': del original_annotation_task.config['num_touches'] if 'term_sampling_group_ids' in params: new_term_sampling_ids = params['term_sampling_group_ids'] existing_tsgs = db_session_users.query(AnnotationTaskTermSamplingGroup)\ .filter_by(annotation_task_id=original_annotation_task.id).all() existing_tsg_id_map = { t.term_sampling_group_id: t for t in existing_tsgs } removed_ids = [ item for item in existing_tsg_id_map.keys() if item not in new_term_sampling_ids ] for term_sampling_group_id in new_term_sampling_ids: if term_sampling_group_id not in existing_tsg_id_map: attsg = AnnotationTaskTermSamplingGroup({ 'annotation_task_id': original_annotation_task.id, 'term_sampling_group_id': term_sampling_group_id }) db_session_users.add(attsg) for term_sampling_group_id in removed_ids: db_session_users.query( AnnotationTaskTermSamplingGroup).filter_by( annotation_task_id=original_annotation_task.id, term_sampling_group_id=term_sampling_group_id).delete( ) db_session_users.add(original_annotation_task) db_session_users.commit() task_to_return = original_annotation_task task_dict = task_to_return.to_dict() task_dict["old_tasks"] = [ t.to_dict() for t in db_session_users.query(AnnotationTask).filter_by( active_task_id=task_to_return.id) ] term_sampling_group_ids = db_session_users.query(AnnotationTaskTermSamplingGroup.term_sampling_group_id) \ .filter_by(annotation_task_id=task_to_return.id) task_dict["term_sampling_group_ids"] = [ t[0] for t in term_sampling_group_ids ] return {"annotation_task": task_dict}