def update_topics(lesson_id): """update or add lesson topics accepts {'progress': [topics in progress], 'finished': [finished topics]}""" data = flask.request.get_json() FINISHED_KEY = "finished" lesson = Appointment.query.filter( and_( Appointment.type == AppointmentType.LESSON.value, Appointment.id == lesson_id, ) ).first() if not lesson: raise RouteError("Lesson does not exist.") if not lesson.student: raise RouteError("Lesson must have a student assigned.") appended_ids = [] for key, topic_ids in data.get("topics").items(): for topic_id in topic_ids: if not Topic.get_by_id(topic_id): raise RouteError("Topic does not exist.") if topic_id in appended_ids: # we don't want the same topic twice continue is_finished = True if key == FINISHED_KEY else False # existing_lesson_topic = lesson.topics.filter_by(topic_id=topic_id).first() # if existing_lesson_topic: # if is_finished: # existing_lesson_topic.update(is_finished=is_finished) # continue lesson_topic = LessonTopic(is_finished=is_finished, topic_id=topic_id) lesson.topics.append(lesson_topic) appended_ids.append(topic_id) lesson.save() return {"data": lesson.to_dict()}, 201
def create_report(): post_data = flask.request.get_json() try: report_type = ReportType[post_data.get("report_type")] except KeyError: raise RouteError("Report type was not found.") car = current_user.teacher.cars.filter_by(id=post_data.get("car")).first() dates = dict() if report_type.name in Report.DATES_REQUIRED: dates["since"] = post_data.get("since") dates["until"] = post_data.get("until") try: dates["since"] = datetime.strptime( dates["since"], WORKDAY_DATE_FORMAT).replace(second=0, microsecond=0) dates["until"] = datetime.strptime( dates["until"], WORKDAY_DATE_FORMAT).replace(second=0, microsecond=0) except (ValueError, TypeError): raise RouteError("Dates are not valid.") report = Report.create(report_type=report_type.value, teacher=current_user.teacher, car=car, **dates) return {"data": report.to_dict()}
def register_car(): """register a new car for a teacher""" data = flask.request.get_json() number = data.get("number") if not number: raise RouteError("Car number is required.") # if this number already exist, raise error exists = current_user.teacher.cars.filter_by(number=number).first() if exists: raise RouteError("Car already exists.") try: type_ = CarType[data.get("type", "")] except KeyError: type_ = CarType.manual color = data.get("color") car = Car.create( name=data.get("name"), type=type_.value, number=number, teacher=current_user.teacher, color=color[:6] if color else None, ) return {"data": car.to_dict()}, 201
def handle_oauth(network: Type[SocialNetwork], access_token: str): if not access_token: raise RouteError("No token received.") network_user_id = network.token_metadata(access_token) oauth = create_or_get_oauth(network.network_name, network_user_id, access_token) user = oauth.user if not user: profile = network.profile(network_user_id, access_token) logger.debug(f"profile of user is {profile}") if not profile.get("email"): raise RouteError("Can not get email from user.") try: image_url = profile["picture"]["data"].get("url") logger.info(f"Uploading {image_url} to Cloudinary...") image = upload(image_url)["public_id"] except Exception: image = "" # Create a new local user account for this user user = User(email=profile.get("email"), name=profile.get("name"), image=image).save() oauth.user = user oauth.save() logger.debug(f"creating new user {user}") exchange_token = user.encode_exchange_token().decode() logger.debug("Logging out of session") logout_user() return {"token": exchange_token}
def update_kilometer(id_): """update kilometer for a specific date""" car = current_user.teacher.cars.filter_by(id=id_).first() if not car: raise RouteError("Car does not exist.") data = flask.request.get_json() try: date = datetime.strptime(data.get("date"), WORKDAY_DATE_FORMAT) except (ValueError, TypeError): raise RouteError("Date is not valid.") # if this date exist, delete it first exists = current_user.teacher.kilometers.filter_by(date=date).first() if exists: exists.delete() start, end = data.get("start"), data.get("end") if not start or not end: raise RouteError("All kilometer distances are required.") if end < start: raise RouteError("Ending value must be bigger than starting value.") kilometer = Kilometer.create( date=date, personal=data.get("personal", 0), start_of_day=start, end_of_day=end, car=car, teacher=current_user.teacher, ) return {"data": kilometer.to_dict()}, 201
def create_ezcount_user(): # https://docs.google.com/document/d/1me6u9CpJtydTIEdMkY3OH1dresZkrPRCK0_xw5Rn0Do/edit# teacher = current_user.teacher if not teacher.crn: return if teacher.invoice_api_key: raise RouteError("Teacher already has an invoice account.") api_key = flask.current_app.config.get("RECEIPTS_API_KEY") payload = { "api_key": api_key, "api_email": RECEIPTS_DEVELOPER_EMAIL, "developer_email": RECEIPTS_DEVELOPER_EMAIL, "create_signature": 1, "company_crn": teacher.crn, "company_email": current_user.email, "user_key": str(current_user.id), "company_name": current_user.name, "company_type": 1, } resp = requests.post(RECEIPT_URL + "api/user/create", json=payload) resp_json = resp.json() if resp_json["success"]: teacher.update(invoice_api_key=resp_json["u_api_key"], invoice_api_uid=resp_json["u_uuid"]) return {"message": "EZCount user created successfully."} raise RouteError(resp_json["errMsg"])
def approve_lesson(lesson_id): lesson = current_user.teacher.lessons.filter_by(id=lesson_id).first() if not lesson: raise RouteError("Lesson does not exist", 404) # check if there isn't another lesson at the same time same_time_lesson = Appointment.query.filter( Appointment.approved_lessons_filter( Appointment.date == lesson.date, Appointment.id != lesson.id ) ).first() if same_time_lesson: raise RouteError("There is another lesson at the same time.") lesson.update(is_approved=True) if lesson.student.user.firebase_token: logger.debug(f"sending fcm for lesson approval") try: FCM.notify( token=lesson.student.user.firebase_token, title=gettext("Lesson Approved"), body=gettext( "Lesson at %(date)s has been approved!", date=format_datetime( lesson.date, locale=LOCALE, format="short", tzinfo=timezone(TIMEZONE), ), ), ) except NotificationError: pass return {"message": "Lesson approved."}
def delete_topic(topic_id): if not current_user.is_admin: raise RouteError("Admin required.", 401) topic = Topic.get_by_id(topic_id) if not topic: raise RouteError("Topic does not exist", 404) topic.delete() return {"message": "Topic deleted."}
def get_data(data: dict, user: User, appointment: Optional[Appointment] = None) -> dict: """get request data and a specific user - we need the user because we are not decorated in login_required here returns dict of new lesson or edited lesson""" try: date = datetime.strptime(data.get("date"), DATE_FORMAT).replace( second=0, microsecond=0 ) except (ValueError, TypeError): raise RouteError("Date is not valid.") if appointment: type_ = appointment.type else: type_ = None duration = data.get("duration") if not duration: raise RouteError("Duration is required.") duration = int(duration) if user.student: student = user.student teacher = user.student.teacher type_ = type_ or AppointmentType.LESSON if date < datetime.utcnow(): # trying to add a new lesson in the past?? raise RouteError("Date is not valid.") check_available_hours_for_student(date, student, appointment, duration) elif user.teacher: type_ = getattr( AppointmentType, data.get("type", "").upper(), type_ or AppointmentType.LESSON, ) handle_teacher_hours(user.teacher, date, duration, type_, appointment) teacher = user.teacher student = Student.get_by_id(data.get("student_id")) if not student: raise RouteError("Student does not exist.") else: raise RouteError("Not authorized.", 401) meetup, dropoff = handle_places(data, student, appointment) try: price = int(data.get("price", "")) except ValueError: price = None return { "date": date, "meetup_place": meetup, "dropoff_place": dropoff, "student": student, "teacher": teacher, "duration": duration, "price": price, "comments": data.get("comments"), "is_approved": True if user.teacher else False, "type": type_, }
def deactivate(student_id): student = Student.get_by_id(student_id) if not student: raise RouteError("Student does not exist.", 404) if student.teacher != current_user.teacher: raise RouteError("Not authorized.", 401) student.update(is_active=False) return {"data": student.to_dict()}
def delete_student(student_id): student = Student.get_by_id(student_id) if not student: raise RouteError("Student does not exist.", 404) if student.appointments.first(): # if any lessons exist raise RouteError("Can't delete student.") if current_user != student.teacher.user: raise RouteError("Not authorized.", 401) student.delete() return {"message": "Student deleted."}
def appointment(id_): appointment = Appointment.get_by_id(id_) if not appointment: raise RouteError("Appointment does not exist.") if current_user.id not in ( appointment.student.user.id, appointment.teacher.user.id, ): raise RouteError("You are not allowed to view this appointment.", 401) return {"data": appointment.to_dict()}
def direct_login(): data = flask.request.get_json() email = data.get("email") if not email: raise RouteError("Email is required.") email = email.lower() user = User.query.filter_by(email=email).first() # Try to authenticate the found user using their password if user and user.check_password(data.get("password")): tokens = user.generate_tokens() return dict(**tokens, **{"user": user.to_dict()}) # User does not exist. Therefore, we return an error message raise RouteError("Invalid email or password.", 401)
def approve(student_id): student = Student.get_by_id(student_id) if not student: raise RouteError("Student does not exist.", 404) if current_user != student.creator and ( current_user.teacher == student.teacher or current_user.student == student or current_user.is_admin): # only allow approving for the user himself or # the teacher that is requested. don't allow for the requester to approve. student.update(is_approved=True) return {"data": student.to_dict()} raise RouteError("Not authorized.", 401)
def handle_teacher_hours( teacher: Teacher, date: datetime, duration: int, type_: Optional[AppointmentType], appointment: Optional[Appointment], ): """check if there are existing lessons in the date given. If so - is test? - delete all existing lessons. NO - raise RouteError""" # check if there's another lesson that ends or starts within this time end_date = date + timedelta(minutes=duration) existing_lessons = Appointment.query.filter_by(teacher=teacher).filter( Appointment.appointments_between(date, end_date) ) if appointment: existing_lessons = existing_lessons.filter(Appointment.id != appointment.id) existing_lessons = existing_lessons.all() logger.debug(f"For {date}, found existing lessons: {existing_lessons}") if existing_lessons: if type_ == AppointmentType.LESSON or date < datetime.utcnow(): raise RouteError("This hour is not available.") # delete all lessons and send FCMs for existing_appointment in existing_lessons: if existing_appointment.type == AppointmentType.LESSON: delete_appointment_with_fcm(existing_appointment)
def show_report(uuid): REPORTS = { "students": lambda report: report.teacher.students.filter_by(is_active=True).join( User, Student.user).order_by(User.name.asc()), "lessons": lambda report: report.teacher.lessons.filter( and_( Appointment.is_approved == True, Appointment.date < report.until, Appointment.date > report.since, )), "kilometers": lambda report: report.teacher.kilometers.filter( and_( Kilometer.date < report.until, Kilometer.date > report.since, Kilometer.car == report.car, )), } report = Report.query.filter_by(uuid=uuid).first() if not report: raise RouteError("Report was not found.") report_data = REPORTS.get(report.report_type.name) html = flask.render_template( f"reports/{report.report_type.name}.html", data=report_data(report).all(), teacher=report.teacher, report=report, ) return render_pdf(HTML(string=html))
def delete_car(id_): car = current_user.teacher.cars.filter_by(id=id_).first() if not car: raise RouteError("Car does not exist.") car.delete() return {"message": "Car deleted."}
def cars(teacher_id): teacher = Teacher.get_by_id(teacher_id) if not teacher: raise RouteError("Teacher not found.") return { "data": [car.to_dict() for car in Car.query.filter_by(teacher=teacher).all()] }
def edit_work_day(day_id): day = current_user.teacher.work_days.filter_by(id=day_id).first() if not day: raise RouteError("Day does not exist", 404) data = flask.request.get_json() from_hour = data.get("from_hour", day.from_hour) to_hour = data.get("to_hour", day.to_hour) day.update(from_hour=from_hour, to_hour=to_hour) return {"message": "Day updated successfully."}
def refresh_token(): args = flask.request.get_json() if not args.get("refresh_token"): raise RouteError("INAVLID_REFRESH_TOKEN") payload = User.decode_token(args["refresh_token"]) if payload["scope"] != TokenScope.REFRESH.value: raise TokenError("INAVLID_REFRESH_TOKEN") user = User.from_payload(payload) return {"auth_token": user.encode_auth_token().decode()}
def appointments(): user = current_user.teacher if not current_user.teacher: user = current_user.student try: return user.filter_appointments(flask.request.args) except ValueError: raise RouteError("Wrong parameters passed.")
def search(): try: query = User.query.filter( and_(User.teacher == None, User.student == None)) return User.filter_and_sort(flask.request.args, query=query, with_pagination=True) except ValueError: raise RouteError("Wrong parameters passed.")
def add_receipt(payment_id): # https://docs.google.com/document/d/1_kSH5xViiZi5Y1tZtWpNrkKiq4Htym7V23TuhL7KlSU/edit# payment = Payment.get_by_id(payment_id) if not payment or payment.teacher != current_user.teacher: raise RouteError("Payment not found.", 404) if not payment.teacher.invoice_api_key: raise RouteError("Teacher does not have an invoice account.") api_key = flask.current_app.config.get("RECEIPTS_API_KEY") payload = { "api_key": payment.teacher.invoice_api_key, "developer_email": RECEIPTS_DEVELOPER_EMAIL, "created_by_api_key": api_key, "transaction_id": payment.id, "type": 320, "customer_name": payment.student.user.name, "customer_email": payment.student.user.email, "customer_crn": payment.crn, "item": { 1: { "details": payment.details, "amount": "1", "price": payment.amount, "price_inc_vat": 1, # this price include the VAT } }, "payment": { 1: { "payment_type": payment.payment_type.value, "payment": payment.amount } }, "price_total": payment.amount, # /*THIS IS A MUST ONLY IN INVOICE RECIEPT*/ } resp = requests.post(RECEIPT_URL + "api/createDoc", json=payload) resp_json = resp.json() if resp_json["success"]: payment.update(pdf_link=resp_json["pdf_link"]) return {"pdf_link": resp_json["pdf_link"]} raise RouteError(resp_json["errMsg"])
def validate_inputs(data, required=None) -> Tuple: """required is a list. if not passed - all fields are required""" if required is None: required = ["name", "area", "password", "email"] email: str = data.get("email") if email: email = email.lower() name: str = data.get("name") area: str = data.get("area") password: str = data.get("password") phone: str = data.get("phone") for var in required: if not vars()[var]: raise RouteError(f"{var.capitalize()} is required.") if var == "email": if not re.match(r"[^@]+@[^@]+\.[^@]+", email): raise RouteError("Email is not valid.") return (name, area, email, password, phone)
def make_student(): data = flask.request.args user = current_user teacher = Teacher.get_by_id(data.get("teacher_id")) if current_user.teacher: user = User.get_by_id(data.get("user_id")) teacher = current_user.teacher if not user: raise RouteError("User was not found.", 401) if user.teacher or user.student: raise RouteError("User is already a student or a teacher.") if not teacher: raise RouteError("Teacher was not found.") try: price = int(data.get("price", "")) except ValueError: price = None student = Student.create(user=user, teacher=teacher, creator=current_user, price=price) # send notification user_to_send_to = student.user body_text = gettext("%(teacher)s added you as a student!", teacher=teacher.user.name) if student.creator == user_to_send_to: user_to_send_to = teacher.user body_text = gettext("%(student)s added you as a teacher!", student=student.user.name) if user_to_send_to.firebase_token: logger.debug(f"sending fcm to {user_to_send_to}") try: FCM.notify( token=user_to_send_to.firebase_token, title=gettext("Join Request"), body=body_text, ) except: pass return {"data": student.to_dict()}, 201
def payments(): """endpoint to return filtered payments""" user = current_user.teacher if not current_user.teacher: user = current_user.student try: return user.filter_payments(flask.request.args) except ValueError: raise RouteError("Wrong parameters passed.")
def make_teacher(): data = flask.request.get_json() if not current_user or current_user.student or current_user.teacher: raise RouteError("User was not found.") price = data.get("price") if not price: raise RouteError("Empty fields.") if price <= 0: raise RouteError("Price must be above 0.") teacher = Teacher.create( user=current_user, price=price, lesson_duration=data.get("lesson_duration"), crn=data.get("crn"), ) return {"data": teacher.to_dict()}, 201
def delete_appointment(id_): try: appointments = current_user.teacher.appointments is_student = False except AttributeError: # a student appointments = current_user.student.appointments is_student = True appointment = appointments.filter_by(id=id_).first() if not appointment: raise RouteError("Appointment does not exist.") # if the appointment is in the past / in less than 24 hours, student can not cancel if is_student and (appointment.date < datetime.utcnow() + timedelta(hours=24)): raise RouteError( "This appointment is either in the past or in less than 24 hours." ) delete_appointment_with_fcm(appointment) return {"message": "Appointment deleted successfully."}
def new_topic(): if not current_user.is_admin: raise RouteError("Admin required.", 401) data = flask.request.get_json() topic = Topic.create( title=data.get("title"), min_lesson_number=data.get("min_lesson_number"), max_lesson_number=data.get("max_lesson_number"), ) return {"data": topic.to_dict()}, 201
def work_days(): """ return work days with filter - only on a specific date, or with no date at all""" try: return { "data": [ day.to_dict() for day in current_user.teacher.filter_work_days( flask.request.args) ] } except ValueError: raise RouteError("Wrong parameters passed.")