Example #1
0
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
Example #2
0
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()}
Example #3
0
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
Example #4
0
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}
Example #5
0
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
Example #6
0
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"])
Example #7
0
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."}
Example #8
0
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."}
Example #9
0
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_,
    }
Example #10
0
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()}
Example #11
0
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."}
Example #12
0
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()}
Example #13
0
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)
Example #14
0
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)
Example #15
0
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)
Example #16
0
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))
Example #17
0
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."}
Example #18
0
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()]
    }
Example #19
0
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."}
Example #20
0
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()}
Example #21
0
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.")
Example #22
0
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.")
Example #23
0
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"])
Example #24
0
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)
Example #25
0
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
Example #26
0
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.")
Example #27
0
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
Example #28
0
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."}
Example #29
0
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
Example #30
0
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.")