示例#1
0
def create_thread_ndb(actor_id, recipients, text, subject=''):
    """Creates a new thread with given recipients, body text, and subject.

    TODO(kanat): check if recipient is a valid account.

    :param actor_id: (int) ID of the account acting.
    :param recipients: (list) list of account IDs in the thread.
    :param text: (str) body text of the initial message.
    :param subject: (str) optional subject line of the thread.
    :return: (kinds.messages.Thread)
    """
    asserts.valid_id_type(actor_id)
    asserts.not_empty(recipients)
    asserts.type_of(text, basestring)
    asserts.type_of(subject, basestring)

    thd = Thread()
    for account_id in set(recipients):
        if account_id != actor_id:
            thd.add_member(account_id)
    if not len(thd.members):
        raise exp.BadRequestExp('Recipients not specified.')
    thd.add_member(actor_id)
    thd.put()
    # Populate MessageDto.
    message_dto = MessageDto()
    message_dto.thread_id = thd.id
    message_dto.text = text
    send_ndb(actor_id, message_dto)
    return thd
示例#2
0
def select_applicant(actor_id, job_id, caregiver_id):
    """Select an applicant for the job.

    TODO(kanat): Notify selected applicant.

    :param actor_id: (int) ID of the account acting.
    :param caregiver_id: (int) ID of the job applicant's account.
    :return: (None)
    """
    asserts.valid_id_type(caregiver_id)
    _assert_job_owner(actor_id, job_id)

    job = get_by_id_ndb(job_id)
    if job.status != JobStatus.Open:
        raise exp.BadRequestExp('Job is not open')
    if actor_id == caregiver_id:
        raise exp.BadRequestExp('Can\'t select yourself.')
    apps = _get_applicants(job_id)
    applicant = apps.find_applicant(caregiver_id)
    if applicant is None:
        raise exp.NotFoundExp('Applicant not found.')
    # Set up job invoice.
    invoice = JobInvoice(job_id=job.id,
                         start_date=job.start_date,
                         end_date=job.end_date)
    invoice.put()
    apps.selected = applicant
    job.status = JobStatus.Accepted
    job.invoice_id = invoice.id
    ndb.put_multi([apps, job])
示例#3
0
def send_request(actor_id, to_id):
    """Sends a request from one account to another.

    TODO(kanat): Send email notification.

    :param actor_id: (int) ID of the account sending.
    :param to_id: (int) ID of the account receiving.
    :return: (kinds.connections.ConnRequest)
        Connection request that was created.
    """
    asserts.valid_id_type(actor_id)
    asserts.valid_id_type(to_id)

    if actor_id == to_id:
        raise exp.BadRequestExp('Cannot send a request to self.')
    if not services.accounts.account_by_id(to_id):
        logging.warning('to_id={} does not exist.'.format(to_id))
        raise exp.NotFoundExp('Account does not exist.')
    # Maybe one or the other already sent a request.
    req = (_conn_request_get(from_id=actor_id, to_id=to_id)
           or _conn_request_get(from_id=to_id, to_id=actor_id))
    if req is not None:
        logging.info('Connection request already exists.')
        return
    req = ConnRequest(from_id=actor_id, to_id=to_id)
    req.put()
    return req
示例#4
0
def remove_connection(actor_id, other_id):
    """Removes a connection from the account.

    :param actor_id: (int) ID of the account removing the connection.
    :param other_id: ID of the account to be removed.
    :return: (None)
    """
    asserts.valid_id_type(actor_id)
    asserts.valid_id_type(other_id)

    if not is_connected(actor_id, other_id):
        logging.warning('Trying to remove a connection that doesn\'t exist: '
                        'actor={}, other={}'.format(actor_id, other_id))
        raise exp.NotFoundExp('Not connected.')

    connlistA = _get_connlist(actor_id)
    connlistB = _get_connlist(other_id)
    # Find the connection request.
    req = (_conn_request_get(from_id=actor_id, to_id=other_id)
           or _conn_request_get(from_id=other_id, to_id=actor_id))
    try:
        connlistA.accepted_reqs.remove(req.id)
        connlistB.accepted_reqs.remove(req.id)
    except ValueError:
        pass
    ndb.put_multi([connlistA, connlistB])
    req.key.delete()
示例#5
0
def posted_jobs_ndb(account_id):
    """Returns jobs posted by the given account_id.

    :param account_id: (int) ID of the account.
    :return: (list<kinds.jobs.Job>)
    """
    asserts.valid_id_type(account_id)

    jobs = Job.gql('WHERE account_id = :1', account_id).fetch()
    return jobs
示例#6
0
def applied_jobs_ndb(account_id):
    """Returns jobs for which the given account_id applied.

    :param account_id: (int) ID of the account.
    :return: (list<kinds.jobs.Job>)
    """
    asserts.valid_id_type(account_id)

    apps = JobApps.gql('WHERE applicants.account_id = :1', account_id)
    return _jobs_from_apps(apps)
示例#7
0
def sent_requests(actor_id):
    """Retrieves sent requests.

    :param actor_id: (int) ID of the account.
    :return: (list<kinds.connections.ConnRequest>)
        A list of connection requests.
    """
    asserts.valid_id_type(actor_id)

    reqs = _conn_requests(from_id=actor_id, status=ConnStatus.Pending)
    return reqs
示例#8
0
def is_connected(first_id, second_id):
    """Check whether two accounts are connected.

    :param first_id: (int) ID of the first account.
    :param second_id: (int) ID of the second account.
    :return: (bool) True if connected; False otherwise.
    """
    asserts.valid_id_type(first_id)
    asserts.valid_id_type(second_id)

    connlist = _get_connlist(first_id)
    return second_id in connlist.accepted_ids
示例#9
0
def pending_requests(actor_id):
    """Retrieves pending requests that were sent to an account.

    :param actor_id: (int) ID of the account.
    :return: (list<dto.connections.PendingDto>) A list of connection Dto's.
    """
    asserts.valid_id_type(actor_id)

    reqs = _conn_requests(from_id=actor_id, status=ConnStatus.Pending)
    account_ids = [r.from_id for r in reqs]
    accounts = services.accounts.accounts_by_ids(account_ids)

    return PendingDto.list_from_req_account(reqs, accounts)
示例#10
0
def nearby_jobs_ndb(account_id):
    """Returns jobs posted nearby the give account_id.

    NOTE: For now, just returns the list of all open jobs.

    :param account_id: (int) ID of the account.
    :return: (list<kinds.jobs.Job>)
    """
    asserts.valid_id_type(account_id)

    jobs = Job.gql('WHERE status = :1 ORDER BY updated DESC',
                   JobStatus.Open).fetch(limit=25)
    return jobs
示例#11
0
def my_connections(actor_id):
    """Returns the list of account IDs the account is connected to.

    :param actor_id: (int) ID of the account.
    :return: (list<dto.connections.ConnDto>) A list of account IDs.
    """
    asserts.valid_id_type(actor_id)

    connlist = _get_connlist(actor_id)
    reqs = ndb.get_multi(ConnRequest.ids_to_keys(connlist.accepted_reqs))
    accounts = services.accounts.accounts_by_ids(connlist.accepted_ids)

    return ConnDto.list_from_req_account(reqs, accounts)
示例#12
0
def get_by_id_ndb(job_id):
    """Returns the job associated with the given job_id.

    :param job_id: (int) ID of the job.
    :return: (kinds.jobs.Job)
    """
    asserts.valid_id_type(job_id)

    job = Job.get_by_id(job_id)
    if job is None:
        logging.warning('Job not found. id={}'.format(job_id))
        raise exp.NotFoundExp()
    return job
示例#13
0
def threads_ndb(actor_id, limit=25):
    """Returns the latest threads the account is a member of.

    :param actor_id: (int) ID of the account.
    :param limit: (int) max number of threads.
    :return: (list<kinds.messages.Thread>)
    """
    asserts.valid_id_type(actor_id)

    query = Thread.query(
        Thread.members == Member(account_id=actor_id, hidden=False))
    query = query.order(-Thread.updated)
    thds = query.fetch(limit=limit)

    return thds
示例#14
0
def patient_remove(actor_id, patient_id):
    """Removes the given patient_id from the given account.

    :param actor_id: (int) ID of the account performing the action.
    :param patient_id: (int) ID of the patient to be removed.
    :return: (None)
    """
    asserts.valid_id_type(actor_id)
    asserts.valid_id_type(patient_id)

    account = account_by_id(actor_id, _dto=False)
    patient = patient_by_id(account.id, patient_id, _dto=False)
    if patient is not None and patient.id in account.patient_ids:
        account.patient_ids.remove(patient.id)
        patient.soft_delete = True
        ndb.put_multi([account, patient])
示例#15
0
def patients_by_account(account_id, _dto=True):
    """Returns the list of patients that belong to the given account_id.

    :param account_id: (int) ID of the account.
    :return: (list<dto.accounts.PatientDto>) if _dto is True
             (list<kinds.accounts.Patient>) if _dto is False
             (None) if patients don't exist.
    """
    asserts.valid_id_type(account_id)

    account = account_by_id(account_id, _dto=False)
    if account.patient_ids is not None:
        keys = Patient.ids_to_keys(account.patient_ids)
        patients = ndb.get_multi(keys)
        if not _dto:
            return patients
        return [PatientDto.from_patient_ndb(p) for p in patients]
示例#16
0
def leave_thread(actor_id, thread_id):
    """Unsubscribes the account from the thread.

    TODO(kanat): Send a system message since the thread has been updated.

    :param actor_id: (int) ID of the account acting.
    :param thread_id: (int) ID of the thread to leave.
    :return: (None)
    """
    asserts.valid_id_type(actor_id)
    asserts.valid_id_type(thread_id)

    thread = Thread.get_by_id(thread_id)
    member = thread.find_member(actor_id)
    if member is not None and member.hidden is False:
        member.hidden = True
        thread.put()
示例#17
0
def caregiver_by_account(account_id, _dto=True):
    """Returns the caregiver details that belongs to the given account_id.

    :param account_id: (int) ID of the account.
    :return: (dto.accounts.CaregiverDto) if _dto is True
             (kinds.accounts.Caregiver) if _dto is False
             (None) if account or caregiver does not exist.
    """
    asserts.valid_id_type(account_id)

    account = account_by_id(account_id, _dto=False)
    if not asserts.is_valid_id_type(account.caregiver_id):
        raise exp.BadRequestExp()
    caregiver = Caregiver.get_by_id(account.caregiver_id)
    if not _dto:
        return caregiver
    return CaregiverDto.from_caregiver_ndb(caregiver)
示例#18
0
def hide_message(actor_id, message_id):
    """Hides the given message from the account.

    :param actor_id: (int) ID of the account acting.
    :param message_id: (int) ID of the message to hide.
    :return: (None)
    """
    asserts.valid_id_type(actor_id)
    asserts.valid_id_type(message_id)

    msg = Message.get_by_id(message_id)

    _assert_thread_member(actor_id, msg.thread_id)

    if actor_id not in msg.hidden_member_ids:
        msg.hidden_member_ids.append(actor_id)
        msg.put()
示例#19
0
def messages_ndb(actor_id, thread_id, limit=25):
    """Returns the latest messages in the given thread.

    TODO(kanat): Update kinds.Messages.Member.last_seen.

    :param actor_id: (int) ID of the account acting.
    :param thread_id: (int) ID of the thread.
    :param limit: (int) max number of messages.
    :return: (list<kinds.messages.Message>)
    """
    asserts.valid_id_type(actor_id)
    asserts.valid_id_type(thread_id)

    _assert_thread_member(actor_id, thread_id)

    msgs = Message.gql('WHERE thread_id = :1 ORDER BY created DESC',
                       thread_id).fetch(limit=limit)
    return [msg for msg in msgs if actor_id not in msg.hidden_member_ids]
示例#20
0
def account_by_id(account_id, _dto=True, _throw=True):
    """Returns the account associated with the given account_id.

    :param account_id: (int) ID of the account.
    :return: (dto.accounts.AccountDto) if _dto is True.
             (kinds.accounts.Account) if _dto is False.
             (None) if account does not exist.
    :raise: (exp.NotFoundExp) if `_throw` is True and account is not found.
    """
    asserts.valid_id_type(account_id)

    account = Account.get_by_id(account_id)
    if account is None and _throw:
        logging.warning('account not found. id={}'.format(account_id))
        raise exp.NotFoundExp('Account not found.')
    if not _dto:
        return account
    return AccountDto.from_account_ndb(account)
示例#21
0
def patient_by_id(actor_id, patient_id, _dto=True):
    """Returns the patient associated with the given account and patient_id.

    :param actor_id: (int) ID of the account performing the action.
    :param patient_id: (int) ID of the patient to retrieve.
    :return: (dto.accounts.PatientDto) if _dto is True
             (kinds.accounts.Patient) if _dto is False
    """
    asserts.valid_id_type(actor_id)
    asserts.valid_id_type(patient_id)

    account = account_by_id(actor_id, _dto=False)
    if patient_id not in account.patient_ids:
        raise exp.NotFoundExp('Care recipient not found.')
    patient = Patient.get_by_id(patient_id)
    if not _dto:
        return patient
    return PatientDto.from_patient_ndb(patient)
示例#22
0
def decline_request(actor_id, from_id):
    """Declines a connection request from the given account.

    :param actor_id: (int) ID of the account.
    :param from_id: (int) ID of the account that sent the connection request.
    :return: (None)
    """
    asserts.valid_id_type(actor_id)
    asserts.valid_id_type(from_id)

    req = _conn_request_get(to_id=actor_id,
                            from_id=from_id,
                            status=ConnStatus.Pending)
    if req is None:
        logging.warning(
            'Pending request does not exist: to={}, from={}'.format(
                actor_id, from_id))
        raise exp.NotFoundExp('Request does not exist.')
    req.key.delete()
示例#23
0
def _cancel_by_caregiver(actor_id, job_id, message=None):
    """Cancel a job the the selected caregiver.

    TODO(kanat): Notify careseeker.

    :return: (None)
    """
    asserts.valid_id_type(actor_id)

    job = get_by_id_ndb(job_id)
    if job.status == JobStatus.Cancelled:
        logging.info('Job already cancelled.')
        return
    apps = _get_applicants(job.id)
    if apps.selected is None or apps.selected.account_id != actor_id:
        raise exp.PermissionExp()
    logging.info('Job cancelled by caregiver. job_id={}'.format(job_id))
    apps.selected = None
    job.status = JobStatus.Cancelled
    ndb.put_multi([apps, job])
示例#24
0
def accept_request(actor_id, from_id):
    """Accepts a connection request from the given account.

    TODO(kanat): Send email notification.

    :param actor_id: (int) ID of the account.
    :param from_id: (int) ID of the account that sent the connection request.
    :return: (kinds.connections.ConnRequest) Accepted connection request.
    """
    asserts.valid_id_type(actor_id)
    asserts.valid_id_type(from_id)

    req = _conn_request_get(to_id=actor_id,
                            from_id=from_id,
                            status=ConnStatus.Pending)
    if req is None:
        logging.warning(
            'Pending request does not exist: to={}, from={}'.format(
                actor_id, from_id))
        raise exp.NotFoundExp('Request does not exist.')
    _accept_request(req)
    return req
示例#25
0
def create_job_ndb(account_id, job_dto):
    """Create a new job.

    TODO(kanat): Proper input validation, e.g:
        - valid patient_id?
        - valid start, end dates?
        - valid wage?

    :param account_id: (int) ID of the account for which the job belongs.
    :param job_dto: (dto.jobs.JobDto) job details.
    :return: (kinds.jobs.Job)
    """
    asserts.valid_id_type(account_id)
    asserts.type_of(job_dto, JobDto)

    job = JobDto.to_job_ndb(job_dto)
    job.account_id = account_id
    job.put()
    apps = JobApps(job_id=job.id)
    apps.put()
    job.apps_id = apps.id
    job.put()
    return job
示例#26
0
def send_ndb(actor_id, message_dto):
    """Sends a reply in a thread.

    :param actor_id: (int) ID of the account acting.
    :param message_dto: (dto.messages.MessageDto) message details.
    :return: (kinds.messages.Message)
    """
    asserts.valid_id_type(actor_id)
    asserts.type_of(message_dto, MessageDto)

    thread_id = message_dto.thread_id

    _assert_thread_member(actor_id, thread_id)

    thd = Thread.get_by_id(thread_id)
    msg = Message(thread_id=thd.id, sender_id=actor_id)
    msg.text = message_dto.text
    if hasattr(message_dto, 'message_type'):
        msg.message_type = message_dto.message_type
    msg.put()
    thd.last_message_id = msg.id
    thd.put()
    return msg
示例#27
0
def apply_job(actor_id, job_id, message=None):
    """Apply for a job.

    TODO(kanat): Notify job owner.

    :param actor_id: (int) ID of the account acting.
    :param job_id: (int) ID of the job applying.
    :param message: (str) Optional message.
    :return: (None)
    """
    asserts.valid_id_type(actor_id)
    asserts.valid_id_type(job_id)

    job = get_by_id_ndb(job_id)
    if job.status != JobStatus.Open:
        raise exp.BadRequestExp('Job is not open.')
    if actor_id == job.account_id:
        raise exp.BadRequestExp('Can\'t apply to own job.')
    apps = _get_applicants(job_id)
    if apps.find_applicant(actor_id) is not None:
        logging.warning('Already applied.')
        return
    apps.add_applicant(actor_id, message)
    apps.put()
示例#28
0
def _is_job_owner(actor_id, job_id):
    asserts.valid_id_type(actor_id)
    job = get_by_id_ndb(job_id)
    return actor_id == job.account_id