def resolve_report(self, db: Session, *, user: models.User, db_obj: models.Fact) -> models.Fact: db.query(models.Reported) \ .filter(models.Reported.fact_id == db_obj.fact_id).delete( synchronize_session=False) db.commit() history_in = schemas.HistoryCreate( time=datetime.now(timezone('UTC')).isoformat(), user_id=user.id, fact_id=db_obj.fact_id, log_type=schemas.Log.resolve_report, details={"study_system": user.repetition_model}) crud.history.create(db=db, obj_in=history_in) return db_obj
def undo_mark(self, db: Session, *, db_obj: models.Fact, user: models.User) -> models.Fact: db.query(models.Marked) \ .filter(and_(models.Marked.marked_fact == db_obj, models.Marked.marker == user)).delete(synchronize_session=False) db.commit() history_in = schemas.HistoryCreate( time=datetime.now(timezone('UTC')).isoformat(), user_id=user.id, fact_id=db_obj.fact_id, log_type=schemas.Log.undo_mark, details={"study_system": user.repetition_model}) crud.history.create(db=db, obj_in=history_in) return db_obj
def mark(self, db: Session, *, db_obj: models.Fact, user: models.User) -> models.Fact: now = datetime.now(timezone('UTC')) mark = models.Marked(marker=user, marked_fact=db_obj, date_marked=now) db.add(mark) db.commit() history_in = schemas.HistoryCreate( time=now, user_id=user.id, fact_id=db_obj.fact_id, log_type=schemas.Log.mark, details={"study_system": user.repetition_model}) crud.history.create(db=db, obj_in=history_in) return db_obj
def report(self, db: Session, *, db_obj: models.Fact, user: models.User, suggestion: schemas.FactToReport) -> models.Fact: now = datetime.now(timezone('UTC')) report = models.Reported(reporter=user, reported_fact=db_obj, date_reported=datetime.now(timezone('UTC')), suggestion=suggestion) db.add(report) db.commit() history_in = schemas.HistoryCreate( time=now, user_id=user.id, fact_id=db_obj.fact_id, log_type=schemas.Log.report, details={"study_system": user.repetition_model}) crud.history.create(db=db, obj_in=history_in) return db_obj
def assign_decks( *, db: Session = Depends(deps.get_db), deck_ids: List[int] = Query(...), current_user: models.User = Depends(deps.get_current_active_user), ) -> Any: """ Assign an existing deck to a new user. """ decks = [] for deck_id in deck_ids: deck = crud.deck.get(db=db, id=deck_id) if not deck: raise HTTPException(status_code=404, detail="Deck not found") if deck.public: if deck not in current_user.decks: deck = crud.deck.assign_viewer(db=db, db_obj=deck, user=current_user) decks.append(deck) else: raise HTTPException( status_code=401, detail= "User does not have permission to add one of the specified " "decks") history_in = schemas.HistoryCreate( time=datetime.now(timezone('UTC')).isoformat(), user_id=current_user.id, log_type=schemas.Log.assign_viewer, details={ "study_system": current_user.repetition_model, "decks": deck_ids }) crud.history.create(db=db, obj_in=history_in) return decks
def update_fact( *, fact_in: schemas.FactUpdate, perms: deps.OwnerFactPerms = Depends(), ) -> Any: """ Update a fact. """ details = { "old_fact": perms.fact.__dict__, "fact_update": fact_in.dict(), } fact = crud.fact.update(db=perms.db, db_obj=perms.fact, obj_in=fact_in) history_in = schemas.HistoryCreate(time=datetime.now( timezone('UTC')).isoformat(), user_id=perms.current_user.id, log_type=schemas.Log.update_fact, details=details) crud.history.create(db=perms.db, obj_in=history_in) return fact
def read_facts( db: Session = Depends(deps.get_db), skip: int = 0, limit: int = 100, all: Optional[str] = None, text: Optional[str] = None, answer: Optional[str] = None, category: Optional[str] = None, identifier: Optional[str] = None, deck_ids: Optional[List[int]] = Query(None), deck_id: Optional[int] = None, marked: Optional[bool] = None, suspended: Optional[bool] = None, reported: Optional[bool] = None, current_user: models.User = Depends(deps.get_current_active_user), ) -> Any: """ Retrieve facts. """ if limit > 1000: raise HTTPException( status_code=445, detail="Too many facts requested. Please limit to <1000 facts.") if deck_ids is not None and 2 in deck_ids: raise HTTPException(status_code=557, detail="This deck is currently unavailable") if suspended and reported: studyable = True else: studyable = False search = schemas.FactSearch(all=all, text=text, answer=answer, category=category, identifier=identifier, deck_ids=deck_ids, deck_id=deck_id, marked=marked, suspended=suspended, reported=reported, studyable=studyable, skip=skip, limit=limit) query = crud.fact.build_facts_query(db=db, user=current_user, filters=search) facts = crud.fact.get_eligible_facts(query=query, skip=skip, limit=limit) total = len(facts) if total == limit: total = crud.fact.count_eligible_facts(query=query) begin_overall_start = time.time() new_facts: List[schemas.Fact] = [] for fact in facts: new_facts.append( crud.fact.get_schema_with_perm(db_obj=fact, user=current_user)) overall_end_time = time.time() overall_total_time = overall_end_time - begin_overall_start logger.info("permissions: " + str(overall_total_time)) fact_browser = schemas.FactBrowse(facts=new_facts, total=total) details = search.dict() details["study_system"] = current_user.repetition_model history_in = schemas.HistoryCreate(time=datetime.now( timezone('UTC')).isoformat(), user_id=current_user.id, log_type=schemas.Log.browser, details=details) crud.history.create(db=db, obj_in=history_in) return fact_browser
def update_schedule( self, db: Session, *, user: models.User, db_obj: models.Fact, schedule: schemas.Schedule ) -> Union[bool, requests.exceptions.RequestException, json.decoder.JSONDecodeError]: try: response = schedule.response date_studied = datetime.now(timezone('UTC')).isoformat() details = { "study_system": user.repetition_model, "typed": schedule.typed, "response": schedule.response, "debug_id": schedule.debug_id, } if schedule.elapsed_seconds_text: details["elapsed_seconds_text"] = schedule.elapsed_seconds_text details[ "elapsed_seconds_answer"] = schedule.elapsed_seconds_answer else: details[ "elapsed_milliseconds_text"] = schedule.elapsed_milliseconds_text details[ "elapsed_milliseconds_answer"] = schedule.elapsed_milliseconds_answer history_in = schemas.HistoryCreate(time=date_studied, user_id=user.id, fact_id=db_obj.fact_id, log_type=schemas.Log.study, details=details) history = crud.history.create(db=db, obj_in=history_in) payload_update = [ schemas.KarlFactUpdate( text=db_obj.text, user_id=user.id, repetition_model=user.repetition_model, fact_id=db_obj.fact_id, history_id=history.id, category=db_obj.category, deck_name=db_obj.deck.title, deck_id=db_obj.deck_id, answer=db_obj.answer, env=settings.ENVIRONMENT, elapsed_seconds_text=schedule.elapsed_seconds_text, elapsed_seconds_answer=schedule.elapsed_seconds_answer, elapsed_milliseconds_text=schedule. elapsed_milliseconds_text, elapsed_milliseconds_answer=schedule. elapsed_milliseconds_answer, label=response, debug_id=schedule.debug_id).dict(exclude_unset=True) ] logger.info(payload_update[0]) request = requests.post(settings.INTERFACE + "api/karl/update", json=payload_update) logger.info(request.request) if 200 <= request.status_code < 300: return True else: return False except requests.exceptions.RequestException as e: capture_exception(e) return e except json.decoder.JSONDecodeError as e: capture_exception(e) return e
def get_study_set( self, db: Session, *, user: models.User, deck_ids: List[int] = None, return_limit: Optional[int] = None, send_limit: Optional[int] = 300, ) -> Union[List[schemas.Fact], requests.exceptions.RequestException, json.decoder.JSONDecodeError]: filters = schemas.FactSearch(deck_ids=deck_ids, limit=send_limit, randomize=True, studyable=True) query = crud.fact.build_facts_query(db=db, user=user, filters=filters) eligible_facts = self.get_eligible_facts(query=query, limit=send_limit) if not eligible_facts: return [] karl_list = [] karl_list_start = time.time() for each_card in eligible_facts: karl_list.append( schemas.KarlFact(text=each_card.text, answer=each_card.answer, category=each_card.category, deck_name=each_card.deck.title, deck_id=each_card.deck_id, user_id=user.id, fact_id=each_card.fact_id, repetition_model=user.repetition_model, env=settings.ENVIRONMENT).dict()) eligible_fact_time = time.time() - karl_list_start logger.info("eligible fact time: " + str(eligible_fact_time)) karl_query_start = time.time() try: scheduler_response = requests.post(settings.INTERFACE + "api/karl/schedule", json=karl_list) response_json = scheduler_response.json() card_order = response_json["order"] rationale = response_json["rationale"] debug_id = response_json["debug_id"] query_time = time.time() - karl_query_start logger.info(scheduler_response.request) logger.info("query time: " + str(query_time)) facts = [] if rationale != "<p>no fact received</p>": reordered_karl_list = [karl_list[x] for x in card_order] if return_limit: for _, each_karl_fact in zip(range(return_limit), reordered_karl_list): retrieved_fact = self.get( db=db, id=int(each_karl_fact["fact_id"])) fact_schema = self.get_schema_with_perm( db_obj=retrieved_fact, user=user) fact_schema.rationale = rationale fact_schema.debug_id = debug_id if retrieved_fact: fact_schema.marked = True if user in retrieved_fact.markers else False facts.append(fact_schema) else: for each_karl_fact in reordered_karl_list: retrieved_fact = self.get( db=db, id=int(each_karl_fact["fact_id"])) fact_schema = self.get_schema_with_perm( db_obj=retrieved_fact, user=user) fact_schema.rationale = rationale # MARK: maybe not the most efficient solution for determining if user has marked a fact if retrieved_fact: fact_schema.marked = retrieved_fact.is_marked(user) facts.append(fact_schema) details = { "study_system": user.repetition_model, "first_fact": facts[0] if len(facts) != 0 else "empty", "eligible_fact_time": query_time, "scheduler_query_time": eligible_fact_time, "debug_id": debug_id, } history_in = schemas.HistoryCreate(time=datetime.now( timezone('UTC')).isoformat(), user_id=user.id, log_type=schemas.Log.get_facts, details=details) crud.history.create(db=db, obj_in=history_in) return facts except requests.exceptions.RequestException as e: capture_exception(e) return e except json.decoder.JSONDecodeError as e: capture_exception(e) return e
async def upload_picture(request: Request, file: UploadFile = File(...), db: Session = Depends(get_db)): """ Upload a user picture. IF picture is valid, the face shape is detected, a new record is added to the pictures and history table, and the picture file is saved :param request: :param file: Selected binary picture file to be uploaded :param db: db session instance :return: Json response that contains the picture information, the detected face shape and the history record """ user_data = get_user_data_from_token(request) if user_data: save_path = PICTURE_UPLOAD_FOLDER file_name = picture_service.save_picture(file, save_path) face_detected = picture_service.detect_face(file_name, save_path) if face_detected is True: # try to find face landmark points face_landmarks = picture_service.detect_face_landmarks( file_name, save_path) if face_landmarks is None: raise HTTPException(status_code=422, detail="No face landmarks detected") else: picture_info = picture_service.get_picture_info( save_path, file_name) # detect face_shape face_shape = picture_service.detect_face_shape( face_landmarks, file_name, save_path) if face_shape is None: raise HTTPException( status_code=422, detail="Face shape could not be detected") new_picture = models.Picture(file_name=picture_info.file_name, file_path=picture_info.file_path, file_size=picture_info.file_size, height=picture_info.height, width=picture_info.width) orig_pic = picture_actions.add_picture(db=db, picture=new_picture) # Testing how to create a history record after detecting a face shape # create History instance # parse face shape string to int face_shape_id = face_shape_service.parse_face_shape( face_shape[0]) face_shape_detected: models.FaceShape = face_shape_actions.get_face_shape( db=db, face_shape_id=face_shape_id) user_id = user_data['id'] new_history: schemas.HistoryCreate = schemas.HistoryCreate( picture_id=orig_pic.id, original_picture_id=orig_pic.id, face_shape_id=face_shape_id, user_id=user_id) new_history_entry: models.History = history_actions.add_history( db=db, history=new_history) results = picture_actions.read_picture_by_file_name( db=db, file_name=new_picture.file_name, limit=1) return { 'picture': results[0], 'face_shape': face_shape[0], 'history_entry': new_history_entry } else: raise HTTPException(status_code=422, detail="No face detected") else: raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid credentials")
async def change_hairstyle(user_picture_id: Optional[int] = None, model_picture_id: Optional[int] = None, user_picture_file_name: Optional[str] = None, model_picture_file_name: Optional[str] = None, db: Session = Depends(get_db)): """ Change user's picture hairstyle based on the selected model picture hairstyle, add record to pictures table and history table :param user_picture_id: The ID of the user picture to be modified :param model_picture_id: the ID of the model picture hairstyle :param user_picture_file_name: the user picture filename :param model_picture_file_name: the model picture filename :param db: db session instance :return: history record """ user_picture: Union[models.Picture, None] = None model_picture: Union[models.ModelPicture, None] = None if user_picture_id and model_picture_id: user_picture = picture_actions.read_picture_by_id( db, picture_id=user_picture_id) model_picture = model_picture_actions.read_model_picture_by_id( db, model_picture_id=model_picture_id) elif user_picture_file_name and model_picture_file_name: picture_results: List[ models.Picture] = picture_actions.read_picture_by_file_name( db=db, file_name=user_picture_file_name, limit=1) if len(picture_results): user_picture = picture_results[0] model_pictures_results = model_picture_actions.read_model_picture_by_file_name( db=db, file_name=model_picture_file_name, limit=1) if len(model_pictures_results): model_picture = model_pictures_results[0] else: raise HTTPException( status_code=400, detail= 'Please enter (user_picture_id AND model_picture_id) OR (user_picture_filename AND ' 'model_picture_filename)') if user_picture and model_picture: try: # get original picture from history based on user_picture pic_history: List[ models.History] = history_actions.get_picture_history( db=db, filename=user_picture.file_name) original_pic_id: int = pic_history[0].original_picture_id original_pic: models.Picture = picture_actions.read_picture_by_id( db=db, picture_id=original_pic_id) # apply hair transfer picture_info = picture_service.change_hairstyle( user_picture=original_pic, model_picture=model_picture) print(picture_info) # create new picture and add to db new_picture: schemas.PictureCreate = schemas.PictureCreate( file_name=picture_info.file_name, file_path=picture_info.file_path, file_size=picture_info.file_size, height=picture_info.height, width=picture_info.width) mod_pic = picture_actions.add_picture(db=db, picture=new_picture) user = history_actions.get_user_id_from_picture_id( db=db, picture_id=user_picture.id) if user: # get latest user history entry to add face_shape_id history: List[ models.History] = history_actions.get_user_history( db=db, user_id=user.id) if len(history): latest_entry = history[-1] model_picture: models.ModelPicture = model_picture_actions.read_model_picture_by_id( db=db, model_picture_id=model_picture.id) if model_picture: new_history: schemas.HistoryCreate = schemas.HistoryCreate( picture_id=mod_pic.id, original_picture_id=latest_entry. original_picture_id, previous_picture_id=latest_entry.picture_id, # hair_colour_id=latest_entry.hair_colour_id, hair_style_id=model_picture.hair_style_id, face_shape_id=latest_entry.face_shape_id, user_id=user.id) history_entry = history_actions.add_history( db=db, history=new_history) new_hair_style = hair_style_actions.get_hair_style( db=db, hair_style_id=history_entry.hair_style_id) current_picture = picture_actions.read_picture_by_id( db=db, picture_id=history_entry.picture_id) original_picture = picture_actions.read_picture_by_id( db=db, picture_id=history_entry.original_picture_id) return { "history_entry": history_entry, "hair_style": new_hair_style, "current_picture": current_picture, "original_picture": original_picture } raise HTTPException(status_code=404, detail='Model picture not found') raise HTTPException( status_code=404, detail= 'No history associated with this user was found. Please upload a picture ' 'first.') raise HTTPException( status_code=404, detail='No user associated with this picture was found') except Exception as e: raise HTTPException( status_code=422, detail= f'Could not apply hair style swap. Please try another image. Exception: {e}' ) raise HTTPException( status_code=404, detail= 'No user picture or model picture associated with these IDs were found' )
async def change_hair_colour(picture_id: int, colour: str, r: int, b: int, g: int, db: Session = Depends(get_db)): """Applies changes to hair colour based on a base colour name (e.g. hot pink) and RGB values, which vary by lightness :param picture_id: ID of the picture to be processed :param db: db session instance :param colour: Base hair colour name :param r: Red channel of the colour :param g: Green channel of the colour :param b: Blue channel of the colour :returns: New picture object and history entry reflecting changes """ selected_picture: Union[models.Picture, None] = None if picture_id: # get latest entry from history where hair_colour_id == None first_history_entry = history_actions.get_first_picture_history_by_id( db=db, picture_id=picture_id) if first_history_entry: pic_history = history_actions.get_picture_history_by_original_picture_id( db=db, original_picture_id=first_history_entry.original_picture_id) if len(pic_history): latest_entry_no_colour = list( filter(lambda h: not h.hair_colour_id, pic_history))[-1] selected_picture = picture_actions.read_picture_by_id( db=db, picture_id=latest_entry_no_colour.picture_id) else: raise HTTPException(status_code=400, detail='Picture ID not found') if selected_picture: # apply hair colour try: picture_info = picture_service.change_hair_colour_RGB( file_name=selected_picture.file_name, r=r, b=b, g=g, file_path=selected_picture.file_path) # create new picture and add to db new_picture = models.Picture(file_name=picture_info.file_name, file_path=picture_info.file_path, file_size=picture_info.file_size, height=picture_info.height, width=picture_info.width) mod_pic = picture_actions.add_picture(db=db, picture=new_picture) # selected_picture = picture_actions.read_picture_by_id(db, picture_id=picture_id) # print(selected_picture.file_name) # print(selected_picture.file_path) # apply selected colour # picture_info = picture_service.change_hair_colour_RGB(file_name=selected_picture.file_name, selected_colour=colour, # r=r, g=g, b=b, # file_path=selected_picture.file_path) # print(picture_info) # create new picture and add to db # new_picture = models.Picture(file_name=picture_info.file_name, file_path=picture_info.file_path, # file_size=picture_info.file_size, height=picture_info.height, width=picture_info.width) # mod_pic = picture_actions.add_picture(db=db, picture=new_picture) # fake user_id user: models.User = history_actions.get_user_id_from_picture_id( db=db, picture_id=picture_id) if user: hair_colour_results: List[ models.HairColour] = hair_colour_actions.get_hair_colours( db=db, search=colour, limit=1) if len(hair_colour_results): hair_colour = hair_colour_results[0] # get latest history entry to extract face_shape_id and hair_style_id user_history: List[ models.History] = history_actions.get_user_history( db=db, user_id=user.id) latest_history_entry: models.History = user_history[-1] new_history: schemas.HistoryCreate = schemas.HistoryCreate( picture_id=mod_pic.id, original_picture_id=latest_history_entry. original_picture_id, previous_picture_id=selected_picture.id, hair_colour_id=hair_colour.id, face_shape_id=latest_history_entry.face_shape_id, hair_style_id=latest_history_entry.hair_style_id, user_id=user.id) history_entry: models.History = history_actions.add_history( db=db, history=new_history) hair_colour_entry: models.HairColour = hair_colour_actions.get_hair_colour_by_id( db=db, hair_colour_id=hair_colour.id) # get new picture with modified hair colour new_pic: models.Picture = picture_actions.read_picture_by_id( db=db, picture_id=history_entry.picture_id) return { 'history_entry': history_entry, 'picture': new_pic, 'hair_colour': hair_colour_entry } raise HTTPException( status_code=404, detail= 'No hair colour record associated with this colour name was found' ) except Exception as ex: print(ex) raise HTTPException( status_code=422, detail= 'Could not change hair colour. Please try a different picture. Exception: ' + ex) raise HTTPException(status_code=404, detail='Selected picture not found') raise HTTPException( status_code=404, detail='No user associated with this picture ID was found')