예제 #1
0
def process_line(infile):
    """
    Scan all lines in INFILE and create corresponding account/e-mail entries
    in Cerebrum.
    """
    stream = open(infile, 'r')

    # Iterate over all persons:
    for line in stream:

        logger.debug5("Processing line: |%s|", line)
        fields = string.split(line.strip(), ";")
        print len(fields)
        if len(fields) != 5:
            logger.error("Bad line: %s. Skipping" % line)
            continue
        # fi
        
        fnr, pname, uname, email, foo = fields
        if not fnr == "":
            logger.debug("Processing person %s", fnr)
            try:
                fodselsnr.personnr_ok(fnr)
            except fodselsnr.InvalidFnrError:
                logger.warn("Bad no_ssn %s, skipping", fnr)
                continue

        person.clear()
        gender = constants.gender_male

        if fodselsnr.er_kvinne(fnr):
            gender = constants.gender_female

        y, m, d = fodselsnr.fodt_dato(fnr)

        # Can't just populate like this, we need to search for persons
        # first.
        try:
            person.find_by_external_id(constants.externalid_fodselsnr, fnr)
        except Errors.NotFoundError:
            pass
        
        person.populate(db.Date(y, m, d), gender)
        person.affect_external_id(constants.system_ekstens,
                                  constants.externalid_fodselsnr)
        person.populate_external_id(constants.system_ekstens,
                                    constants.externalid_fodselsnr,
                                    fnr)
        person.write_db()
        
        update_names(fnr, pname)
        
        person.write_db()
        logger.debug("Created new person with fnr %s", fnr)

        p_id = person.entity_id

        account_id = process_user(p_id, uname)

        process_mail(account_id, 'defaultmail', email)
예제 #2
0
def p_create(fnr, lname, fname):
    gender = constants.gender_female
    try:
        fodselsnr.personnr_ok(fnr)
    except fodselsnr.InvalidFnrError:
        logger.error("Cannot create person, bad no_ssn |%s|", fnr)
        return None
    if not (lname and fname):
        logger.error("Cannot create person %s, missing name")
        return None
    if fodselsnr.er_mann(fnr):
        gender = constants.gender_female
    # this is very wrong, but as we do not have any birth days
    # registered we need to try producing them.
    year, mon, day = fodselsnr.fodt_dato(fnr)
    # in order to be a guardian you need to be over 18 years old which
    # excludes anyone born in after 1990 so this will work for now :-)
    birth_date = db.Date(year, mon, day)
    person.clear()
    # Create person
    person.populate(birth_date, gender)
    person.affect_external_id(constants.system_manual,
                              constants.externalid_fodselsnr)
    person.populate_external_id(constants.system_manual,
                                constants.externalid_fodselsnr, fnr)
    person.write_db()
    logger.info("Created new person with fnr %s", fnr)
    # Add person_name
    person.affect_names(constants.system_manual, constants.name_first,
                        constants.name_last)
    person.populate_name(constants.name_first, fname)
    person.populate_name(constants.name_last, lname)
    person.write_db()
    logger.debug("Added name %s %s to fnr %s", fname, lname, fnr)
    return person.entity_id
예제 #3
0
def populate_external_ids(tpl):
    """
    Locate (or create) a person holding the IDs contained in FIELDS and
    register these external IDs if necessary.

    This function both alters the PERSON object and retuns a boolean value
    (True means the ID update/lookup was successful, False means the
    update/lookup failed and nothing can be ascertained about the PERSON's
    state).

    There are two external IDs in SAP -- the Norwegian social security number
    (11-siffret personnummer, fnr) and the SAP employee id (sap_id). SAP IDs
    are (allegedly) permanent and unique, fnr can change.
    """

    error, person = locate_person(tpl.sap_ansattnr, tpl.sap_fnr)
    if error:
        logger.error("Lookup for (sap_id; fnr) == (%s; %s) failed",
                     tpl.sap_ansattnr, tpl.sap_fnr)
        return None

    if person is not None:
        logger.debug("A person owning IDs (%s, %s) already exists",
                     tpl.sap_ansattnr, tpl.sap_fnr)
        # Now, we *must* check that the IDs registered in Cerebrum match
        # those in SAP dump. I.e. we select the external IDs from Cerebrum
        # and compare them to SAP_ID and FNR. They must either match
        # exactly or be absent.
        if not match_external_ids(person, tpl.sap_ansattnr, tpl.sap_fnr):
            return None
    else:
        person = Factory.get("Person")(database)
        logger.debug("New person for IDs (%s, %s)", tpl.sap_ansattnr,
                     tpl.sap_fnr)

    try:
        fodselsnr.personnr_ok(tpl.sap_fnr)
    except fodselsnr.InvalidFnrError:
        # IVR 2007-02-15 It is *wrong* to simply ignore these, but since they
        # do occur, and they may be difficult to get rid of, we'll downgrade
        # the severity to avoid being spammed to death.
        logger.info("No valid checksum for FNR (%s)!", tpl.sap_fnr)
        return None

    gender = const.gender_male
    if fodselsnr.er_kvinne(tpl.sap_fnr):
        gender = const.gender_female

    # This would allow us to update birthdays and gender information for
    # both new and existing people.
    person.populate(tpl.sap_birth_date, gender)
    person.affect_external_id(const.system_sap, const.externalid_fodselsnr,
                              const.externalid_sap_ansattnr)
    person.populate_external_id(const.system_sap,
                                const.externalid_sap_ansattnr,
                                tpl.sap_ansattnr)
    person.populate_external_id(const.system_sap, const.externalid_fodselsnr,
                                tpl.sap_fnr)
    person.write_db()
    return person
예제 #4
0
def process_line(infile):
    """
    Scan all lines in INFILE and create corresponding account/e-mail entries
    in Cerebrum.
    """
    stream = open(infile, 'r')

    # Iterate over all persons:
    for line in stream:

        logger.debug5("Processing line: |%s|", line)
        fields = string.split(line.strip(), ";")
        print len(fields)
        if len(fields) != 5:
            logger.error("Bad line: %s. Skipping" % line)
            continue
        # fi

        fnr, pname, uname, email, foo = fields
        if not fnr == "":
            logger.debug("Processing person %s", fnr)
            try:
                fodselsnr.personnr_ok(fnr)
            except fodselsnr.InvalidFnrError:
                logger.warn("Bad no_ssn %s, skipping", fnr)
                continue

        person.clear()
        gender = constants.gender_male

        if fodselsnr.er_kvinne(fnr):
            gender = constants.gender_female

        y, m, d = fodselsnr.fodt_dato(fnr)

        # Can't just populate like this, we need to search for persons
        # first.
        try:
            person.find_by_external_id(constants.externalid_fodselsnr, fnr)
        except Errors.NotFoundError:
            pass

        person.populate(db.Date(y, m, d), gender)
        person.affect_external_id(constants.system_ekstens,
                                  constants.externalid_fodselsnr)
        person.populate_external_id(constants.system_ekstens,
                                    constants.externalid_fodselsnr, fnr)
        person.write_db()

        update_names(fnr, pname)

        person.write_db()
        logger.debug("Created new person with fnr %s", fnr)

        p_id = person.entity_id

        account_id = process_user(p_id, uname)

        process_mail(account_id, 'defaultmail', email)
예제 #5
0
 def _add_external_ids(self, entity, id_dict):
     if int(id_type) == self.co.externalid_fodselsnr:
         # Check fnr with the fnr module.
         try:
             fodselsnr.personnr_ok(id_dict[id_type])
         except fodselsnr.InvalidFnrError:
             raise ABCErrorInData, "fnr not valid: '%s'" %  id_dict[id_type]
     return self.__super._add_external_ids(entity, id_dict)
예제 #6
0
def process_person(fnr, lname, fname, bewid, set_names):
    """
    Find or create a person; return the person_id corresponding to
    fnr. Set name for new persons if set_name is True.
    """    
    logger.debug("Processing person %s %s (%s)", fname, lname, fnr)
    try:
        fodselsnr.personnr_ok(fnr)
    except fodselsnr.InvalidFnrError:
        logger.warn("Bad no_ssn |%s|", fnr)
        return None
    
    if fnr2person_id.has_key(fnr):
        logger.debug("Person with fnr %s exists in Cerebrum", fnr)
        return fnr2person_id[fnr]
    
    # ... otherwise, create a new person
    person.clear()
    gender = constants.gender_male
    if fodselsnr.er_kvinne(fnr):
        gender = constants.gender_female
    year, mon, day = fodselsnr.fodt_dato(fnr)
    person.populate(db.Date(year, mon, day), gender)
    if bewid:
        person.affect_external_id(constants.system_migrate,
                                  constants.externalid_fodselsnr,
                                  constants.externalid_bewatorid)
    else:
        person.affect_external_id(constants.system_migrate,
                                  constants.externalid_fodselsnr)        
    person.populate_external_id(constants.system_migrate,
                                constants.externalid_fodselsnr,
                                fnr)
    person.write_db()
    e_id = person.entity_id
    logger.info("Created new person with id %s and fnr %s", e_id, fnr)

    if bewid:
        person.populate_external_id(constants.system_migrate,
                                    constants.externalid_bewatorid,
                                    bewid)
    person.write_db()
    logger.info("Added BewatorID %s for %s", bewid, fnr)
    
    if set_names:
        if lname and fname:
            person.affect_names(constants.system_migrate,
                                constants.name_first, constants.name_last)
            person.populate_name(constants.name_first, fname)
            person.populate_name(constants.name_last, lname)
            logger.info("Name %s %s set for person %s", fname, lname, fnr)
            person.write_db()
        else:
            logger.warn("Couldn't set name %s %s for person %s",
                        fname, lname, fnr)

    fnr2person_id[fnr] = e_id
    return e_id
예제 #7
0
def import_data(fname):
    # Hent betalings-id'er hittil i år
    processed_payment_ids = {}
    for row in ppq.get_history_payments(
        transaction_type=co.pqtt_quota_fill_pay,
        bank_id_mask='FS:%%'):
        processed_payment_ids[row['bank_id']] = True    
    
    for attrs in GeneralDataParser(fname, "betaling"):
        fnr = fodselsnr.personnr_ok("%06d%05d" % (int(attrs['fodselsdato']),
                                                  int(attrs['personnr'])))
        try:
            person.clear()
            person.find_by_external_id(
                co.externalid_fodselsnr, fnr, source_system=co.system_fs)
        except Errors.NotFoundError:
            #
            # This is actually not a real problem as the person in
            # question usually is registered a day or two after
            # The proper solution would be to check whether
            # this error occurs more than twice and issue a warning
            # if that is the case, but we cannot support that
            # kind of solution at the time being
            # For now we log the error each time it occurs but we don't
            # issue a warning of any kind.
            #
            # TODO (Jazz, 2008-01-21): implement some kind of support for
            # registration of repetitive errors
            logger.info("Payment for unknown person: %s" % fnr)
            continue
        person_id = person.entity_id
        # Asert that person has a quota_status entry
        try:
            row = ppq.find(person_id)
        except Errors.NotFoundError:
            ppq.new_quota(person_id)

        ekstern_betaling_id = ":".join(('FS', attrs['fakturanr'], attrs['detaljlopenr']))
        if processed_payment_ids.has_key(ekstern_betaling_id):
            logger.debug("Already added: %s" % ekstern_betaling_id)
            continue
        description = 'FS-betaling'
        payment_tstamp = attrs.get('dato_betalt', None)
        try:
            logger.debug("Add payment %s for %s" % (attrs['belop'], fnr))
            pq_util.add_payment(person_id,
                                PPQUtil.PPQUtil.FS,
                                ekstern_betaling_id,
                                float(attrs['belop']),
                                description,
                                payment_tstamp=payment_tstamp,
                                update_program='money2paper')
            payment_logger.info("Registered %s kr for %i (id: %s)" % (
                attrs['belop'], person_id, ekstern_betaling_id))
        except db.DatabaseError, msg:
            db.rollback()
            logger.warn("Input %s caused DatabaseError:%s" % (attrs, msg))
            continue

        db.commit()
예제 #8
0
def process_person(fnr):
    """
    Find (or create, if necessary) and return the person_id corresponding to
    FNR.
    """

    logger.debug("Processing person %s", fnr)

    if not fodselsnr.personnr_ok(fnr):
        logger.warn("Bad no_ssn |%s|", fnr)
        return None
    # fi

    if fnr2person_id.has_key(fnr):
        logger.debug("Person with fnr %s exists in Cerebrum", fnr)
        return fnr2person_id[fnr]
    # fi

    # ... otherwise, create a new person
    person.clear()
    gender = constants.gender_male
    if fodselsnr.er_kvinne(fnr):
        gender = constants.gender_female
    # fi
    year, mon, day = fodselsnr.fodt_dato(fnr)
    person.populate(db.Date(year, mon, day), gender)
    person.affect_external_id(constants.system_migrate,
                              constants.externalid_fodselsnr)
    person.populate_external_id(constants.system_migrate,
                                constants.externalid_fodselsnr, fnr)
    person.write_db()
    logger.debug("Created new person with fnr %s", fnr)
    e_id = person.entity_id
    fnr2person_id[fnr] = e_id
    return e_id
예제 #9
0
    def _get_voip_person(self, designation):
        """Lookup a person by <something>.
        """

        person = Factory.get("Person")(self.db)
        if self._get_entity_id(designation):
            try:
                person.find(self._get_entity_id(designation))
                return person
            except Errors.NotFoundError:
                pass

        # fnr?
        exc = CerebrumError("No person found for designation %r" % designation)
        id_type, value = self._human_repr2id(designation)
        if text_type(value).isdigit():
            try:
                fnr = personnr_ok(text_type(value))
                person.find_by_external_id(self.const.externalid_fodselsnr,
                                           fnr)
                return person
            except (InvalidFnrError, Errors.NotFoundError):
                pass

        # account?
        try:
            account = Factory.get("Account")(self.db)
            account.find_by_name(text_type(value))
            if account.owner_type == self.const.entity_person:
                return self._get_voip_person(account.owner_id)
        except Errors.NotFoundError:
            pass

        # By other external ids? By name?
        raise exc
예제 #10
0
def import_data(fname):
    # Hent betalings-id'er hittil i år
    processed_payment_ids = {}
    for row in ppq.get_history_payments(
        transaction_type=co.pqtt_quota_fill_pay,
        bank_id_mask='FS:%%'):
        processed_payment_ids[row['bank_id']] = True    
    
    for attrs in GeneralDataParser(fname, "betaling"):
        fnr = fodselsnr.personnr_ok("%06d%05d" % (int(attrs['fodselsdato']),
                                                  int(attrs['personnr'])))
        try:
            person.clear()
            person.find_by_external_id(
                co.externalid_fodselsnr, fnr, source_system=co.system_fs)
        except Errors.NotFoundError:
            #
            # This is actually not a real problem as the person in
            # question usually is registered a day or two after
            # The proper solution would be to check whether
            # this error occurs more than twice and issue a warning
            # if that is the case, but we cannot support that
            # kind of solution at the time being
            # For now we log the error each time it occurs but we don't
            # issue a warning of any kind.
            #
            # TODO (Jazz, 2008-01-21): implement some kind of support for
            # registration of repetitive errors
            logger.info("Payment for unknown person: %s" % fnr)
            continue
        person_id = person.entity_id
        # Asert that person has a quota_status entry
        try:
            row = ppq.find(person_id)
        except Errors.NotFoundError:
            ppq.new_quota(person_id)

        ekstern_betaling_id = ":".join(('FS', attrs['fakturanr'], attrs['detaljlopenr']))
        if processed_payment_ids.has_key(ekstern_betaling_id):
            logger.debug("Already added: %s" % ekstern_betaling_id)
            continue
        description = 'FS-betaling'
        payment_tstamp = attrs.get('dato_betalt', None)
        try:
            logger.debug("Add payment %s for %s" % (attrs['belop'], fnr))
            pq_util.add_payment(person_id,
                                PPQUtil.PPQUtil.FS,
                                ekstern_betaling_id,
                                float(attrs['belop']),
                                description,
                                payment_tstamp=payment_tstamp,
                                update_program='money2paper')
            payment_logger.info("Registered %s kr for %i (id: %s)" % (
                attrs['belop'], person_id, ekstern_betaling_id))
        except db.DatabaseError, msg:
            db.rollback()
            logger.warn("Input %s caused DatabaseError:%s" % (attrs, msg))
            continue

        db.commit()
예제 #11
0
    def move_student_callback(self, person_info):
        """We will only move the student if it has a valid fnr from FS,
        and it is not currently on a student disk.

        If the new homedir cannot be determined, user will be moved to a
        pending disk.  process_students moves users from this disk as soon
        as a proper disk can be determined.

        Currently we only operate on the disk whose spread is
        default_spread"""

        fnr = "%06d%05d" % (int(
            person_info['fodselsdato']), int(person_info['personnr']))
        logger.debug("Callback for %s" % fnr)
        try:
            fodselsnr.personnr_ok(fnr)
        except Exception, e:
            logger.exception(e)
            return
예제 #12
0
    def move_student_callback(self, person_info):
        """We will only move the student if it has a valid fnr from FS,
        and it is not currently on a student disk.

        If the new homedir cannot be determined, user will be moved to a
        pending disk.  process_students moves users from this disk as soon
        as a proper disk can be determined.

        Currently we only operate on the disk whose spread is
        default_spread"""

        fnr = "%06d%05d" % (int(person_info['fodselsdato']),
                            int(person_info['personnr']))
        logger.debug("Callback for %s" % fnr)
        try:
            fodselsnr.personnr_ok(fnr)
        except Exception, e:
            logger.exception(e)
            return
예제 #13
0
 def _get_fnr(self, person_info):
     unchecked_fnr = "%06d%05d" % (int(
         person_info['fodselsdato']), int(person_info['personnr']))
     try:
         fnr = fodselsnr.personnr_ok(unchecked_fnr)
         logger.info("Processing %s", fnr)
         return fnr
     except fodselsnr.InvalidFnrError:
         logger.warn("Ugyldig fødselsnr: %s", unchecked_fnr)
         raise fodselsnr.InvalidFnrError
예제 #14
0
def process_person(fnr, card_id):
    """
    Find or create a person; return the person_id corresponding to
    fnr.
    """

    logger.debug("Processing person %s", fnr)
    try:
        fodselsnr.personnr_ok(fnr)
    except fodselsnr.InvalidFnrError:
        logger.warn("Bad no_ssn |%s|", fnr)
        return None
    
    if fnr2person_id.has_key(fnr):
        logger.debug("Person with fnr %s exists in Cerebrum", fnr)
        return fnr2person_id[fnr]
    
    # ... otherwise, create a new person
    person.clear()
    gender = constants.gender_male
    if fodselsnr.er_kvinne(fnr):
        gender = constants.gender_female
    year, mon, day = fodselsnr.fodt_dato(fnr)
    person.populate(db.Date(year, mon, day), gender)
    person.affect_external_id(constants.system_migrate,
                              constants.externalid_fodselsnr,
                              constants.externalid_bewatorid)
    person.populate_external_id(constants.system_migrate,
                                constants.externalid_fodselsnr,
                                fnr)
    if card_id is not None and card_id != '':
        person.populate_external_id(constants.system_migrate,
                                    constants.externalid_bewatorid,
                                    card_id)
    else:
        logger.debug("No Bewator-ID found for person %s" % fnr)
        
    person.write_db()
    logger.debug("Created new person with fnr %s and card-ID %s" % (fnr, card_id))
    e_id = person.entity_id
    fnr2person_id[fnr] = e_id
    return e_id
예제 #15
0
def p_create(fnr, lname, fname):
    gender = constants.gender_female
    try:
        fodselsnr.personnr_ok(fnr)
    except fodselsnr.InvalidFnrError:
        logger.error("Cannot create person, bad no_ssn |%s|", fnr)
        return None
    if not (lname and fname):
        logger.error("Cannot create person %s, missing name")
        return None
    if fodselsnr.er_mann(fnr):
        gender = constants.gender_female
    # this is very wrong, but as we do not have any birth days
    # registered we need to try producing them.
    year, mon, day = fodselsnr.fodt_dato(fnr)
    # in order to be a guardian you need to be over 18 years old which
    # excludes anyone born in after 1990 so this will work for now :-)
    birth_date = db.Date(year, mon, day)
    person.clear()
    # Create person
    person.populate(birth_date, gender)
    person.affect_external_id(constants.system_manual,
                              constants.externalid_fodselsnr)
    person.populate_external_id(constants.system_manual,
                                constants.externalid_fodselsnr, fnr)
    person.write_db()
    logger.info("Created new person with fnr %s", fnr)
    # Add person_name
    person.affect_names(constants.system_manual,
                        constants.name_first,
                        constants.name_last)
    person.populate_name(constants.name_first, fname)
    person.populate_name(constants.name_last, lname)
    person.write_db()
    logger.debug("Added name %s %s to fnr %s", fname, lname, fnr)
    return person.entity_id
예제 #16
0
    def parse_person_info(self, sub):
        self.logger.debug("Parsing %s" % sub.tag)

        for el in sub.iter():
            value = None
            if el.text:
                value = el.text.strip().encode("latin1")

            # TBD: få bort Mellomnavn fra sap?
            # Per baardj's request, we consider middle names as first names.
            middle = ""
            middle = el.find("PersonInfo/Mellomnavn")
            if middle is not None and middle.text:
                middle = middle.text.encode("latin1").strip()

            if el.tag == "Fornavn":
                if middle:
                    value += " " + middle

                # TBD: Skal vi fortsatt ta hensyn til '*' og'@'
                if '*' in value or '@' in value:
                    self.logger.debug("Name contains '@' or '*', ignored")
                    # Since the element is marked as void, there is no need to
                    # process further (we have no guarantee that any data
                    # would make sense and we won't have even more spurious
                    # warnings).
                    return None
                # TODO: fix
                self.result.add_name(DataName(self.tag2type[el.tag], value))
            elif el.tag == "Etternavn":
                if '*' in value or '@' in value:
                    self.logger.debug("Name contains '@' or '*', ignored")
                    # Se <Fornavn>.
                    return None
                # TODO: fix
                self.result.add_name(DataName(self.tag2type[el.tag], value))
            elif el.tag == "Fodselsnummer":
                self.result.add_id(self.tag2type[el.tag], personnr_ok(value))
            elif el.tag == "Ansattnr":
                self.result.add_id(self.tag2type[el.tag], value)
            elif el.tag == "Fodselsdato":
                self.result.birth_date = self._make_mxdate(value)
            elif el.tag == "Kjonn":
                self.result.gender = self.tag2type[value]
            elif sub.tag == "Title":
                if value:
                    self.result.add_name(
                        DataName(self.tag2type[sub.tag], value))
예제 #17
0
def recalc_quota_callback(person_info):
    # Kun kvoteverdier styres av studconfig.xml.  De øvrige
    # parameterene er for komplekse til å kunne uttrykkes der uten å
    # introdusere nye tag'er.
    person_id = person_info.get('person_id', None)
    logger.set_indent(0)
    if person_id is None:
        try:
            fnr = fodselsnr.personnr_ok("%06d%05d" % (
                int(person_info['fodselsdato']),
                int(person_info['personnr'])
            ))
        except fodselsnr.InvalidFnrError:
            logger.warn('Invalid FNR detected')
        if fnr not in fnr2pid:
            logger.warn("fnr %r is an unknown person", fnr)
            return
        person_id = fnr2pid[fnr]
    processed_person[person_id] = True
    logger.debug("callback for %r", person_id)
    logger.set_indent(3)

    # Sjekk at personen er underlagt kvoteregimet
    if person_id not in quota_victims:
        logger.debug("not a quota victim %r", person_id)
        logger.set_indent(0)
        # assert that person does _not_ have quota
        set_quota(person_id, has_quota=False)
        return

    # Sjekk matching mot studconfig.xml
    quota = None
    try:
        profile = autostud.get_profile(
            person_info,
            member_groups=person_id_member.get(
                person_id,
                []
            ),
            person_affs=person_id_affs.get(
                person_id,
                []
            )
        )
        quota = profile.get_pquota(as_list=True)
    except AutoStud.ProfileHandler.NoMatchingProfiles, msg:
        # A common situation, so we won't log it
        profile = None
예제 #18
0
    def parse_person_info(self, sub):
        self.logger.debug("Parsing %s" % sub.tag)

        for el in sub.iter():
            value = None
            if el.text:
                value = el.text.strip().encode("latin1")
    
            # TBD: få bort Mellomnavn fra sap?
            # Per baardj's request, we consider middle names as first names.
            middle = ""
            middle = el.find("PersonInfo/Mellomnavn")
            if middle is not None and middle.text:
                middle = middle.text.encode("latin1").strip()
    
            if el.tag == "Fornavn":
                if middle:
                    value += " " + middle
    
                # TBD: Skal vi fortsatt ta hensyn til '*' og'@'
                if '*' in value or '@' in value:
                    self.logger.debug("Name contains '@' or '*', ignored")
                    # Since the element is marked as void, there is no need to
                    # process further (we have no guarantee that any data
                    # would make sense and we won't have even more spurious
                    # warnings). 
                    return None
                # TODO: fix
                self.result.add_name(DataName(self.tag2type[el.tag], value))
            elif el.tag == "Etternavn":
                if '*' in value or '@' in value:
                    self.logger.debug("Name contains '@' or '*', ignored")
                    # Se <Fornavn>.
                    return None
                # TODO: fix
                self.result.add_name(DataName(self.tag2type[el.tag], value))
            elif el.tag == "Fodselsnummer":
                self.result.add_id(self.tag2type[el.tag], personnr_ok(value))
            elif el.tag == "Ansattnr":
                self.result.add_id(self.tag2type[el.tag], value)
            elif el.tag == "Fodselsdato":
                self.result.birth_date = self._make_mxdate(value)
            elif el.tag == "Kjonn":
                self.result.gender = self.tag2type[value]
            elif sub.tag == "Title":
                if value:
                    self.result.add_name(DataName(self.tag2type[sub.tag], value))
예제 #19
0
def recalc_quota_callback(person_info, fnr2pid, quota_victims,
                          kopiavgift_fritak, betaling_fritak):
    # Kun kvoteverdier styres av studconfig.xml.  De øvrige
    # parameterene er for komplekse til å kunne uttrykkes der uten å
    # introdusere nye tag'er.
    person_id = person_info.get('person_id', None)
    if person_id is None:
        try:
            fnr = fodselsnr.personnr_ok("%06d%05d" % (int(
                person_info['fodselsdato']), int(person_info['personnr'])))
        except fodselsnr.InvalidFnrError:
            logger.warn('Invalid FNR detected')
        if fnr not in fnr2pid:
            logger.warn("fnr %r is an unknown person", fnr)
            return
        person_id = fnr2pid[fnr]
    processed_person[person_id] = True
    logger.debug("callback for %r", person_id)

    # Sjekk at personen er underlagt kvoteregimet
    if person_id not in quota_victims:
        logger.debug("not a quota victim %r", person_id)
        # assert that person does _not_ have quota
        set_pq_exempt(person_id, exempt=True)
        return

    # Har fritak fra kvote
    if person_id in betaling_fritak:
        logger.debug("%s is exempt from quota", person_id)
        set_pq_exempt(person_id, exempt=True)
        return

    # Set quota for those without har kopiavgift-fritak
    if person_id not in kopiavgift_fritak:
        logger.debug("block %r (fritak=%r)", person_id,
                     kopiavgift_fritak.get(person_id, False))
        set_pq_exempt(person_id, exempt=False)
        return

    set_pq_exempt(person_id, exempt=False)
예제 #20
0
def process_lines(infile):
    """
    Scan all lines in INFILE and create corresponding account entries
    in Cerebrum.
    """

    stream = open(infile, 'r')
    commit_count = 0
    commit_limit = 1000

    # Iterate over all persons:
    for line in stream:
        commit_count += 1
        logger.debug5("Processing line: |%s|", line.strip())

        fields = string.split(line.strip(), ";")
        if len(fields) != 2:
            logger.error("Bad line: %s. Skipping" % l)
            continue
        
        uname, fnr = fields

        try:
            fnr = fodselsnr.personnr_ok(fnr)
        except fodselsnr.InvalidFnrError:
            logger.error("Bad fnr: %s, uname %s. Skipping!" % (fnr, uname))
            continue

        person_id = process_person(fnr)
        logger.debug4("Processing person with user: %s", uname)
        account_id = process_user(person_id, uname)
        
        if not account_id:
            logger.error("Bad uname: %s Skipping", line)

        if commit_count % commit_limit == 0:
            attempt_commit()
예제 #21
0
def recalc_quota_callback(person_info):
    # Kun kvoteverdier styres av studconfig.xml.  De øvrige
    # parameterene er for komplekse til å kunne uttrykkes der uten å
    # introdusere nye tag'er.
    person_id = person_info.get('person_id', None)
    logger.set_indent(0)
    if person_id is None:
        fnr = fodselsnr.personnr_ok(
            "%06d%05d" %
            (int(person_info['fodselsdato']), int(person_info['personnr'])))
        if fnr not in fnr2pid:
            logger.warn("fnr %s is an unknown person" % fnr)
            return
        person_id = fnr2pid[fnr]
    processed_person[person_id] = True
    logger.debug("callback for %s" % person_id)
    logger.set_indent(3)

    # Sjekk at personen er underlagt kvoteregimet
    if person_id not in quota_victims:
        logger.debug("not a quota victim %s" % person_id)
        logger.set_indent(0)
        # assert that person does _not_ have quota
        set_quota(person_id, has_quota=False)
        return

    # Sjekk matching mot studconfig.xml
    quota = None
    try:
        profile = autostud.get_profile(
            person_info,
            member_groups=person_id_member.get(person_id, []),
            person_affs=person_id_affs.get(person_id, []))
        quota = profile.get_pquota(as_list=True)
    except AutoStud.ProfileHandler.NoMatchingProfiles, msg:
        # A common situation, so we won't log it
        profile = None
예제 #22
0
def process_lines(infile):
    """
    Scan all lines in INFILE and create corresponding account entries
    in Cerebrum.
    """

    stream = open(infile, 'r')
    commit_count = 0
    commit_limit = 1000

    # Iterate over all persons:
    for line in stream:
        commit_count += 1
        logger.debug5("Processing line: |%s|", line.strip())

        fields = string.split(line.strip(), ";")
        if len(fields) != 2:
            logger.error("Bad line: %s. Skipping" % l)
            continue

        uname, fnr = fields

        try:
            fnr = fodselsnr.personnr_ok(fnr)
        except fodselsnr.InvalidFnrError:
            logger.error("Bad fnr: %s, uname %s. Skipping!" % (fnr, uname))
            continue

        person_id = process_person(fnr)
        logger.debug4("Processing person with user: %s", uname)
        account_id = process_user(person_id, uname)

        if not account_id:
            logger.error("Bad uname: %s Skipping", line)

        if commit_count % commit_limit == 0:
            attempt_commit()
예제 #23
0
    def _get_voip_person(self, designation):
        """Lookup a person by <something>.
        """

        person = Factory.get("Person")(self.db)
        if self._get_entity_id(designation):
            try:
                person.find(self._get_entity_id(designation))
                return person
            except Errors.NotFoundError:
                pass

        # fnr?
        exc = CerebrumError("No person found for designation %r" %
                            designation)
        id_type, value = self._human_repr2id(designation)
        if text_type(value).isdigit():
            try:
                fnr = personnr_ok(text_type(value))
                person.find_by_external_id(self.const.externalid_fodselsnr,
                                           fnr)
                return person
            except (InvalidFnrError, Errors.NotFoundError):
                pass

        # account?
        try:
            account = Factory.get("Account")(self.db)
            account.find_by_name(text_type(value))
            if account.owner_type == self.const.entity_person:
                return self._get_voip_person(account.owner_id)
        except Errors.NotFoundError:
            pass

        # By other external ids? By name?
        raise exc
예제 #24
0
def process_person(fnr):
    """
    Find (or create, if necessary) and return the person_id corresponding to
    FNR.
    """

    logger.debug("Processing person %s", fnr)
    
    if not fodselsnr.personnr_ok(fnr):
        logger.warn("Bad no_ssn |%s|", fnr)
        return None
    # fi
    
    if fnr2person_id.has_key(fnr):
        logger.debug("Person with fnr %s exists in Cerebrum", fnr)
        return fnr2person_id[fnr]
    # fi
    
    # ... otherwise, create a new person
    person.clear()
    gender = constants.gender_male
    if fodselsnr.er_kvinne(fnr):
        gender = constants.gender_female
    # fi
    year, mon, day = fodselsnr.fodt_dato(fnr)
    person.populate(db.Date(year, mon, day), gender)
    person.affect_external_id(constants.system_migrate,
                              constants.externalid_fodselsnr)
    person.populate_external_id(constants.system_migrate,
                                constants.externalid_fodselsnr,
                                fnr)
    person.write_db()
    logger.debug("Created new person with fnr %s", fnr)
    e_id = person.entity_id
    fnr2person_id[fnr] = e_id
    return e_id
예제 #25
0
    def next_object(self, element):
        """Return the next SAPPerson object.

        Consume the next XML-element describing a person, and return a
        suitable representation (SAPPerson).

        Should something fail (which prevents this method from constructing a
        proper SAPPerson object), an exception is raised.

        """
        result = SAPPerson()

        # Per baardj's request, we consider middle names as first names.
        middle = ""
        middle = element.find("Person/Mellomnavn")
        if middle is not None and middle.text:
            middle = ensure_unicode(middle.text.strip(), self.encoding)

        # Iterate over *all* subelements, 'fill up' the result object
        for sub in element.getiterator():
            value = None
            if sub.text:
                value = ensure_unicode(sub.text.strip(), self.encoding)

            if sub.tag == "Fornavn":
                if middle:
                    value += " " + middle

                # IVR 2007-05-30 FIXME: This is not pretty.
                #
                # In an e-mail from 2007-05-29, Johannes Paulsen suggests that
                # marking invalid entries with '*' in the some of the name
                # elements is the easiest approach. This is an ugly hack, but
                # since the invalid entries will not disappear anytime soon,
                # this is the easiest way of skipping them.
                #
                # JAZZ 2007-08-01
                # '*' did not work all that well as it is used as common
                # wildcard in SAP. Johannes suggests that we use '@' in
                # stead. As the data is not updated yet (we don't know when
                # that will happen) we need to test for '*' as well in order
                # to skip all the invalid elements
                #
                if '*' in value or '@' in value:
                    if self.logger:
                        self.logger.debug("Name contains '@' or '*', ignored")
                    # Since the element is marked as void, there is no need to
                    # process further (we have no guarantee that any data
                    # would make sense and we won't have even more spurious
                    # warnings).
                    return None
                result.add_name(DataName(self.tag2type[sub.tag], value))
            elif sub.tag == "Etternavn":
                if '*' in value or '@' in value:
                    if self.logger:
                        self.logger.debug("Name contains '@' or '*', ignored")
                    # Se <Fornavn>.
                    return None
                result.add_name(DataName(self.tag2type[sub.tag], value))
            elif sub.tag == "Fodselsnummer" and value is not None:
                result.add_id(self.tag2type[sub.tag], personnr_ok(value))
            elif sub.tag == "Ansattnummer":
                result.add_id(self.tag2type[sub.tag], value)
                self.logger.debug(value)
            elif sub.tag == "Fodselsdato":
                result.birth_date = self._make_mxdate(value, format="%Y-%m-%d")
            elif sub.tag == "Kjonn":
                result.gender = self.tag2type[value]
            elif sub.tag == "Adresse":
                result.add_address(self._make_address(sub))
            elif sub.tag in ("Hovedstilling", "Bistilling"):
                emp = self._make_employment(sub)
                if emp is not None:
                    result.add_employment(emp)
            elif sub.tag == "Roller" and sub.findtext("IKKE-ANGIT") is None:
                emp = self._make_role(sub)
                if emp is not None:
                    result.add_employment(emp)
            elif sub.tag == "Person":
                # Lots of the other entries above also are part of the
                # "person"-firstlevel element, but we need to
                # specifically look here for Tittel => personal title,
                # to avoid confusion with worktitles
                for subsub in sub.findall("Tittel"):
                    personal_title = self._make_title(HRDataPerson.NAME_TITLE,
                                                      subsub)
                    if personal_title:
                        result.add_name(personal_title)
            elif sub.tag == "PersonligID":
                # Store additional person ids, like passport numbers.
                # Handle passport numbers
                if sub.find('Type').text in self.sap2idtype:
                    # Add the ID to the data-structure
                    pers_id = '{0}-{1}'.format(
                        ensure_unicode(sub.find('Land').text, self.encoding),
                        ensure_unicode(sub.find('Verdi').text, self.encoding)
                    )
                    result.add_id(self.sap2idtype[sub.find('Type').text],
                                  pers_id)
                else:
                    self.logger.debug(
                        "Unknown %s type '%s': skipping id type",
                        sub.tag, sub.find('Type').text)
            elif sub.tag == "SGM":
                # New feature and unique (for now?) for UiO is SGM,
                # external attachments for person.
                self.logger.debug("SGM for %s", result)
                result.add_external_work(self._make_sgm(sub))
        # We need to order 'Telefon 1' and 'Telefon 2' properly
        celems = list(element.findall("Kommunikasjon"))
        celems.sort(lambda x, y: cmp(x.find("Type").text,
                                     y.find("Type").text))
        # TBD: Priorities!
        priority = 0
        for ct in celems:
            contact = self._make_contact(ct, priority)
            if contact:
                result.add_contact(contact)
                priority += 1

        # Reservations for catalogue publishing
        # default: One active employment => can be published
        to_reserve = not result.has_active_employments()

        # Everyone with 'RESE' is reserved (regardless of everything else)
        # If no 'RESE' exists, but there is a 'SAMT' => no reservation
        for i in element.findall("Adresse/Reservert"):
            if i.text:
                tmp = i.text.strip()
                if tmp == "RESE":
                    to_reserve = True
                    break
                elif tmp == "SAMT":
                    to_reserve = False
        result.reserved = to_reserve

        # Address magic
        # If there is a sensible 'Sted for lønnsslipp', it will result i
        # proper "post/besøksaddresse" later. This code matches LT's behaviour
        # more closely (an employee 'inherits' the address of his/her
        # "primary" workplace.
        for sub in element.getiterator("Kommunikasjon"):
            txt = ensure_unicode(sub.findtext("Type"), self.encoding)
            val = ensure_unicode(sub.findtext("Verdi"), self.encoding)
            if (txt and txt == "Sted for lønnslipp" and val
                    # *some* of the entries have a space here and there.
                    # and some contain non-digit data
                    and val.replace(" ", "").isdigit()):
                val = val.replace(" ", "")
                fak, inst, gruppe = [int(x) for x in
                                     (val[:2], val[2:4], val[4:])]
                result.primary_ou = (cereconf.DEFAULT_INSTITUSJONSNR,
                                     fak, inst, gruppe)

        # We require people to have first/last name.
        if not (result.get_name(result.NAME_FIRST) and
                result.get_name(result.NAME_LAST)):
            self.logger.warn(
                "People must have first and last names. %s skipped",
                list(result.iterids())
            )
            return None

        return result
예제 #26
0
def move_student_callback(person_info):
    """We will only move the student if it has a valid fnr from FS,
    and it is not currently on a student disk.

    If the new homedir cannot be determined, user will be moved to a
    pending disk.  process_students moves users from this disk as soon
    as a proper disk can be determined.

    Currently we only operate on the disk whose spread is
    default_spread"""

    fnr = fodselsnr.personnr_ok("%06d%05d" % (int(person_info['fodselsdato']),
                                              int(person_info['personnr'])))
    if fnr not in fnr2move_student:
        return
    logger.debug("Callback for %s" % fnr)
    account = Utils.Factory.get('Account')(db)
    group = Utils.Factory.get('Group')(db)
    br = BofhdRequests(db, const)
    for account_id, request_id, requestee_id in fnr2move_student.get(fnr, []):
        account.clear()
        account.find(account_id)
        groups = list(int(x["group_id"]) for x in
                      group.search(member_id=account_id,
                                   indirect_members=False))
        try:
            profile = autostud.get_profile(person_info, member_groups=groups)
            logger.debug(profile.matcher.debug_dump())
        except AutostudError, msg:
            logger.debug("Error getting profile, using pending: %s" % msg)
            continue

        # Determine disk
        disks = []
        spreads = [int(s) for s in profile.get_spreads()]
        try:
            for d_spread in profile.get_disk_spreads():
                if d_spread != default_spread:
                    # TBD:  How can all spreads be taken into account?
                    continue
                if d_spread in spreads:
                    try:
                        ah = account.get_home(d_spread)
                        homedir_id = ah['homedir_id']
                        current_disk_id = ah['disk_id']
                    except Errors.NotFoundError:
                        homedir_id, current_disk_id = None, None
                    if autostud.disk_tool.get_diskdef_by_diskid(
                            int(current_disk_id)):
                        logger.debug("Already on a student disk")
                        br.delete_request(request_id=request_id)
                        db.commit()
                        # actually, we remove a bit too much data from
                        # the below dict, but remaining data will be
                        # rebuilt on next run.

                        del(fnr2move_student[fnr])
                        raise NextAccount
                    try:
                        new_disk = profile.get_disk(d_spread, current_disk_id,
                                                    do_check_move_ok=False)
                        if new_disk == current_disk_id:
                            continue
                        disks.append((new_disk, d_spread))
                        if (autostud.disk_tool.using_disk_kvote and
                                homedir_id is not None):
                            from Cerebrum.modules.no.uio import DiskQuota
                            disk_quota_obj = DiskQuota.DiskQuota(db)
                            try:
                                cur_quota = disk_quota_obj.get_quota(
                                    homedir_id)
                            except Errors.NotFoundError:
                                cur_quota = None
                            quota = profile.get_disk_kvote(new_disk)
                            if (cur_quota is None or
                                    cur_quota['quota'] != int(quota)):
                                disk_quota_obj.set_quota(homedir_id,
                                                         quota=int(quota))
                    except AutostudError, msg:
                        # Will end up on pending (since we only use one spread)
                        logger.debug("Error getting disk: %s" % msg)
                        break
        except NextAccount:
            pass   # Stupid python don't have labeled breaks
        logger.debug(str((fnr, account_id, disks)))
        if disks:
            logger.debug("Destination %s" % repr(disks))
            del(fnr2move_student[fnr])
            for disk, spread in disks:
                br.delete_request(request_id=request_id)
                br.add_request(requestee_id, br.batch_time,
                               const.bofh_move_user,
                               account_id, disk, state_data=spread)
                db.commit()
예제 #27
0
def process_person_callback(person_info):
    """Called when we have fetched all data on a person from the xml
    file.  Updates/inserts name, address and affiliation
    information."""

    global no_name
    try:
        fnr = fodselsnr.personnr_ok("%06d%05d" % (int(person_info["fodselsdato"]), int(person_info["personnr"])))
        fnr = fodselsnr.personnr_ok(fnr)
        logger.info("Process %s " % (fnr))
        (year, mon, day) = fodselsnr.fodt_dato(fnr)
        if year < 1970 and getattr(cereconf, "ENABLE_MKTIME_WORKAROUND", 0) == 1:
            # Seems to be a bug in time.mktime on some machines
            year = 1970
    except fodselsnr.InvalidFnrError:
        logger.warn("Ugyldig fødselsnr: %s" % fnr)
        return

    gender = co.gender_male
    if fodselsnr.er_kvinne(fnr):
        gender = co.gender_female

    etternavn = fornavn = None
    studentnr = None
    affiliations = []
    address_info = None
    aktiv_sted = []

    # Iterate over all person_info entries and extract relevant data
    if person_info.has_key("aktiv"):
        for row in person_info["aktiv"]:
            if studieprog2sko[row["studieprogramkode"]] is not None:
                aktiv_sted.append(int(studieprog2sko[row["studieprogramkode"]]))
                logger.debug("App2akrivts")

    for dta_type in person_info.keys():
        x = person_info[dta_type]
        p = x[0]
        if isinstance(p, str):
            continue
        # Get name
        if dta_type in ("fagperson", "evu", "aktiv"):
            etternavn = p["etternavn"]
            fornavn = p["fornavn"]
        if p.has_key("studentnr_tildelt"):
            studentnr = "%06d" % int(p["studentnr_tildelt"])

        # Get affiliations
        if dta_type in ("fagperson",):
            _process_affiliation(
                co.affiliation_tilknyttet,
                co.affiliation_status_tilknyttet_fagperson,
                affiliations,
                _get_sko(p, "faknr", "instituttnr", "gruppenr", "institusjonsnr"),
            )
        elif dta_type in ("aktiv",):
            for row in x:
                # aktiv_sted is necessary in order to avoid different affiliation statuses
                # to a same 'stedkode' to be overwritten
                # e.i. if a person has both affiliations status 'evu' and
                # aktive to a single stedkode we want to register the status 'aktive'
                # in cerebrum
                if studieprog2sko[row["studieprogramkode"]] is not None:
                    aktiv_sted.append(int(studieprog2sko[row["studieprogramkode"]]))
                _process_affiliation(
                    co.affiliation_student,
                    co.affiliation_status_student_aktiv,
                    affiliations,
                    studieprog2sko[row["studieprogramkode"]],
                )
        elif dta_type in ("evu",):
            subtype = co.affiliation_status_student_evu
            if studieprog2sko[row["studieprogramkode"]] in aktiv_sted:
                subtype = co.affiliation_status_student_aktiv
            _process_affiliation(
                co.affiliation_student, subtype, affiliations, studieprog2sko[row["studieprogramkode"]]
            )

    if etternavn is None:
        logger.debug("Ikke noe navn på %s" % fnr)
        no_name += 1
        return

    new_person = Factory.get("Person")(db)
    if fnr2person_id.has_key(fnr):
        new_person.find(fnr2person_id[fnr])

    new_person.populate(mx.DateTime.Date(year, mon, day), gender)

    new_person.affect_names(co.system_fs, co.name_first, co.name_last)
    new_person.populate_name(co.name_first, fornavn)
    new_person.populate_name(co.name_last, etternavn)

    if studentnr is not None:
        new_person.affect_external_id(
            co.system_fs, co.externalid_fodselsnr, co.externalid_studentnr, co.externalid_bewatorid
        )

        new_person.populate_external_id(co.system_fs, co.externalid_studentnr, studentnr)
        logger.debug("Studentnr is %s", studentnr)
        new_bew_id = "01221%06d0" % int(studentnr)
        logger.debug("Adding bewator-ID %s for %s", new_bew_id, studentnr)
        # we have to use system_fs here (for technical reasons) even
        # though we should be using system_manual
        new_person.populate_external_id(co.system_fs, co.externalid_bewatorid, new_bew_id)
    else:
        new_person.affect_external_id(co.system_fs, co.externalid_fodselsnr)
    new_person.populate_external_id(co.system_fs, co.externalid_fodselsnr, fnr)

    ad_post, ad_post_private, ad_street = _calc_address(person_info)
    for address_info, ad_const in (
        (ad_post, co.address_post),
        (ad_post_private, co.address_post_private),
        (ad_street, co.address_street),
    ):
        # TBD: Skal vi slette evt. eksisterende adresse v/None?
        if address_info is not None:
            logger.debug("Populating address...")
            new_person.populate_address(co.system_fs, ad_const, **address_info)
    # if this is a new Person, there is no entity_id assigned to it
    # until written to the database.
    op = new_person.write_db()
    for a in filter_affiliations(affiliations):
        ou, aff, aff_status = a
        new_person.populate_affiliation(co.system_fs, ou, aff, aff_status)
        if include_delete:
            key_a = "%s:%s:%s" % (new_person.entity_id, ou, int(aff))
            if old_aff.has_key(key_a):
                old_aff[key_a] = False

    register_cellphone(new_person, person_info)

    op2 = new_person.write_db()
    if op is None and op2 is None:
        logger.info("**** EQUAL ****")
    elif op == True:
        logger.info("**** NEW ****")
    else:
        logger.info("**** UPDATE ****")

    # Reservations
    if gen_groups:
        should_add = False
        for dta_type in person_info.keys():
            p = person_info[dta_type][0]
            if isinstance(p, str):
                continue
            # Presence of 'fagperson' elements for a person should not
            # affect that person's reservation status.
            if dta_type in ("fagperson",):
                continue
            # We only fetch the column in these queries
            if dta_type not in ("evu"):
                continue
            # If 'status_reserv_nettpubl' == "N": add to group
            if p.get("status_reserv_nettpubl", "") == "N":
                should_add = True
            else:
                should_add = False
        if should_add:
            # The student has explicitly given us permission to be
            # published in the directory.
            _add_res(new_person.entity_id)
        else:
            # The student either hasn't registered an answer to
            # the "Can we publish info about you in the directory"
            # question at all, or has given an explicit "I don't
            # want to appear in the directory" answer.
            _rem_res(new_person.entity_id)
    db.commit()
예제 #28
0
    def _process_student(person_info):
        fnr = fodselsnr.personnr_ok(
            "%06d%05d" %
            (int(person_info['fodselsdato']), int(person_info['personnr'])))
        logger.set_indent(0)
        logger.debug("Callback for %s", fnr)

        logger.set_indent(3)
        pinfo = persons.get(fnr, None)
        if pinfo is None:
            logger.warn("Unknown person %s", fnr)
            return
        logger.debug(pformat(_filter_person_info(person_info)))
        if fnr not in persons:
            logger.warn("(person) not found error for %s", fnr)
            logger.set_indent(0)
            return
        try:
            profile = autostud.get_profile(
                person_info,
                member_groups=persons[fnr].get_groups(),
                person_affs=persons[fnr].get_affiliations())
            logger.debug(profile.matcher.debug_dump())
        except AutoStud.ProfileHandler.NoMatchingProfiles as msg:
            logger.warn("No matching profile error for %s: %s", fnr, msg)
            logger.set_indent(0)
            return
        except AutoStud.ProfileHandler.NoAvailableDisk as msg:
            # pretend that the account was processed so that
            # list_noncallback_users doesn't include the user(s).
            # While this is only somewhat correct behaviour, the
            # NoAvailableDisk situation should be resolved switftly.
            for account_id in pinfo.get_student_ac():
                processed_accounts[account_id] = True
            raise
        processed_students[fnr] = 1
        keep_account_home[fnr] = profile.get_build()['home']
        if fast_test:
            logger.debug(profile.debug_dump())
            # logger.debug("Disk: %s", profile.get_disk())
            logger.set_indent(0)
            return
        try:
            _debug_dump_profile_match(profile, fnr)
            if dryrun:
                logger.set_indent(0)
                return
            if (create_users and not pinfo.has_student_ac()
                    and profile.get_build()['action']):
                if pinfo.has_other_ac():
                    logger.debug("Has active non-student account, skipping")
                    return
                elif pinfo.has_reserved_ac():  # has a reserved account
                    logger.debug("using reserved: %s",
                                 pinfo.get_best_reserved_ac())
                    BuildAccounts._update_persons_accounts(
                        profile, fnr, [pinfo.get_best_reserved_ac()])
                elif pinfo.has_deleted_ac():
                    logger.debug("using deleted: %s",
                                 pinfo.get_best_deleted_ac())
                    BuildAccounts._update_persons_accounts(
                        profile, fnr, [pinfo.get_best_deleted_ac()])
                else:
                    account_id = AccountUtil.create_user(fnr, profile)
                    logger.debug("would create account for %s", fnr)
                    if account_id is None:
                        logger.set_indent(0)
                        return
                # students.setdefault(fnr, {})[account_id] = []
            elif update_accounts and pinfo.has_student_ac():
                BuildAccounts._update_persons_accounts(profile, fnr,
                                                       pinfo.get_student_ac())
        except AutoStud.ProfileHandler.NoAvailableDisk as msg:
            logger.error("  Error for %s: %s", fnr, msg)
        logger.set_indent(0)
        # We commit once for each person to avoid locking too many db-rows
        if not dryrun:
            db.commit()
예제 #29
0
    def person_student_info(self, operator, person_id):
        person_exists = False
        person = None
        try:
            person = self._get_person(*self._map_person_id(person_id))
            person_exists = True
        except CerebrumError as e:
            # Check if person exists in FS, but is not imported yet, e.g.
            # emnestudents. These should only be listed with limited
            # information.
            if person_id and len(person_id) == 11 and person_id.isdigit():
                try:
                    person_id = fodselsnr.personnr_ok(person_id)
                except Exception:
                    raise e
                self.logger.debug('Unknown person %r, asking FS directly',
                                  person_id)
                self.ba.can_get_student_info(operator.get_entity_id(), None)
                fodselsdato, pnum = person_id[:6], person_id[6:]
            else:
                raise e
        else:
            self.ba.can_get_student_info(operator.get_entity_id(), person)
            fnr = person.get_external_id(
                id_type=self.const.externalid_fodselsnr,
                source_system=self.const.system_fs)
            if not fnr:
                raise CerebrumError("No matching fnr from FS")
            fodselsdato, pnum = fodselsnr.del_fnr(fnr[0]['external_id'])
        ret = []
        try:
            db = database.connect(user=cereconf.FS_USER,
                                  service=cereconf.FS_DATABASE_NAME,
                                  DB_driver=cereconf.DB_DRIVER_ORACLE)
        except database.DatabaseError as e:
            self.logger.warn("Can't connect to FS (%s)", text_type(e))
            raise CerebrumError("Can't connect to FS, try later")
        fs = FS(db)
        for row in fs.student.get_undervisningsmelding(fodselsdato, pnum):
            ret.append({
                'undvkode': row['emnekode'],
                'dato': row['dato_endring'],
            })

        har_opptak = set()
        if person_exists:
            for row in fs.student.get_studierett(fodselsdato, pnum):
                har_opptak.add(row['studieprogramkode'])
                ret.append({
                    'studprogkode': row['studieprogramkode'],
                    'studierettstatkode': row['studierettstatkode'],
                    'studentstatkode': row['studentstatkode'],
                    'studieretningkode': row['studieretningkode'],
                    'dato_tildelt': row['dato_studierett_tildelt'],
                    'dato_gyldig_til': row['dato_studierett_gyldig_til'],
                    'privatist': row['status_privatist'],
                })

            for row in fs.student.get_eksamensmeldinger(fodselsdato, pnum):
                programmer = []
                for row2 in fs.info.get_emne_i_studieprogram(row['emnekode']):
                    if row2['studieprogramkode'] in har_opptak:
                        programmer.append(row2['studieprogramkode'])
                ret.append({
                    'ekskode': row['emnekode'],
                    'programmer': ",".join(programmer),
                    'dato': row['dato_opprettet'],
                })

            for row in fs.student.get_utdanningsplan(fodselsdato, pnum):
                ret.append({
                    'studieprogramkode': row['studieprogramkode'],
                    'terminkode_bekreft': row['terminkode_bekreft'],
                    'arstall_bekreft': row['arstall_bekreft'],
                    'dato_bekreftet': row['dato_bekreftet'],
                })

            def _ok_or_not(input):
                """Helper function for proper feedback of status."""
                if not input or input == 'N':
                    return 'Nei'
                if input == 'J':
                    return 'Ja'
                return input

            semregs = tuple(fs.student.get_semreg(fodselsdato, pnum,
                                                  only_valid=False))
            for row in semregs:
                ret.append({
                    'regstatus': _ok_or_not(row['status_reg_ok']),
                    'regformkode': row['regformkode'],
                    'dato_endring': row['dato_endring'],
                    'dato_regform_endret': row['dato_regform_endret'],
                })
                ret.append({
                    'betstatus': _ok_or_not(row['status_bet_ok']),
                    'betformkode': row['betformkode'],
                    'dato_betaling': row['dato_betaling'],
                })
            # The semreg and sembet lines should always be sent, to make it
            # easier for the IT staff to see if a student have paid or not.
            if not semregs:
                ret.append({
                    'regstatus': 'Nei',
                    'regformkode': None,
                    'dato_endring': None,
                    'dato_regform_endret': None,
                })
                ret.append({
                    'betstatus': 'Nei',
                    'betformkode': None,
                    'dato_betaling': None,
                })

        db.close()
        return ret
예제 #30
0
    def next_object(self, element):
        """Return the next SAPPerson object.

        Consume the next XML-element describing a person, and return a
        suitable representation (SAPPerson).

        Should something fail (which prevents this method from constructing a
        proper SAPPerson object), an exception is raised.

        """
        def to_latin1(string):
            import codecs
            import unicodedata
            try:
                return codecs.encode(string, 'latin1')
            except UnicodeEncodeError:
                return codecs.encode(unicodedata.normalize('NFKD', string),
                                     'latin1', 'ignore')

        result = SAPPerson()

        # Per baardj's request, we consider middle names as first names.
        middle = ""
        middle = element.find("Person/Mellomnavn")
        if middle is not None and middle.text:
            middle = to_latin1(middle.text).strip()

        # Iterate over *all* subelements, 'fill up' the result object
        for sub in element.getiterator():
            value = None
            if sub.text:
                value = to_latin1(sub.text.strip())

            if sub.tag == "Fornavn":
                if middle:
                    value += " " + middle

                # IVR 2007-05-30 FIXME: This is not pretty.
                #
                # In an e-mail from 2007-05-29, Johannes Paulsen suggests that
                # marking invalid entries with '*' in the some of the name
                # elements is the easiest approach. This is an ugly hack, but
                # since the invalid entries will not disappear anytime soon,
                # this is the easiest way of skipping them.
                #
                # JAZZ 2007-08-01
                # '*' did not work all that well as it is used as common
                # wildcard in SAP. Johannes suggests that we use '@' in
                # stead. As the data is not updated yet (we don't know when
                # that will happen) we need to test for '*' as well in order
                # to skip all the invalid elements
                #
                if '*' in value or '@' in value:
                    if self.logger:
                        self.logger.debug("Name contains '@' or '*', ignored")
                    # Since the element is marked as void, there is no need to
                    # process further (we have no guarantee that any data
                    # would make sense and we won't have even more spurious
                    # warnings).
                    return None
                result.add_name(DataName(self.tag2type[sub.tag], value))
            elif sub.tag == "Etternavn":
                if '*' in value or '@' in value:
                    if self.logger:
                        self.logger.debug("Name contains '@' or '*', ignored")
                    # Se <Fornavn>.
                    return None
                result.add_name(DataName(self.tag2type[sub.tag], value))
            elif sub.tag == "Fodselsnummer" and value is not None:
                result.add_id(self.tag2type[sub.tag], personnr_ok(value))
            elif sub.tag == "Ansattnummer":
                result.add_id(self.tag2type[sub.tag], value)
                self.logger.debug(value)
            elif sub.tag == "Fodselsdato":
                result.birth_date = self._make_mxdate(value, format="%Y-%m-%d")
            elif sub.tag == "Kjonn":
                result.gender = self.tag2type[value]
            elif sub.tag == "Adresse":
                result.add_address(self._make_address(sub))
            elif sub.tag in ("Hovedstilling", "Bistilling"):
                emp = self._make_employment(sub)
                if emp is not None:
                    result.add_employment(emp)
            elif sub.tag == "Roller" and sub.findtext("IKKE-ANGIT") is None:
                emp = self._make_role(sub)
                if emp is not None:
                    result.add_employment(emp)
            elif sub.tag == "Person":
                # Lots of the other entries above also are part of the
                # "person"-firstlevel element, but we need to
                # specifically look here for Tittel => personal title,
                # to avoid confusion with worktitles
                for subsub in sub.findall("Tittel"):
                    personal_title = self._make_title(HRDataPerson.NAME_TITLE,
                                                      subsub)
                    if personal_title:
                        result.add_name(personal_title)
            elif sub.tag == "PersonligID":
                # Store additional person ids, like passport numbers.
                # Handle passport numbers
                if sub.find('Type').text in self.sap2idtype:
                    # Add the passport number to the data-structure
                    result.add_id(
                        self.sap2idtype[sub.find('Type').text], "%s-%s" %
                        (sub.find('Land').text, sub.find('Verdi').text))
                else:
                    self.logger.debug("Unknown %s type '%s': skipping id type",
                                      sub.tag,
                                      sub.find('Type').text)
            elif sub.tag == "SGM":
                # New feature and unique (for now?) for UiO is SGM,
                # external attachments for person.
                self.logger.debug("SGM for %s", result)
                result.add_external_work(self._make_sgm(sub))
        # We need to order 'Telefon 1' and 'Telefon 2' properly
        celems = list(element.findall("Kommunikasjon"))
        celems.sort(lambda x, y: cmp(x.find("Type").text, y.find("Type").text))
        # TBD: Priorities!
        priority = 0
        for ct in celems:
            contact = self._make_contact(ct, priority)
            if contact:
                result.add_contact(contact)
                priority += 1

        # Reservations for catalogue publishing
        # default: One active employment => can be published
        to_reserve = not result.has_active_employments()

        # Everyone with 'RESE' is reserved (regardless of everything else)
        # If no 'RESE' exists, but there is a 'SAMT' => no reservation
        for i in element.findall("Adresse/Reservert"):
            if i.text:
                tmp = i.text.strip()
                if tmp == "RESE":
                    to_reserve = True
                    break
                elif tmp == "SAMT":
                    to_reserve = False
        result.reserved = to_reserve

        # Address magic
        # If there is a sensible 'Sted for lønnsslipp', it will result i
        # proper "post/besøksaddresse" later. This code matches LT's behaviour
        # more closely (an employee 'inherits' the address of his/her
        # "primary" workplace.
        for sub in element.getiterator("Kommunikasjon"):
            txt = sub.findtext("Type")
            val = sub.findtext("Verdi")
            if (txt and txt.encode("latin1") == "Sted for lønnslipp" and val
                    # *some* of the entries have a space here and there.
                    # and some contain non-digit data
                    and val.replace(" ", "").isdigit()):
                val = val.replace(" ", "")
                fak, inst, gruppe = [
                    int(x) for x in (val[:2], val[2:4], val[4:])
                ]
                result.primary_ou = (cereconf.DEFAULT_INSTITUSJONSNR, fak,
                                     inst, gruppe)

        # We require people to have first/last name.
        if not (result.get_name(result.NAME_FIRST)
                and result.get_name(result.NAME_LAST)):
            self.logger.warn(
                "People must have first and last names. %s skipped",
                list(result.iterids()))
            return None

        return result
예제 #31
0
    def new_project(self, input):
        """Create a given project.

        TODO: A lot of this code should be moved into e.g. TSD's OU mixin, or
        somewhere else to be usable both by various scripts and bofhd.

        @type input: dict
        @param input: The survey answers about the requested project.

        """
        pname = input['p_id']
        logger.info('New project: %s', pname)
        ou = self._create_ou(input)

        # Update the requestee for the project:
        pe = self._get_person()
        self._update_person(pe, input)

        # Give the respondent an affiliation to the project.
        # If the respondent sets himself as the Project Owner (responsible), it
        # gets status as the owner. Otherwise we give him PA status:
        # TBD: do we need to differentiate between owner and PA?
        status = co.affiliation_status_project_admin
        if self.fnr == input['rek_owner']:
            status = co.affiliation_status_project_owner
        pe.populate_affiliation(source_system=co.system_nettskjema,
                                ou_id=ou.entity_id, status=status,
                                affiliation=co.affiliation_project)
        pe.write_db()

        # Check the responsible and give access to the project by an
        # affiliation:
        if self.fnr != input['rek_owner']:
            # TODO: Should we create a person with this ID or not?
            try:
                pe2 = self._get_person(input['rek_owner'],
                                       create_nonexisting=False)
                pe2.populate_affiliation(source_system=co.system_nettskjema,
                                         ou_id=ou.entity_id,
                                         affiliation=co.affiliation_project,
                                         status=co.affiliation_status_project_owner)
                # Note that no name or anything else is now set, so we wait with the
                # account.
                pe2.write_db()
            except Errors.NotFoundError:
                logger.warn("Project owner not found: %s", input['rek_owner'])

        # Give the PA an account:
        ac = self._create_account(pe, ou, input['pa_username'])

        # Fill the pre approve list with external ids:
        pre_approve_list = set()
        for fnr in set(input['p_persons'].split()):
            for fnr1 in set(fnr.split(',')):
                try:
                    fnr1 = fodselsnr.personnr_ok(fnr1)
                except fodselsnr.InvalidFnrError:
                    logger.debug("Ignoring invalid fnr: %s", fnr1)
                    continue
                except ValueError:
                    logger.debug("Ignoring invalid fnr: %s", fnr1)
                    continue
                pre_approve_list.add(fnr1)
        if pre_approve_list:
            logger.debug("Pre approvals: %s", ', '.join(pre_approve_list))
            ou.add_pre_approved_persons(pre_approve_list)
            ou.write_db()
        # TODO: How should we signal that a new project is waiting for approval?
        return True
예제 #32
0
def fetch_data(drgrad_file, fritak_kopiavg_file, betalt_papir_file,
               sysname, person_file):
    """Finner alle personer som rammes av kvoteordningen ved å:

    - finne alle som har en student-affiliation (0.1)
    - ta bort ansatte (1.2.1)
    - ta bort dr-grads stipendiater (1.2.2)

    I.e. vi fjerner alle som har fullstendig fritak fra ordningen.  De
    som kun har fritak fra kopiavgiften er ikke fjernet.

    Finner alle gruppe-medlemskap, enten via person eller account for
    de som rammes av ordningen.

    Finner fritak fra kopiavgift (1.2.3 og 1.2.4)
    """

    logger.debug("Prefetching data")

    betaling_fritak = get_bet_fritak_utv_data(sysname, person_file)
    logger.debug2("Fritak for: %r", betaling_fritak)
    # Finn alle som skal rammes av kvoteregimet
    quota_victim = {}

    for pid in get_students():
        quota_victim[pid] = True

    # Ta bort de som ikke har konto
    account = Account.Account(db)
    account_id2pid = {}
    has_account = {}
    for row in account.list_accounts_by_type(fetchall=False):
        account_id2pid[int(row['account_id'])] = int(row['person_id'])
        has_account[int(row['person_id'])] = True
    for p_id in quota_victim.keys():
        if p_id not in has_account:
            del(quota_victim[p_id])
    logger.debug("after removing non-account people: %i" % len(quota_victim))

    # Ansatte har fritak
    # TODO: sparer litt ytelse ved å gjøre dette i get_students()
    for row in person.list_affiliations(
            affiliation=const.affiliation_ansatt,
            status=(const.affiliation_status_ansatt_bil,
                    const.affiliation_status_ansatt_vit,
                    const.affiliation_status_ansatt_tekadm,),
            source_system=const.system_sap,
            include_deleted=False, fetchall=False
    ):
        if int(row['person_id']) in quota_victim:
            del(quota_victim[int(row['person_id'])])
    logger.debug("removed employees: %i" % len(quota_victim))

    # Alle personer som har disse typer tilknyttet affiliation skal ha fritak
    for row in person.list_affiliations(
            affiliation=const.affiliation_tilknyttet,
            status=(const.affiliation_tilknyttet_bilag,
                    const.affiliation_tilknyttet_ekst_forsker,
                    const.affiliation_tilknyttet_ekst_partner,
                    const.affiliation_tilknyttet_emeritus,
                    const.affiliation_tilknyttet_gjesteforsker,
                    const.affiliation_tilknyttet_innkjoper,),
            source_system=const.system_sap,
            include_deleted=False, fetchall=False
    ):
        if int(row['person_id']) in quota_victim:
            del(quota_victim[int(row['person_id'])])
    logger.debug("removed tilknyttet people: %i" % len(quota_victim))

    # Mappe fødselsnummer til person-id
    fnr2pid = {}
    for p in person.search_external_ids(source_system=const.system_fs,
                                        id_type=const.externalid_fodselsnr,
                                        fetchall=False):
        fnr2pid[p['external_id']] = int(p['entity_id'])

    # Dr.grads studenter har fritak
    for row in GeneralDataParser(drgrad_file, "drgrad"):
        fnr = fodselsnr.personnr_ok("%06d%05d" % (int(row['fodselsdato']),
                                                  int(row['personnr'])))
        pid = fnr2pid.get(fnr, None)
        if not pid:
            continue
        if pid in quota_victim:
            del(quota_victim[pid])
    logger.debug("removed drgrad: %i" % len(quota_victim))

    # map person-id til gruppemedlemskap.  Hvis gruppe-medlemet er av
    # typen konto henter vi ut owner_id.
    person_id_member = {}
    group = Group.Group(db)
    count = [0, 0]
    groups = autostud.pc.group_defs.keys()
    logger.debug("Finding members in %s" % groups)
    for g in groups:
        group.clear()
        group.find(g)
        for m in set([int(row["member_id"]) for row in
                      group.search_members(group_id=group.entity_id,
                                           indirect_members=True,
                                           member_type=const.entity_account)]):
            if m in account_id2pid:
                person_id_member.setdefault(account_id2pid[m], []).append(g)
                count[0] += 1
            else:
                person_id_member.setdefault(m, []).append(g)
                count[1] += 1
    logger.debug("memberships: persons:%i, p_m=%i, a_m=%i",
                 len(person_id_member), count[1], count[0])

    # Fetch any affiliations used as select criteria
    person_id_affs = {}
    for sm in autostud.pc.select_tool.select_map_defs.values():
        if not isinstance(sm, AutoStud.Select.SelectMapPersonAffiliation):
            continue
        for aff_attrs in sm._select_map.keys():
            affiliation = aff_attrs[0]
            for row in person.list_affiliations(
                affiliation=affiliation, include_deleted=False,
                fetchall=False
            ):
                person_id_affs.setdefault(int(row['person_id']), []).append(
                    (int(row['ou_id']),
                     int(row['affiliation']),
                     int(row['status'])))

    # fritak fra selve kopiavgiften (1.2.3 og 1.2.4)
    kopiavgift_fritak = {}
    for row in GeneralDataParser(fritak_kopiavg_file, "betfritak"):
        fnr = fodselsnr.personnr_ok("%06d%05d" % (int(row['fodselsdato']),
                                                  int(row['personnr'])))
        pid = fnr2pid.get(fnr, None)
        if not pid:
            continue
        kopiavgift_fritak[pid] = True
    logger.debug("%i personer har kopiavgiftsfritak" % len(kopiavgift_fritak))

    # De som har betalt kopiavgiften
    har_betalt = {}
    for row in GeneralDataParser(betalt_papir_file, "betalt"):
        fnr = fodselsnr.personnr_ok("%06d%05d" % (int(row['fodselsdato']),
                                                  int(row['personnr'])))
        pid = fnr2pid.get(fnr, None)
        if not pid:
            continue
        har_betalt[pid] = True
    logger.debug("%i personer har betalt kopiavgift" % len(har_betalt))

    # Hent gratis-tildelinger hittil i år
    free_this_term = {}
    for row in ppq.get_history_payments(
        transaction_type=const.pqtt_quota_fill_free,
        desc_mask=term_init_prefix+'%%'
    ):
        free_this_term.setdefault(int(row['person_id']), []).append(
            row['description'][len(term_init_prefix):])
    logger.debug("free_this_term: %i" % len(free_this_term))

    logger.debug("Prefetch returned %i students" % len(quota_victim))
    n = 0
    for pid in betaling_fritak.keys():
        if pid in quota_victim:
            n += 1
    logger.debug("%i av disse har betaling_fritak" % n)
    return (fnr2pid, quota_victim, person_id_member, person_id_affs,
            kopiavgift_fritak, har_betalt, free_this_term, betaling_fritak)
예제 #33
0
    def next_object(self, element):
        def get_value(element_value):
            return ensure_unicode(element_value, self.encoding)

        def extract(element_attr):
            return get_value(element.get(element_attr, ""))

        result = HRDataPerson()

        # Pull out all names
        self._register_names(result, element)
        # Pull out fnr
        tmp = "%02d%02d%02d%05d" % tuple([
            int(element.get(x))
            for x in ("fodtdag", "fodtmnd", "fodtar", "personnr")
        ])
        fnr = fodselsnr.personnr_ok(tmp)
        result.add_id(result.NO_SSN, fnr)
        # Since LT does not provide birth date directly, we extract it from fnr
        result.birth_date = Date(*fodselsnr.fodt_dato(fnr))
        # ... and gender
        if fodselsnr.er_mann(fnr):
            result.gender = result.GENDER_MALE
        else:
            result.gender = result.GENDER_FEMALE
        # fi
        # Register address
        # extract = lambda y: ensure_unicode(element.get(y, ""), self.encoding)
        result.address = DataAddress(
            kind=DataAddress.ADDRESS_PRIVATE,
            street=(extract("adresselinje1_privatadresse"),
                    extract("adresselinje2_privatadresse")),
            zip=extract("poststednr_privatadresse"),
            city=extract("poststednavn_privatadresse"))

        # Contact information and jobs
        # FIXME: We do not have anything more intelligent for priorities
        priorities = dict()
        for sub in element.getiterator():
            if sub.tag in (
                    "bilag",
                    "gjest",
                    "tils",
            ):
                emp = self._make_employment(sub)
                result.add_employment(emp)
            elif sub.tag in (
                    "komm",
                    "arbtlf",
            ):
                for contact in self._make_contact(sub, priorities):
                    result.add_contact(contact)
                # od
        # od

        # Reservation rules. Roughly, all employees are not reserved, unless
        # they say otherwise. Everyone else *is* reserved, unless they
        # explicitly allow publication in catalogues.
        has_active = result.has_active_employments()
        if has_active:
            to_reserve = False
            for resv in element.findall("res"):
                if (resv.get("katalogkode") == "ELKAT" and
                        resv.get("felttypekode") not in ("PRIVADR", "PRIVTLF")
                        and resv.get("resnivakode") != "SAMTYKKE"):
                    to_reserve = True
        else:
            to_reserve = True
            for resv in element.findall("res"):
                if (resv.get("katalogkode") == "ELKAT"
                        and resv.get("felttypekode")
                        not in ("PRIVADR", "PRIVTLF")):
                    to_reserve = resv.get("resnivakode") != "SAMTYKKE"
        result.reserved = to_reserve
        if (element.get("fakultetnr_for_lonnsslip")
                and element.get("instituttnr_for_lonnsslip")
                and element.get("gruppenr_for_lonnsslip")):
            result.primary_ou = (cereconf.DEFAULT_INSTITUSJONSNR,
                                 extract("fakultetnr_for_lonnsslip"),
                                 extract("instituttnr_for_lonnsslip"),
                                 extract("gruppenr_for_lonnsslip"))

        if not (result.get_name(result.NAME_FIRST)
                and result.get_name(result.NAME_LAST)):
            raise AssertionError("Missing name for %s" %
                                 list(result.iterids()))

        return result
예제 #34
0
def process_person_callback(person_info):
    """Called when we have fetched all data on a person from the xml
    file.  Updates/inserts name, address and affiliation
    information."""
    global no_name
    try:
        fnr = fodselsnr.personnr_ok("%06d%05d" % (
            int(person_info['fodselsdato']),
            int(person_info['personnr'])))
        fnr = fodselsnr.personnr_ok(fnr)
        logger.info("Processing %s", fnr)

        (year, mon, day) = fodselsnr.fodt_dato(fnr)
        if (year < 1970
                and getattr(cereconf, "ENABLE_MKTIME_WORKAROUND", 0) == 1):
            # Seems to be a bug in time.mktime on some machines
            year = 1970
    except fodselsnr.InvalidFnrError:
        logger.warn(u"Ugyldig f�dselsnr for: %s",
                    person_info['fodselsdato'])
        return

    gender = co.gender_male
    if(fodselsnr.er_kvinne(fnr)):
        gender = co.gender_female

    etternavn = fornavn = None
    studentnr = None
    affiliations = []
    address_info = None
    aktiv_sted = []
    # Iterate over all person_info entries and extract relevant data
    for dta_type in person_info.keys():
        x = person_info[dta_type]
        p = x[0]
        if isinstance(p, str):
            continue
        if dta_type not in ('tilbud', 'eksamen', 'evu'):
            if 'studentnr_tildelt' in p:
                studentnr = p['studentnr_tildelt']
            else:
                logger.info("\n%s mangler studentnr!", fnr)
        # Get name
        if dta_type in ('aktiv', 'tilbud', 'evu', 'privatist_studieprogram', ):
            etternavn = p['etternavn']
            fornavn = p['fornavn']
        # Get address
        if address_info is None:
            if dta_type in ('aktiv', 'privatist_studieprogram', ):
                address_info = _ext_address_info(
                    p,
                    'adrlin1_semadr', 'adrlin2_semadr',
                    'adrlin3_semadr', 'postnr_semadr',
                    'adresseland_semadr')
                if address_info is None:
                    address_info = _ext_address_info(
                        p,
                        'adrlin1_hjemsted', 'adrlin2_hjemsted',
                        'adrlin3_hjemsted', 'postnr_hjemsted',
                        'adresseland_hjemsted')
            elif dta_type in ('evu',):
                address_info = _ext_address_info(
                    p, 'adrlin1_hjem',
                    'adrlin2_hjem', 'adrlin3_hjem', 'postnr_hjem',
                    'adresseland_hjem')
                if address_info is None:
                    address_info = _ext_address_info(
                        p,
                        'adrlin1_hjemsted', 'adrlin2_hjemsted',
                        'adrlin3_hjemsted', 'postnr_hjemsted',
                        'adresseland_hjemsted')
            elif dta_type in ('tilbud',):
                address_info = _ext_address_info(
                    p,
                    'adrlin1_kontakt', 'adrlin2_kontakt',
                    'adrlin3_kontakt', 'postnr_kontakt',
                    'adresseland_kontakt')

        # Get affiliations
        # Lots of changes here compared to import_FS.py @ uio
        # TODO: split import_FS into a common part and organization spesific
        # parts
        if dta_type in ('aktiv', ):
            for row in x:
                # aktiv_sted is necessary in order to avoid different
                # affiliation statuses to a same 'stedkode' to be overwritten
                # e.i. if a person has both affiliations status 'tilbud' and
                # aktive to a single stedkode we want to register the status
                # 'aktive' in cerebrum
                if studieprog2sko[row['studieprogramkode']] is not None:
                    aktiv_sted.append(
                        int(studieprog2sko[row['studieprogramkode']]))
                    _process_affiliation(
                        co.affiliation_student,
                        co.affiliation_status_student_aktiv, affiliations,
                        studieprog2sko[row['studieprogramkode']])
        elif dta_type in ('evu',):
            for row in x:
                _process_affiliation(
                    co.affiliation_student,
                    co.affiliation_status_student_evu, affiliations,
                    _get_sko(p, 'faknr_adm_ansvar',
                             'instituttnr_adm_ansvar',
                             'gruppenr_adm_ansvar'))
        elif dta_type in ('privatist_studieprogram', ):
            for row in x:
                _process_affiliation(
                    co.affiliation_student,
                    co.affiliation_status_student_privatist,
                    affiliations, studieprog2sko[row['studieprogramkode']])
        elif dta_type in ('tilbud', ):
            for row in x:
                subtype = co.affiliation_status_student_tilbud
                if studieprog2sko[row['studieprogramkode']] in aktiv_sted:
                    subtype = co.affiliation_status_student_aktiv
                _process_affiliation(co.affiliation_student,
                                     subtype, affiliations,
                                     studieprog2sko[row['studieprogramkode']])
    if etternavn is None:
        logger.info("Ikke noe navn p� %s", fnr)
        no_name += 1
        return

    # TODO: If the person already exist and has conflicting data from
    # another source-system, some mechanism is needed to determine the
    # superior setting.
    fsids = [(co.externalid_fodselsnr, fnr)]
    if studentnr is not None:
        fsids.append((co.externalid_studentnr, studentnr))

    new_person = Factory.get('Person')(db)
    try:
        new_person.find_by_external_ids(*fsids)
    except Errors.NotFoundError:
        pass
    except Errors.TooManyRowsError, e:
        logger.error("Trying to find studentnr %s, getting several persons: %s",
                     studentnr, e)
        return
예제 #35
0
    def next_object(self, element):

        def get_value(element_value):
            return ensure_unicode(element_value, self.encoding)

        def extract(element_attr):
            return get_value(element.get(element_attr, ""))

        result = HRDataPerson()

        # Pull out all names
        self._register_names(result, element)
        # Pull out fnr
        tmp = "%02d%02d%02d%05d" % tuple([int(element.get(x))
                                          for x in ("fodtdag",
                                                    "fodtmnd",
                                                    "fodtar",
                                                    "personnr")])
        fnr = fodselsnr.personnr_ok(tmp)
        result.add_id(result.NO_SSN, fnr)
        # Since LT does not provide birth date directly, we extract it from fnr
        result.birth_date = Date(*fodselsnr.fodt_dato(fnr))
        # ... and gender
        if fodselsnr.er_mann(fnr):
            result.gender = result.GENDER_MALE
        else:
            result.gender = result.GENDER_FEMALE
        # fi
        # Register address
        # extract = lambda y: ensure_unicode(element.get(y, ""), self.encoding)
        result.address = DataAddress(
            kind=DataAddress.ADDRESS_PRIVATE,
            street=(extract("adresselinje1_privatadresse"),
                    extract("adresselinje2_privatadresse")),
            zip=extract("poststednr_privatadresse"),
            city=extract("poststednavn_privatadresse"))

        # Contact information and jobs
        # FIXME: We do not have anything more intelligent for priorities
        priorities = dict()
        for sub in element.getiterator():
            if sub.tag in ("bilag", "gjest", "tils",):
                emp = self._make_employment(sub)
                result.add_employment(emp)
            elif sub.tag in ("komm", "arbtlf",):
                for contact in self._make_contact(sub, priorities):
                    result.add_contact(contact)
                # od
        # od

        # Reservation rules. Roughly, all employees are not reserved, unless
        # they say otherwise. Everyone else *is* reserved, unless they
        # explicitly allow publication in catalogues.
        has_active = result.has_active_employments()
        if has_active:
            to_reserve = False
            for resv in element.findall("res"):
                if (resv.get("katalogkode") == "ELKAT" and
                        resv.get("felttypekode") not in
                        ("PRIVADR", "PRIVTLF") and
                        resv.get("resnivakode") != "SAMTYKKE"):
                    to_reserve = True
        else:
            to_reserve = True
            for resv in element.findall("res"):
                if (resv.get("katalogkode") == "ELKAT" and
                        resv.get("felttypekode") not in
                        ("PRIVADR", "PRIVTLF")):
                    to_reserve = resv.get("resnivakode") != "SAMTYKKE"
        result.reserved = to_reserve
        if (element.get("fakultetnr_for_lonnsslip") and
                element.get("instituttnr_for_lonnsslip") and
                element.get("gruppenr_for_lonnsslip")):
            result.primary_ou = (cereconf.DEFAULT_INSTITUSJONSNR,
                                 extract("fakultetnr_for_lonnsslip"),
                                 extract("instituttnr_for_lonnsslip"),
                                 extract("gruppenr_for_lonnsslip"))

        if not (result.get_name(result.NAME_FIRST) and
                result.get_name(result.NAME_LAST)):
            raise AssertionError("Missing name for %s" %
                                 list(result.iterids()))

        return result
예제 #36
0
def process_person_callback(person_info):
    """Called when we have fetched all data on a person from the xml
    file.  Updates/inserts name, address and affiliation
    information."""
    
    global no_name
    try:
        fnr = fodselsnr.personnr_ok("%06d%05d" % (int(person_info['fodselsdato']),
                                                  int(person_info['personnr'])))
        fnr = fodselsnr.personnr_ok(fnr)
        logger.info("Process %s " % (fnr))
        (year, mon, day) = fodselsnr.fodt_dato(fnr)
        if (year < 1970
            and getattr(cereconf, "ENABLE_MKTIME_WORKAROUND", 0) == 1):
            # Seems to be a bug in time.mktime on some machines
            year = 1970
    except fodselsnr.InvalidFnrError:
        logger.warn("Ugyldig fødselsnr: %s" % fnr)
        return

    gender = co.gender_male
    if(fodselsnr.er_kvinne(fnr)):
        gender = co.gender_female

    etternavn = fornavn = None
    studentnr = None
    affiliations = []
    address_info = None
    aktiv_sted = []

    # Iterate over all person_info entries and extract relevant data    
    if person_info.has_key('aktiv'):
        for row in person_info['aktiv']:
            if studieprog2sko[row['studieprogramkode']] is not None:
                aktiv_sted.append(int(studieprog2sko[row['studieprogramkode']]))

    for dta_type in person_info.keys():
        x = person_info[dta_type]
        p = x[0]
        if isinstance(p, str):
            continue
        # Get name
        if dta_type in ('fagperson', 'evu', 'aktiv'):
            etternavn = p['etternavn']
            fornavn = p['fornavn']
        if p.has_key('studentnr_tildelt'):
            studentnr = p['studentnr_tildelt']
    
        # Get affiliations
        if dta_type in ('fagperson',):
            _process_affiliation(co.affiliation_tilknyttet,
                                 co.affiliation_status_tilknyttet_fagperson,
                                 affiliations, _get_sko(p, 'faknr',
                                 'instituttnr', 'gruppenr', 'institusjonsnr'))
        elif dta_type in ('aktiv', ):
 	  for row in x:
	      # aktiv_sted is necessary in order to avoid different affiliation statuses
	      # to a same 'stedkode' to be overwritten 
              # e.i. if a person has both affiliations status 'evu' and
	      # aktive to a single stedkode we want to register the status 'aktive'
	      # in cerebrum
              if studieprog2sko[row['studieprogramkode']] is not None:
                  aktiv_sted.append(int(studieprog2sko[row['studieprogramkode']]))
		  _process_affiliation(co.affiliation_student,
				       co.affiliation_status_student_aktiv, affiliations,
				       studieprog2sko[row['studieprogramkode']])
	elif dta_type in ('evu',):
            subtype = co.affiliation_status_student_evu
            if studieprog2sko[row['studieprogramkode']] in aktiv_sted:
                subtype = co.affiliation_status_student_aktiv
            _process_affiliation(co.affiliation_student,
                                 subtype, affiliations,
                                 studieprog2sko[row['studieprogramkode']])

    if etternavn is None:
        logger.debug("Ikke noe navn på %s" % fnr)
        no_name += 1 
        return

    # TODO: If the person already exist and has conflicting data from
    # another source-system, some mechanism is needed to determine the
    # superior setting.
    
    new_person = Factory.get('Person')(db)
    if fnr2person_id.has_key(fnr):
        new_person.find(fnr2person_id[fnr])

    new_person.populate(mx.DateTime.Date(year, mon, day), gender)

    new_person.affect_names(co.system_fs, co.name_first, co.name_last)
    new_person.populate_name(co.name_first, fornavn)
    new_person.populate_name(co.name_last, etternavn)

    if studentnr is not None:
        new_person.affect_external_id(co.system_fs,
                                      co.externalid_fodselsnr,
                                      co.externalid_studentnr)
        new_person.populate_external_id(co.system_fs, co.externalid_studentnr,
                                        studentnr)
    else:
        new_person.affect_external_id(co.system_fs,
                                      co.externalid_fodselsnr)
    new_person.populate_external_id(co.system_fs, co.externalid_fodselsnr, fnr)

    ad_post, ad_post_private, ad_street = _calc_address(person_info)
    for address_info, ad_const in ((ad_post, co.address_post),
                                   (ad_post_private, co.address_post_private),
                                   (ad_street, co.address_street)):
        # TBD: Skal vi slette evt. eksisterende adresse v/None?
        if address_info is not None:
            logger.debug("Populating address...")
            new_person.populate_address(co.system_fs, ad_const, **address_info)

    # if this is a new Person, there is no entity_id assigned to it
    # until written to the database.
    op = new_person.write_db()

    for a in filter_affiliations(affiliations):
        ou, aff, aff_status = a
        new_person.populate_affiliation(co.system_fs, ou, aff, aff_status)
        if include_delete:
            key_a = "%s:%s:%s" % (new_person.entity_id,ou,int(aff))
            if old_aff.has_key(key_a):
                old_aff[key_a] = False
    
    register_cellphone(new_person, person_info)

    op2 = new_person.write_db()
    if op is None and op2 is None:
        logger.info("**** EQUAL ****")
    elif op == True:
        logger.info("**** NEW ****")
    else:
        logger.info("**** UPDATE ****")

    register_fagomrade(new_person, person_info)

    # Reservations    
    if gen_groups:
        should_add = False
        if person_info.has_key('nettpubl'):
            for row in person_info['nettpubl']:
                if row.get('akseptansetypekode', "") == "NETTPUBL" and row.get('status_svar', "") == "J":
                    should_add = True

        if should_add:
            # The student has explicitly given us permission to be
            # published in the directory.
            _add_res(new_person.entity_id)
        else:
            # The student either hasn't registered an answer to
            # the "Can we publish info about you in the directory"
            # question at all, or has given an explicit "I don't
            # want to appear in the directory" answer.
            _rem_res(new_person.entity_id)

    db.commit()
예제 #37
0
    def _process_person(self, person):
        db = self.db
        const = self.const
        paga_nr = int(person['ansattnr'])
        logger.info("Processing paga_id=%r", paga_nr)

        try:
            birthdate = parse_date(person['fodselsdato'])
        except ValueError as e:
            raise SkipPerson("Invalid birth date (%s)" % (e, ))

        if person['kjonn'] == 'M':
            gender = const.gender_male
        else:
            gender = const.gender_female

        fnr = person['fnr']
        if fnr and fnr[6:11] != '00000':
            try:
                fodselsnr.personnr_ok(fnr)
            except Exception as e:
                raise SkipPerson("Invalid fnr (%s)" % (e, ))

            gender_chk = const.gender_male
            if fodselsnr.er_kvinne(fnr):
                gender_chk = const.gender_female

            if gender_chk != gender:
                raise SkipPerson("Inconsistent gender (gender=%r, ssn=%r)" %
                                 (gender, gender_chk))

            fnr_date = datetime.date(*(fodselsnr.fodt_dato(fnr)))
            if not cmp_birthdates(birthdate, fnr_date):
                raise SkipPerson("Inconsistent birth date (date=%r, ssn=%r)" %
                                 (birthdate, fnr_date))

        # Passport data
        national_id_type = person['edag_id_type']
        origin_country = person['country']
        if not origin_country:
            origin_country = 'NO'
        if person['edag_id_nr']:
            # generate external id on the form:
            # <2-char national id>-<national_id>
            national_id_val = '%s-%s' % (origin_country, person['edag_id_nr'])
        else:
            national_id_val = None

        # Abort early if there are no valid identifiers from the source system:
        if not any((
                fnr and fnr[6:11] != '00000',
                national_id_type == 'passnummer' and national_id_val,
        )):
            # TODO: Wouldn't it be enough with ansattnr?
            raise SkipPerson("No valid identifier (fnr, passnummer)")

        new_person = Factory.get('Person')(db)

        identifiers = [(const.externalid_paga_ansattnr, paga_nr),
                       (const.externalid_fodselsnr, fnr)]
        if national_id_type == 'passnummer' and national_id_val:
            identifiers.append((const.externalid_pass_number, national_id_val))
        for id_type, id_value in identifiers:
            if _populate_existing(new_person, id_type, id_value):
                break

        if not person.get('fornavn', '').strip():
            raise SkipPerson("Missing first name")
        if not person.get('etternavn', '').strip():
            raise SkipPerson("Missing last name")

        new_person.populate(mx.DateTime.DateFrom(birthdate), gender)

        new_person.affect_names(const.system_paga, const.name_first,
                                const.name_last)
        new_person.affect_external_id(const.system_paga,
                                      const.externalid_fodselsnr,
                                      const.externalid_paga_ansattnr,
                                      const.externalid_pass_number)
        new_person.populate_name(const.name_first, person['fornavn'])
        new_person.populate_name(const.name_last, person['etternavn'])

        if fnr and fnr[6:11] != '00000':
            # do not import external id where external_id type is fnr and
            # fnr[6-11] =='00000'
            new_person.populate_external_id(const.system_paga,
                                            const.externalid_fodselsnr, fnr)
        if national_id_type == 'passnummer' and national_id_val:
            new_person.populate_external_id(const.system_paga,
                                            const.externalid_pass_number,
                                            national_id_val)
        new_person.populate_external_id(const.system_paga,
                                        const.externalid_paga_ansattnr,
                                        paga_nr)

        # If it's a new person, we need to call write_db() to have an entity_id
        # assigned to it.
        op = new_person.write_db()

        if person.get('tittel_personlig'):
            new_person.add_name_with_language(
                name_variant=const.personal_title,
                name_language=const.language_nb,
                name=person['tittel_personlig'])

        # work_title is set by _determine_affiliations
        affiliations = _determine_affiliations(self.db, self.const, new_person,
                                               person)
        new_person.populate_affiliation(const.system_paga)
        if 'fakultetnr_for_lonnsslip' in person:
            sted = get_sted(db, person['fakultetnr_for_lonnsslip'],
                            person['instituttnr_for_lonnsslip'],
                            person['gruppenr_for_lonnsslip'])
            if sted is not None:
                if sted['addr_street'] is not None:
                    new_person.populate_address(const.system_paga,
                                                type=const.address_street,
                                                **sted['addr_street'])
                if sted['addr_post'] is not None:
                    new_person.populate_address(const.system_paga,
                                                type=const.address_post,
                                                **sted['addr_post'])

        if 'lokasjon' in person:
            logger.debug(
                'Populating paga_id=%r location address with '
                'source=%s, type=%s, text=%r', paga_nr, const.system_paga,
                const.address_location, person['lokasjon'])
            new_person.populate_address(source_system=const.system_paga,
                                        type=const.address_location,
                                        address_text=person['lokasjon'])
        else:
            logger.warning("No location address for paga_id=%r", paga_nr)

        for k, v in affiliations.items():
            ou_id, aff, aff_stat = v
            new_person.populate_affiliation(const.system_paga, ou_id, int(aff),
                                            int(aff_stat))
            self.old_affs.discard(k)
        c_prefs = {}
        new_person.populate_contact_info(const.system_paga)

        for c_type, value in determine_contact(const, person):
            c_type = int(c_type)
            pref = c_prefs.get(c_type, 0)
            new_person.populate_contact_info(const.system_paga, c_type, value,
                                             pref)
            c_prefs[c_type] = pref + 1

        #
        # Also add personal/home street address if it exists in the import file
        #
        priv_addr = (person.get('adresse'), person.get('postnr'),
                     person.get('poststed'))
        if any(priv_addr):
            logger.debug("Setting additional home address: %s %s %s",
                         priv_addr[0], priv_addr[1], priv_addr[2])
            # TODO: Address country? Should probably use const.human2constant()
            # country_code = new_person.get_country_code(origin_country)
            # TODO: Is there really a guaranteed connection between passport
            #       origin country and home address?
            new_person.populate_address(const.system_paga,
                                        const.address_post_private,
                                        priv_addr[0], None, priv_addr[1],
                                        priv_addr[2], None)

        op2 = new_person.write_db()

        set_person_spreads(self.db, self.const, new_person, person)

        if op is None and op2 is None:
            logger.info("EQUAL: No change to person with paga_id=%r", paga_nr)
        elif op:
            logger.info("NEW: Created new person with paga_id=%r", paga_nr)
        else:
            logger.info("UPDATE: Updated person with paga_id=%r (%s, %s)",
                        paga_nr, op, op2)
예제 #38
0
def process_person_callback(person_info):
    """Called when we have fetched all data on a person from the xml
    file.  Updates/inserts name, address and affiliation
    information."""

    global no_name
    try:
        fnr = fodselsnr.personnr_ok(
            "%06d%05d" %
            (int(person_info['fodselsdato']), int(person_info['personnr'])))
        fnr = fodselsnr.personnr_ok(fnr)
        logger.info("Process %s " % (fnr))
        (year, mon, day) = fodselsnr.fodt_dato(fnr)
        if (year < 1970
                and getattr(cereconf, "ENABLE_MKTIME_WORKAROUND", 0) == 1):
            # Seems to be a bug in time.mktime on some machines
            year = 1970
    except fodselsnr.InvalidFnrError:
        logger.warn("Ugyldig fødselsnr: %s" % fnr)
        return

    gender = co.gender_male
    if (fodselsnr.er_kvinne(fnr)):
        gender = co.gender_female

    etternavn = fornavn = None
    studentnr = None
    affiliations = []
    address_info = None
    aktiv_sted = []

    for dta_type in person_info.keys():
        x = person_info[dta_type]
        p = x[0]
        if isinstance(p, str):
            continue
        # Get name
        if dta_type in ('fagperson', 'evu', 'aktiv'):
            etternavn = p['etternavn']
            fornavn = p['fornavn']
        if p.has_key('studentnr_tildelt'):
            studentnr = '%06d' % int(p['studentnr_tildelt'])

    if etternavn is None:
        logger.debug("Ikke noe navn på %s" % fnr)
        no_name += 1
        return

    new_person = Factory.get('Person')(db)
    if fnr2person_id.has_key(fnr):
        new_person.find(fnr2person_id[fnr])

    new_person.populate(mx.DateTime.Date(year, mon, day), gender)

    new_person.affect_names(co.system_fs, co.name_first, co.name_last)
    new_person.populate_name(co.name_first, fornavn)
    new_person.populate_name(co.name_last, etternavn)

    if studentnr is not None:
        new_person.affect_external_id(co.system_fs, co.externalid_fodselsnr,
                                      co.externalid_studentnr)

        new_person.populate_external_id(co.system_fs, co.externalid_studentnr,
                                        studentnr)
        logger.debug("Studentnr is %s", studentnr)
    else:
        new_person.affect_external_id(co.system_fs, co.externalid_fodselsnr)
    new_person.populate_external_id(co.system_fs, co.externalid_fodselsnr, fnr)

    ad_post, ad_post_private, ad_street = _calc_address(person_info)
    for address_info, ad_const in ((ad_post, co.address_post),
                                   (ad_post_private, co.address_post_private),
                                   (ad_street, co.address_street)):
        # TBD: Skal vi slette evt. eksisterende adresse v/None?
        if address_info is not None:
            logger.debug("Populating address...")
            new_person.populate_address(co.system_fs, ad_const, **address_info)
    # if this is a new Person, there is no entity_id assigned to it
    # until written to the database.
    op = new_person.write_db()

    # Iterate over all person_info entries and extract relevant data
    if person_info.has_key('aktiv'):
        for row in person_info['aktiv']:
            try:
                if studieprog2sko[row['studieprogramkode']] is not None:
                    aktiv_sted.append(
                        int(studieprog2sko[row['studieprogramkode']]))
                    logger.debug("App2akrivts")
            except KeyError:
                logger.warn(
                    'App2akrivts: Person id %d har stud.prog.kode %s som ikke eksisterer.',
                    new_person.entity_id, row['studieprogramkode'])

    for dta_type in person_info.keys():
        # Get affiliations
        if dta_type in ('fagperson', ):
            _process_affiliation(
                co.affiliation_tilknyttet,
                co.affiliation_status_tilknyttet_fagperson, affiliations,
                _get_sko(p, 'faknr', 'instituttnr', 'gruppenr',
                         'institusjonsnr'))
        elif dta_type in ('aktiv', ):
            for row in x:
                # aktiv_sted is necessary in order to avoid different affiliation statuses
                # to a same 'stedkode' to be overwritten
                # e.i. if a person has both affiliations status 'evu' and
                # aktive to a single stedkode we want to register the status 'aktive'
                # in cerebrum
                try:
                    if studieprog2sko[row['studieprogramkode']] is not None:
                        aktiv_sted.append(
                            int(studieprog2sko[row['studieprogramkode']]))
                    _process_affiliation(
                        co.affiliation_student,
                        co.affiliation_status_student_aktiv, affiliations,
                        studieprog2sko[row['studieprogramkode']])
                except KeyError:
                    logger.warn(
                        'AKTIV: Person id %d har stud.prog.kode %s som ikke eksisterer.',
                        new_person.entity_id, row['studieprogramkode'])

        elif dta_type in ('evu', ):
            subtype = co.affiliation_status_student_evu
            try:
                if studieprog2sko[row['studieprogramkode']] in aktiv_sted:
                    subtype = co.affiliation_status_student_aktiv
                _process_affiliation(co.affiliation_student, subtype,
                                     affiliations,
                                     studieprog2sko[row['studieprogramkode']])
            except KeyError:
                logger.warn(
                    'EVU: Person id %d har stud.prog.kode %s som ikke eksisterer.',
                    new_person.entity_id, row['studieprogramkode'])

    for a in filter_affiliations(affiliations):
        ou, aff, aff_status = a
        new_person.populate_affiliation(co.system_fs, ou, aff, aff_status)
        if include_delete:
            key_a = "%s:%s:%s" % (new_person.entity_id, ou, int(aff))
            if old_aff.has_key(key_a):
                old_aff[key_a] = False

    register_contact(new_person, person_info)

    op2 = new_person.write_db()
    if op is None and op2 is None:
        logger.info("**** EQUAL ****")
    elif op == True:
        logger.info("**** NEW ****")
    else:
        logger.info("**** UPDATE ****")

    # Reservations
    if gen_groups:
        should_add = False
        for dta_type in person_info.keys():
            p = person_info[dta_type][0]
            if isinstance(p, str):
                continue
            # Presence of 'fagperson' elements for a person should not
            # affect that person's reservation status.
            if dta_type in ('fagperson', ):
                continue
            # We only fetch the column in these queries
            if dta_type not in ('evu'):
                continue
            # If 'status_reserv_nettpubl' == "N": add to group
            if p.get('status_reserv_nettpubl', "") == "N":
                should_add = True
            else:
                should_add = False
        if should_add:
            # The student has explicitly given us permission to be
            # published in the directory.
            _add_res(new_person.entity_id)
        else:
            # The student either hasn't registered an answer to
            # the "Can we publish info about you in the directory"
            # question at all, or has given an explicit "I don't
            # want to appear in the directory" answer.
            _rem_res(new_person.entity_id)
    db.commit()
예제 #39
0
def fetch_data(drgrad_file, fritak_kopiavg_file, sysname, person_file,
               autostud):
    """Finner alle personer som rammes av kvoteordningen ved å:

    - finne alle som har en student-affiliation (0.1)
    - ta bort ansatte (1.2.1)
    - ta bort dr-grads stipendiater (1.2.2)

    I.e. vi fjerner alle som har fullstendig fritak fra ordningen.  De
    som kun har fritak fra kopiavgiften er ikke fjernet.

    Finner alle gruppe-medlemskap, enten via person eller account for
    de som rammes av ordningen.

    Finner fritak fra kopiavgift (1.2.3 og 1.2.4)
    """

    logger.debug("Prefetching data")

    betaling_fritak = get_bet_fritak_utv_data(sysname, person_file)
    logger.debug("Fritak for: %r", betaling_fritak)
    # Finn alle som skal rammes av kvoteregimet
    quota_victim = {}

    for pid in get_students():
        quota_victim[pid] = True

    # Ta bort de som ikke har konto
    account = Account.Account(db)
    account_id2pid = {}
    has_account = {}
    for row in account.list_accounts_by_type(fetchall=False):
        account_id2pid[int(row['account_id'])] = int(row['person_id'])
        has_account[int(row['person_id'])] = True
    for p_id in quota_victim.keys():
        if p_id not in has_account:
            del (quota_victim[p_id])
    logger.debug("after removing non-account people: %i" % len(quota_victim))

    # Ansatte har fritak
    for row in pe.list_affiliations(affiliation=co.affiliation_ansatt,
                                    status=(
                                        co.affiliation_status_ansatt_bil,
                                        co.affiliation_status_ansatt_vit,
                                        co.affiliation_status_ansatt_tekadm,
                                    ),
                                    source_system=co.system_sap,
                                    include_deleted=False,
                                    fetchall=False):
        if int(row['person_id']) in quota_victim:
            del (quota_victim[int(row['person_id'])])
    logger.debug("removed employees: %i" % len(quota_victim))

    # Alle personer som har disse typer tilknyttet affiliation skal ha fritak
    for row in pe.list_affiliations(
            affiliation=co.affiliation_tilknyttet,
            status=(
                co.affiliation_tilknyttet_bilag,
                co.affiliation_tilknyttet_ekst_forsker,
                co.affiliation_tilknyttet_ekst_partner,
                co.affiliation_tilknyttet_ekst_stip,
                co.affiliation_tilknyttet_emeritus,
                co.affiliation_tilknyttet_gjesteforsker,
                co.affiliation_tilknyttet_innkjoper,
            ),
            source_system=co.system_sap,
            include_deleted=False,
            fetchall=False):
        if int(row['person_id']) in quota_victim:
            del (quota_victim[int(row['person_id'])])
    logger.debug("removed tilknyttet people: %i" % len(quota_victim))

    # Mappe fødselsnummer til person-id
    fnr2pid = {}
    for p in pe.search_external_ids(source_system=co.system_fs,
                                    id_type=co.externalid_fodselsnr,
                                    fetchall=False):
        fnr2pid[p['external_id']] = int(p['entity_id'])

    # Dr.grads studenter har fritak
    for row in GeneralDataParser(drgrad_file, "drgrad"):
        fnr = fodselsnr.personnr_ok(
            "%06d%05d" % (int(row['fodselsdato']), int(row['personnr'])))
        pid = fnr2pid.get(fnr, None)
        if not pid:
            continue
        if pid in quota_victim:
            del (quota_victim[pid])
    logger.debug("removed drgrad: %i" % len(quota_victim))

    # map person-id til gruppemedlemskap.  Hvis gruppe-medlemet er av
    # typen konto henter vi ut owner_id.
    person_id_member = {}
    group = Group.Group(db)
    count = [0, 0]
    groups = autostud.pc.group_defs.keys()
    logger.debug("Finding members in %s" % groups)
    for g in groups:
        group.clear()
        group.find(g)
        for m in set([
                int(row["member_id"])
                for row in group.search_members(group_id=group.entity_id,
                                                indirect_members=True,
                                                member_type=co.entity_account)
        ]):
            if m in account_id2pid:
                person_id_member.setdefault(account_id2pid[m], []).append(g)
                count[0] += 1
            else:
                person_id_member.setdefault(m, []).append(g)
                count[1] += 1
    logger.debug("memberships: persons:%i, p_m=%i, a_m=%i",
                 len(person_id_member), count[1], count[0])

    # Fetch any affiliations used as select criteria
    person_id_affs = {}
    for sm in autostud.pc.select_tool.select_map_defs.values():
        if not isinstance(sm, AutoStud.Select.SelectMapPersonAffiliation):
            continue
        for aff_attrs in sm._select_map.keys():
            affiliation = aff_attrs[0]
            for row in pe.list_affiliations(affiliation=affiliation,
                                            include_deleted=False,
                                            fetchall=False):
                person_id_affs.setdefault(int(row['person_id']), []).append(
                    (int(row['ou_id']), int(row['affiliation']),
                     int(row['status'])))

    # fritak fra selve kopiavgiften (1.2.3 og 1.2.4)
    kopiavgift_fritak = {}
    for row in GeneralDataParser(fritak_kopiavg_file, "betfritak"):
        fnr = fodselsnr.personnr_ok(
            "%06d%05d" % (int(row['fodselsdato']), int(row['personnr'])))
        pid = fnr2pid.get(fnr, None)
        if not pid:
            continue
        kopiavgift_fritak[pid] = True
    logger.debug("%i personer har kopiavgiftsfritak" % len(kopiavgift_fritak))

    logger.debug("Prefetch returned %i students" % len(quota_victim))
    n = 0
    for pid in betaling_fritak.keys():
        if pid in quota_victim:
            n += 1
    logger.debug("%i av disse har betaling_fritak" % n)
    return (fnr2pid, quota_victim, person_id_member, person_id_affs,
            kopiavgift_fritak, betaling_fritak)
예제 #40
0
def process_person_callback(person_info):
    """Called when we have fetched all data on a person from the xml
    file.  Updates/inserts name, address and affiliation
    information."""

    global no_name
    try:
        fnr = fodselsnr.personnr_ok(
            "%06d%05d" %
            (int(person_info['fodselsdato']), int(person_info['personnr'])))
        fnr = fodselsnr.personnr_ok(fnr)
        logger.info("Process %s " % (fnr))
        (year, mon, day) = fodselsnr.fodt_dato(fnr)
        if (year < 1970
                and getattr(cereconf, "ENABLE_MKTIME_WORKAROUND", 0) == 1):
            # Seems to be a bug in time.mktime on some machines
            year = 1970
    except fodselsnr.InvalidFnrError:
        logger.warn("Ugyldig fødselsnr: %s" % fnr)
        return

    gender = co.gender_male
    if (fodselsnr.er_kvinne(fnr)):
        gender = co.gender_female

    etternavn = fornavn = None
    studentnr = None
    affiliations = []
    address_info = None
    aktiv_sted = []

    # Iterate over all person_info entries and extract relevant data
    if person_info.has_key('aktiv'):
        for row in person_info['aktiv']:
            if studieprog2sko[row['studieprogramkode']] is not None:
                aktiv_sted.append(int(
                    studieprog2sko[row['studieprogramkode']]))
                logger.debug("App2akrivts")

    for dta_type in person_info.keys():
        x = person_info[dta_type]
        p = x[0]
        if isinstance(p, str):
            continue
        # Get name
        if dta_type in ('fagperson', 'evu', 'aktiv'):
            etternavn = p['etternavn']
            fornavn = p['fornavn']
        if p.has_key('studentnr_tildelt'):
            studentnr = p['studentnr_tildelt']

        # Get affiliations
        if dta_type in ('fagperson', ):
            _process_affiliation(
                co.affiliation_tilknyttet,
                co.affiliation_status_tilknyttet_fagperson, affiliations,
                _get_sko(p, 'faknr', 'instituttnr', 'gruppenr',
                         'institusjonsnr'))
        elif dta_type in ('aktiv', ):
            for row in x:
                # aktiv_sted is necessary in order to avoid different affiliation statuses
                # to a same 'stedkode' to be overwritten
                # e.i. if a person has both affiliations status 'evu' and
                # aktive to a single stedkode we want to register the status 'aktive'
                # in cerebrum
                if studieprog2sko[row['studieprogramkode']] is not None:
                    aktiv_sted.append(
                        int(studieprog2sko[row['studieprogramkode']]))
                    _process_affiliation(
                        co.affiliation_student,
                        co.affiliation_status_student_aktiv, affiliations,
                        studieprog2sko[row['studieprogramkode']])
        elif dta_type in ('evu', ):
            subtype = co.affiliation_status_student_evu
            if studieprog2sko[row['studieprogramkode']] in aktiv_sted:
                subtype = co.affiliation_status_student_aktiv
            _process_affiliation(co.affiliation_student, subtype, affiliations,
                                 studieprog2sko[row['studieprogramkode']])

    if etternavn is None:
        logger.debug("Ikke noe navn på %s" % fnr)
        no_name += 1
        return

    # TODO: If the person already exist and has conflicting data from
    # another source-system, some mechanism is needed to determine the
    # superior setting.

    new_person = Factory.get('Person')(db)
    if fnr2person_id.has_key(fnr):
        new_person.find(fnr2person_id[fnr])

    new_person.populate(mx.DateTime.Date(year, mon, day), gender)

    new_person.affect_names(co.system_fs, co.name_first, co.name_last)
    new_person.populate_name(co.name_first, fornavn)
    new_person.populate_name(co.name_last, etternavn)

    if studentnr is not None:
        new_person.affect_external_id(co.system_fs, co.externalid_fodselsnr,
                                      co.externalid_studentnr)
        new_person.populate_external_id(co.system_fs, co.externalid_studentnr,
                                        studentnr)
    else:
        new_person.affect_external_id(co.system_fs, co.externalid_fodselsnr)
    new_person.populate_external_id(co.system_fs, co.externalid_fodselsnr, fnr)

    ad_post, ad_post_private, ad_street = _calc_address(person_info)
    for address_info, ad_const in ((ad_post, co.address_post),
                                   (ad_post_private, co.address_post_private),
                                   (ad_street, co.address_street)):
        # TBD: Skal vi slette evt. eksisterende adresse v/None?
        if address_info is not None:
            logger.debug("Populating address...")
            new_person.populate_address(co.system_fs, ad_const, **address_info)
    # if this is a new Person, there is no entity_id assigned to it
    # until written to the database.
    try:
        op = new_person.write_db()
    except Exception, e:
        logger.exception("write_db failed for person %s: %s", fnr, e)
        # Roll back in case of db exceptions:
        db.rollback()
        return
예제 #41
0
def import_person(persons, all_nodes):
    print("import person")
    global dryrun
    global cere_list
    global include_del
    logger.info(
        "database:%s" % cereconf.CEREBRUM_DATABASE_CONNECT_DATA['host'])
    for person in persons:

        ssn_not_valid = False
        person_to_be_processed = {}

        person = person.rstrip()
        person_list = person.split(";")

        logger.debug("--- Processing new person ---")

        try:
            # print "fnr:'%s'" % person_list[1]
            person_to_be_processed['noreduorgnin'] = person_list[3]
            person_to_be_processed['birth_day'] = person_list[0][0:2]
            person_to_be_processed['birth_month'] = person_list[0][2:4]
            person_to_be_processed['birth_year'] = person_list[0][4:6]

            # Make sure year contains 4 digits.
            if (person_to_be_processed['birth_year'] < 20):
                birth_year = "20%s" % (person_to_be_processed['birth_year'])
            else:
                birth_year = "19%s" % (person_to_be_processed['birth_year'])
            person_to_be_processed['birth_year'] = birth_year
            # print "birth_day:'%s'" % person_to_be_processed['birth_day']
            # print "birth_month:'%s'" % person_to_be_processed['birth_month']
            # print "birth_year:'%s'" % person_to_be_processed['birth_year']
        except ValueError:
            logger.warning(
                "Empty Birthdate for person named:%s %s. Continue with " +
                "next person", person_list[1], person_list[2])
            continue
        except IndexError:
            logger.warning("empty person file?")
            continue
        # Check if SSN is valid
        # print "current fnr:%s" % person_list[0]
        try:
            fodselsnr.personnr_ok(person_list[0])
            person_to_be_processed['ssn'] = person_list[0]
        except fodselsnr.InvalidFnrError:
            logger.warning(
                "Empty or non-valid ssn %s for person:%s %s from :%s. " +
                "Continue with next person",
                person_list[0],
                person_list[1],
                person_list[2],
                person_list[4])
            # ssn_not_valid = True
            # person_to_be_processed['ssn'] = ''
            continue

        # set person gender
        # gender = const.gender_male
        gender = const.gender_male
        if not ssn_not_valid:
            if fodselsnr.er_kvinne(person_to_be_processed['ssn']):
                gender = const.gender_female
        else:
            # Impossible to set gender. Return error message and set gender to
            # unknown
            logger.warning(
                "Impossible to set gender for person:%s %s. Using Unknown",
                person_list[1], person_list[2])
            gender = const.gender_unknown

        # set gender
        person_to_be_processed['gender'] = gender
        # get person firstname
        person_to_be_processed['firstname'] = person_list[1]
        # print "firstname:%s" % person_to_be_processed['firstname']
        # get person lastname
        person_to_be_processed['lastname'] = person_list[2]
        # print "lastname:%s" % person_to_be_processed['lastname']

        if (person_to_be_processed['firstname'].isspace() or
                person_to_be_processed['lastname'].isspace()):
            # Firstname and/or lastname is made of whitespace ONLY.
            # generate error message and continue with NEXT person
            logger.warn(
                "missing first and/or lastname for person:%s. Person NOT " +
                "imported", person)
            continue

        # set correct encoding
        person_to_be_processed['firstname'] = decode_text(
            person_to_be_processed['firstname'])
        person_to_be_processed['lastname'] = decode_text(
            person_to_be_processed['lastname'])

        #
        # Finished building person_to_be_processed dict.
        # pp.pprint(person_to_be_processed)
        #
        # create person object
        db_person.clear()
        try:
            db_person.find_by_external_id(const.externalid_fodselsnr,
                                          person_to_be_processed['ssn'])
            logger.info("Ssn already in database. update person object")
            # existing_person = True
        except Errors.NotFoundError:
            logger.warning("Unknown ssn:%s, create new person object" %
                           person_to_be_processed['ssn'])
            # Unable to find person with ssn in the database.
            pass

        #
        # Populate person object
        #
        try:
            db_person.populate(
                mx.DateTime.Date(int(person_to_be_processed['birth_year']),
                                 int(person_to_be_processed['birth_month']),
                                 int(person_to_be_processed['birth_day'])),
                int(person_to_be_processed['gender']))
        except Errors.CerebrumError as m:
            # unable to populate person object. Return error message and
            # continue with next person
            logger.error("Person:%s population failed",
                         person_to_be_processed['ssn'],
                         m)
            continue

        # affect name and external id
        db_person.affect_names(const.system_flyt, const.name_first,
                               const.name_last)
        # populate firstname, lastname and external id
        db_person.populate_name(const.name_first,
                                person_to_be_processed['firstname'])
        db_person.populate_name(const.name_last,
                                person_to_be_processed['lastname'])

        db_person.affect_external_id(const.system_flyt,
                                     const.externalid_fodselsnr)
        db_person.populate_external_id(const.system_flyt,
                                       const.externalid_fodselsnr,
                                       person_to_be_processed['ssn'])

        # In case this is a new person, we will need to write to DB before
        # we can continue.
        try:
            op = db_person.write_db()
        except db.IntegrityError as e:
            db_person.clear()
            db.rollback()
            logger.info("Error:%s - person not imported to BAS", e)
            continue

        # op = db_person.write_db()

        # Determine person affiliation and affiliation_status
        det_ou, det_affiliation = determine_affiliation(person_list, all_nodes)

        # logger.debug(" --- from determine affiliation, the following is
        # calculated ---")
        # pp.pprint(det_affiliation)

        for single_ou in det_ou:
            for single_aff in det_affiliation:
                new_aff = getattr(const, single_aff)
                new_aff_stat = getattr(const, det_affiliation[single_aff])
                ou.clear()
                ou.find_stedkode(single_ou[0:2], single_ou[2:4],
                                 single_ou[4:6],
                                 cereconf.DEFAULT_INSTITUSJONSNR)
                logger.debug(
                    "setting:: ou_id:%s, aff:%s, aff_stat:%s for person:%s",
                    int(ou.entity_id), int(new_aff), int(new_aff_stat),
                    db_person.entity_id)

                db_person.populate_affiliation(const.system_flyt,
                                               ou.entity_id,
                                               new_aff,
                                               new_aff_stat)
                k = "%s:%s:%s" % (db_person.entity_id, int(ou.entity_id),
                                  int(new_aff))
                if include_del:
                    if k in cere_list:
                        cere_list[k] = False

        # store mobile for those that has it
        # contact = determine_contact(db_person)
        if len(person_list[13]) > 1:
            person_list[13]
            logger.debug("has mobile:%s" % person_list[13])
            number = person_list[13]
            c_prefs = {}
            c_type = int(const.contact_mobile_phone)
            pref = c_prefs.get(c_type, 0)
            db_person.populate_contact_info(const.system_flyt, c_type, number,
                                            pref)
            pref = c_prefs[c_type] = pref = 1
        else:
            logger.debug("No mobile registered for this user")

        op2 = db_person.write_db()

        if op is None and op2 is None:
            logger.info("**** EQUAL ****")
        elif op:
            logger.info("**** NEW ****")
        else:
            logger.info("**** UPDATE  (%s:%s) ****" % (op, op2))
예제 #42
0
def populate_external_ids(tpl):
    """
    Locate (or create) a person holding the IDs contained in FIELDS and
    register these external IDs if necessary.

    This function both alters the PERSON object and retuns a boolean value
    (True means the ID update/lookup was successful, False means the
    update/lookup failed and nothing can be ascertained about the PERSON's
    state).

    There are two external IDs in SAP -- the Norwegian social security number
    (11-siffret personnummer, fnr) and the SAP employee id (sap_id). SAP IDs
    are (allegedly) permanent and unique, fnr can change.
    """

    error, person = locate_person(tpl.sap_ansattnr, tpl.sap_fnr)
    if error:
        logger.error("Lookup for (sap_id; fnr) == (%s; %s) failed",
                     tpl.sap_ansattnr, tpl.sap_fnr)
        return None

    if person is not None:
        logger.debug("A person owning IDs (%s, %s) already exists",
                     tpl.sap_ansattnr, tpl.sap_fnr)
        # Now, we *must* check that the IDs registered in Cerebrum match
        # those in SAP dump. I.e. we select the external IDs from Cerebrum
        # and compare them to SAP_ID and FNR. They must either match
        # exactly or be absent.
        if not match_external_ids(person,
                                  tpl.sap_ansattnr, tpl.sap_fnr):
            return None
    else:
        person = Factory.get("Person")(database)
        logger.debug("New person for IDs (%s, %s)",
                     tpl.sap_ansattnr, tpl.sap_fnr)

    try:
        fodselsnr.personnr_ok(tpl.sap_fnr)
    except fodselsnr.InvalidFnrError:
        # IVR 2007-02-15 It is *wrong* to simply ignore these, but since they
        # do occur, and they may be difficult to get rid of, we'll downgrade
        # the severity to avoid being spammed to death.
        logger.info("No valid checksum for FNR (%s)!", tpl.sap_fnr)
        return None

    gender = const.gender_male
    if fodselsnr.er_kvinne(tpl.sap_fnr):
        gender = const.gender_female

    # This would allow us to update birthdays and gender information for
    # both new and existing people.
    person.populate(tpl.sap_birth_date, gender)
    person.affect_external_id(const.system_sap,
                              const.externalid_fodselsnr,
                              const.externalid_sap_ansattnr)
    person.populate_external_id(const.system_sap,
                                const.externalid_sap_ansattnr,
                                tpl.sap_ansattnr)
    person.populate_external_id(const.system_sap,
                                const.externalid_fodselsnr,
                                tpl.sap_fnr)
    person.write_db()
    return person
예제 #43
0
def create_sysx_person(db, sxp, update_affs, stats):
    """
    Create or update person with sysx data.

    :param db:
    :param sxp: Person dict from ``SYSX``
    :param update_affs: A set of current SYSX aff keys
    :param stats: A defaultdict with counts
    """

    co = Factory.get('Constants')(db)
    add_sysx_id = fnr_found = False

    sysx_id = sxp['id']
    fnr = sxp['fnr']
    fornavn = sxp['fornavn']
    etternavn = sxp['etternavn']
    fullt_navn = "%s %s" % (fornavn, etternavn)

    if check_expired_sourcedata(sxp['expire_date']):
        logger.info("Skipping sysx_id=%r (%s), expire_date=%r", sysx_id,
                    fullt_navn, sxp['expire_date'])
        stats['skipped'] += 1
        return

    # check if approved. Ditch if not.
    if not sxp['approved']:
        logger.error("Skipping sysx_id=%r (%s), not approved", sysx_id,
                     fullt_navn)
        stats['skipped'] += 1
        return

    my_stedkode = Factory.get('OU')(db)
    pers_sysx = Factory.get('Person')(db)
    pers_fnr = Factory.get('Person')(db)

    try:
        pers_sysx.find_by_external_id(co.externalid_sys_x_id, sysx_id)
    except Errors.NotFoundError:
        add_sysx_id = True

    if fnr:
        try:
            fnr = fodselsnr.personnr_ok(fnr)
        except fodselsnr.InvalidFnrError:
            logger.error("Skipping sysx_id=%r (%s), invalid fnr", sysx_id,
                         fullt_navn)
            stats['skipped'] += 1
            return
        birth_date = datetime.date(*fodselsnr.fodt_dato(fnr))

        if fodselsnr.er_kvinne(fnr):
            gender = co.gender_female
        else:
            gender = co.gender_male

        try:
            pers_fnr.find_by_external_id(co.externalid_fodselsnr, fnr)
        except Errors.NotFoundError:
            pass
        except Errors.TooManyRowsError as e:
            # This persons fnr has multiple rows in entity_external_id table
            # This is an error, person should not have not more than one entry.
            # Don't know which person object to use, return error message.
            logger.error(
                "Skipping sysx_id=%r (%s), matched multiple persons (%s)",
                sysx_id, fullt_navn, e)
            stats['skipped'] += 1
            return
        else:
            if not add_sysx_id and (pers_fnr.entity_id != pers_sysx.entity_id):
                logger.error(
                    "Skipping sysx_id=%r (%s), matched multiple persons (sysx "
                    "id matched person_id=%r, sysx fnr matched person_id=%r)",
                    sysx_id, fullt_navn, pers_sysx.entity_id,
                    pers_fnr.entity_id)
                stats['skipped'] += 1
                return
            fnr_found = True

    else:
        # foreigner without norwegian ssn,
        birth_date = sxp['birth_date']
        if not birth_date:
            logger.error("sysx_id=%r (%s) is missing birth date", sysx_id,
                         fullt_navn)

        if sxp['gender'] == 'M':
            gender = co.gender_male
        elif sxp['gender'] == 'F':
            gender = co.gender_female
        else:
            logger.error("Skipping sysx_id=%r (%s), invalid gender %r",
                         sysx_id, fullt_navn, sxp['gender'])
            stats['skipped'] += 1
            return

    logger.info("Processing sysx_id=%r (%s)", sysx_id, fullt_navn)
    if fnr_found:
        person = pers_fnr
    else:
        person = pers_sysx

    # person object located, populate...
    try:
        person.populate(mx.DateTime.DateFrom(birth_date), gender)
    except Errors.CerebrumError:
        logger.error("Skipping sysx_id=%r (%s), populate() failed",
                     sysx_id,
                     fullt_navn,
                     exc_info=True)
        stats['skipped'] += 1
        return

    person.affect_names(co.system_x, co.name_first, co.name_last, co.name_full)
    person.populate_name(co.name_first, fornavn)
    person.populate_name(co.name_last, etternavn)
    person.populate_name(co.name_full, fullt_navn)

    # add external ids
    if fnr:
        person.affect_external_id(co.system_x, co.externalid_fodselsnr,
                                  co.externalid_sys_x_id)
        person.populate_external_id(co.system_x, co.externalid_fodselsnr, fnr)
        logger.debug("Set NO_BIRTHNO for sysx_id=%r (%s)", sysx_id, fullt_navn)
    else:
        person.affect_external_id(co.system_x, co.externalid_sys_x_id)

    person.populate_external_id(co.system_x, co.externalid_sys_x_id, sysx_id)
    logger.debug("Set sysx_id for sysx_id=%r (%s)", sysx_id, fullt_navn)

    # setting affiliation and affiliation_status
    affiliation = co.PersonAffiliation(sxp['affiliation'])
    affiliation_status = co.PersonAffStatus(affiliation,
                                            sxp['affiliation_status'])
    # Assert that the affs are real
    int(affiliation), int(affiliation_status)

    # get ou_id of stedkode used
    fak = sxp['ou'][0:2]
    ins = sxp['ou'][2:4]
    avd = sxp['ou'][4:6]

    # KEB
    if fak == '0':
        logger.warning("sysx_id=%r (%s) has invalid sko=%r", sysx_id,
                       fullt_navn, sxp['ou'])
        ins = avd = fak
    try:
        my_stedkode.find_stedkode(fak, ins, avd,
                                  cereconf.DEFAULT_INSTITUSJONSNR)
    except EntityExpiredError:
        logger.error("Skipping sysx_id=%r (%s), expired sko=%r", sysx_id,
                     fullt_navn, sxp['ou'])
        stats['skipped'] += 1
        return

    ou_id = int(my_stedkode.entity_id)

    # if this is a new Person, there is no entity_id assigned to it
    # until written to the database.
    op = person.write_db()

    # populate the person affiliation table
    person.populate_affiliation(co.system_x, ou_id, int(affiliation),
                                int(affiliation_status))

    # make sure we don't delete this aff when processing deleted affs
    aff_key = affiliation_key(person.entity_id, ou_id, affiliation)
    update_affs.discard(aff_key)

    op2 = person.write_db()

    logger.debug("OP codes: op=%s,op2=%s" % (op, op2))

    if op is None and op2 is None:
        logger.info("**** EQUAL ****")
        stats['unchanged'] += 1
    elif op is None and op2 is False:
        logger.info("**** AFF UPDATE ****")
        stats['updated'] += 1
    elif op is True:
        logger.info("**** NEW ****")
        stats['added'] += 1
    elif op is False:
        logger.info("**** UPDATE ****")
        stats['updated'] += 1
    return
예제 #44
0
    def approve_persons(self, input):
        """Let project owner and PAs approve more persons to their project.

        Sending this through Nettskjema is needed as we have no web GUI inside
        TSD in the beginning, and project admins need a way to approve people
        for their projects.

        """
        # Find the project:
        pid = input['p_id']
        ou.clear()
        ou.find_by_tsd_projectid(pid)
        logger.info('Approve persons for project: %s', pid)

        # Find the requestor
        pe = self._get_person()

        # The requestor must be owner or PA:
        if not list(pe.list_affiliations(person_id=pe.entity_id,
                        affiliation=co.affiliation_project,
                        status=(co.affiliation_status_project_owner,
                                co.affiliation_status_project_admin),
                        ou_id=ou.entity_id)):
            # TODO: Delete the JSON file?
            raise BadInputError("Person %s is not PA of project %s" %
                                       (pe.entity_id, pid))

        # Update contact info for PA:
        self._update_person(pe, input)

        # Try to find and add the given person to the project
        pe2 = Factory.get('Person')(db)
        pre_approvals = set()
        for fnr in set(input['p_persons'].split()):
            for fnr1 in set(fnr.split(',')):
                try:
                    fnr1 = fodselsnr.personnr_ok(fnr1)
                except fodselsnr.InvalidFnrError:
                    logger.debug("Ignoring invalid fnr: %s", fnr1)
                    continue
                # Find the person:
                try:
                    pe2 = self._get_person(fnr1, create_nonexisting=False)
                except Errors.NotFoundError:
                    logger.info("Person %s not found, added to pre approve list",
                                 fnr1)
                    pre_approvals.add(fnr1)
                    continue
                # Affiliate person to project:
                if not pe2.list_affiliations(person_id=pe2.entity_id,
                                             ou_id=ou.entity_id,
                                             affiliation=co.affiliation_project):
                    logger.info("Approve person %s for project: %s", fnr1, pid)
                    pe2.populate_affiliation(source_system=co.system_nettskjema,
                                         ou_id=ou.entity_id,
                                         affiliation=co.affiliation_project,
                                         status=co.affiliation_status_project_member)
                # Remove pending aff, if set. Note that this also removes any other
                # pending statuses, if we should create more of those in the future.
                pe2.delete_affiliation(ou_id=ou.entity_id,
                                       affiliation=co.affiliation_pending,
                                       source=co.system_nettskjema)
                pe2.write_db()

                # Find the member's project account, if it exist:
                accounts = self._get_project_account(pe2, ou)
                if not accounts:
                    logger.info("No project account for %s, added to approve list",
                                 fnr1)
                    pre_approvals.add(fnr1)
                    continue
                ac.clear()
                ac.find(accounts[0]['account_id'])
                logger.info("Approving person %s PM to %s: %s ", fnr1,
                            ac.account_name, pid)

                # Update the account types:
                ac.set_account_type(ou.entity_id, co.affiliation_project)
                # TODO: check if this works if the type is already set:
                ac.del_account_type(ou.entity_id, co.affiliation_pending)
                ac.del_account_type(ou.entity_id, co.affiliation_pending)
                # Remove quarantine:
                ac.delete_entity_quarantine(co.quarantine_not_approved)
                ac.write_db()

                # Promote posix:
                if ou.is_approved():
                    pu = Factory.get('PosixUser')(db)
                    try:
                        pu.find(ac.entity_id)
                    except Errors.NotFoundError:
                        uid = pu.get_free_uid()
                        pu.populate(uid, None, None, co.posix_shell_bash, parent=ac,
                                    creator_id=systemaccount_id)
                        logger.debug("Promote POSIX, UID: %s", ac.entity_id, uid)
                        pu.write_db()

                # Stop if person already is PA or owner, can't have member-aff at
                # the same time:
                #if pe2.list_affiliations(person_id=pe2.entity_id,
                #                         affiliation=co.affiliation_project,
                #                         status=(co.affiliation_status_project_owner,
                #                                 co.affiliation_status_project_admin),
                #                         ou_id=ou.entity_id):
                #    logger.debug("Person is already owner or PA: %s", fnr1)
                #    continue
                #pe2.populate_affiliation(source_system=co.system_nettskjema,
                #                         ou_id=ou.entity_id,
                #                         affiliation=co.affiliation_project,
                #                         status=co.affiliation_status_project_member)
                #pe2.write_db()
                # TODO: The person will get an account when successfully registered
                # with its name. This could have happened already, so might create
                # it now already?

        # Store those not processed for later approval:
        if pre_approvals:
            logger.debug("Remaining non-existing persons: %d",
                         len(pre_approvals))
            ou.add_pre_approved_persons(pre_approvals)
            ou.write_db()
        return True
예제 #45
0
def import_person(db, person, update_affs=None):
    """
    Import a single person.

    :param person:
        A dict from py:func:`parse_person`
    :param update_affs:
        A set of affiliations from py:func:`load_sito_affiliations`.
        Affiliations imported by this function will be removed from the set.
    """
    new_person = Factory.get('Person')(db)
    const = Factory.get('Constants')(db)

    if update_affs is None:
        update_affs = set()

    employee_id = person['employee_id']
    logger.info("Processing employee_id=%r", employee_id)

    #
    # Validate person data
    #

    # Birthdate
    try:
        birthdate = parse_date(person['birthdate'])
        valid_birthdate = True
    except ValueError:
        logger.warning('Invalid birth date for employee_id=%r (%r)',
                       person['employee_id'], person['birthdate'])
        valid_birthdate = False
        birthdate = None

    # SSN
    try:
        fodselsnr.personnr_ok(person['ssn'])
        valid_ssn = True
    except fodselsnr.InvalidFnrError:
        logger.warning("Empty SSN for employee_id=%r",
                       person['employee_id'])
        valid_ssn = False

    # set person gender (checking both SSN and gender from sito input file)
    if valid_ssn:
        if fodselsnr.er_kvinne(person['ssn']):
            gender = const.gender_female
        else:
            gender = const.gender_male
    elif person['gender'] == 'Female':
        gender = const.gender_female
    elif person['gender'] == 'Male':
        gender = const.gender_male
    else:
        logger.warning('Unknown gender value for employee_id=%r (%r)',
                       employee_id, person['gender'])
        gender = const.gender_unknown

    # Validate Birthdate against SSN
    if valid_ssn and valid_birthdate:
        ssndate = datetime.date(*fodselsnr.fodt_dato(person['ssn']))
        if birthdate != ssndate:
            raise SkipPerson('inconsistent birth date and ssn (%r, %r)',
                             birthdate, ssndate)
    elif valid_ssn and not valid_birthdate:
        logger.warning('Missing birth date for employee_id=%r, using date'
                       ' from ssn', person['employee_id'])
        birthdate = datetime.date(*fodselsnr.fodt_dato(person['ssn']))
    elif not valid_ssn and valid_birthdate:
        # person have birthdate but NOT ssn. Nothing to do here
        pass
    elif not valid_ssn and not valid_birthdate:
        # person does not have birthdate nor ssn. This person cannot be
        # built.  SSN or Birthdate required. Return error message and
        # continue with NEXT person
        raise SkipPerson('missing ssn and birth date')

    # Check names
    if not person['first_name']:
        raise SkipPerson('Missing first name')
    if not person['last_name']:
        raise SkipPerson('Missing last name')

    if person['middle_name']:
        fname = person['first_name'] + ' ' + person['middle_name']
    else:
        fname = person['first_name']
    lname = person['last_name']

    #
    # Get person object som DB if it exists
    #

    found = False
    new_person.clear()
    try:
        new_person.find_by_external_id(const.externalid_sito_ansattnr,
                                       employee_id)
        found = True
    except Errors.NotFoundError:
        # could not find person in DB based on ansattnr.
        if valid_ssn:
            # try to find person using ssn if ssn is valid
            try:
                new_person.clear()
                new_person.find_by_external_id(const.externalid_fodselsnr,
                                               person['ssn'])
                found = True
            except Errors.NotFoundError:
                pass
    if found:
        logger.info('Updating person object for employee_id=%r', employee_id)
    else:
        logger.info('Creating person object for employee_id=%r', employee_id)

    #
    # Populate the person object
    #

    new_person.populate(mx.DateTime.DateFrom(birthdate), gender)
    new_person.affect_names(const.system_sito, const.name_first,
                            const.name_last, const.name_work_title)
    new_person.affect_external_id(const.system_sito,
                                  const.externalid_fodselsnr,
                                  const.externalid_sito_ansattnr)
    new_person.populate_name(const.name_first, fname)
    new_person.populate_name(const.name_last, lname)

    if person['title']:
        new_person.populate_name(const.name_work_title,
                                 person['title'])
    if valid_ssn:
        new_person.populate_external_id(const.system_sito,
                                        const.externalid_fodselsnr,
                                        person['ssn'])
    new_person.populate_external_id(const.system_sito,
                                    const.externalid_sito_ansattnr,
                                    employee_id)

    # intermediary write to get an entity_id if this is a new person.
    new_person.write_db()

    new_person.populate_affiliation(const.system_sito)
    new_person.populate_contact_info(const.system_sito)

    # set person affiliation
    for key, ou_id, aff, status in determine_affiliations(
            db, new_person.entity_id, person):
        logger.info("affiliation for employee_id=%r to ou_id=%r",
                    employee_id, ou_id)
        new_person.populate_affiliation(const.system_sito, ou_id,
                                        int(aff), int(status))

        # set this persons affiliation entry to False
        # this ensures that this persons affiliations will not be removed
        # when the clean_affiliation function is called after import person
        update_affs.discard(key)

    # get person work, cellular and home phone numbers
    c_prefs = {}
    for con, number in person['phone'].items():
        c_type = int(const.human2constant(con, const.ContactInfo))
        if c_type in c_prefs:
            pref = c_prefs[c_type]
        else:
            pref = 0
            c_prefs[c_type] = 1
        new_person.populate_contact_info(const.system_sito,
                                         c_type, number, pref)
        logger.debug("contact for employee_id=%r, system=%s, "
                     "c_type=%s, number=%s, pref=%s",
                     employee_id, const.system_sito, c_type, number, pref)
    new_person.write_db()
    return not found
예제 #46
0
def process_person_callback(person_info):
    """Called when we have fetched all data on a person from the xml
    file.  Updates/inserts name, address and affiliation
    information."""
    
    global no_name
    try:
        fnr = fodselsnr.personnr_ok("%06d%05d" % (int(person_info['fodselsdato']),
                                                  int(person_info['personnr'])))
        fnr = fodselsnr.personnr_ok(fnr)
        logger.info("Process %s " % (fnr))
        (year, mon, day) = fodselsnr.fodt_dato(fnr)
        if (year < 1970
            and getattr(cereconf, "ENABLE_MKTIME_WORKAROUND", 0) == 1):
            # Seems to be a bug in time.mktime on some machines
            year = 1970
    except fodselsnr.InvalidFnrError:
        logger.warn("Ugyldig fødselsnr: %s" % fnr)
        return

    gender = co.gender_male
    if(fodselsnr.er_kvinne(fnr)):
        gender = co.gender_female

    etternavn = fornavn = None
    studentnr = None
    affiliations = []
    address_info = None
    aktiv_sted = []

    # Iterate over all person_info entries and extract relevant data    
    if person_info.has_key('aktiv'):
        for row in person_info['aktiv']:
            if studieprog2sko[row['studieprogramkode']] is not None:
                aktiv_sted.append(int(studieprog2sko[row['studieprogramkode']]))
                logger.debug("App2akrivts")

    for dta_type in person_info.keys():
        x = person_info[dta_type]
        p = x[0]
        if isinstance(p, str):
            continue
        # Get name
        if dta_type in ('fagperson', 'evu', 'aktiv'):
            etternavn = p['etternavn']
            fornavn = p['fornavn']
        if p.has_key('studentnr_tildelt'):
            studentnr = p['studentnr_tildelt']
    
        # Get affiliations
        if dta_type in ('fagperson',):
            _process_affiliation(co.affiliation_tilknyttet,
                                 co.affiliation_status_tilknyttet_fagperson,
                                 affiliations, _get_sko(p, 'faknr',
                                 'instituttnr', 'gruppenr', 'institusjonsnr'))
        elif dta_type in ('aktiv', ):
 	  for row in x:
	      # aktiv_sted is necessary in order to avoid different affiliation statuses
	      # to a same 'stedkode' to be overwritten 
              # e.i. if a person has both affiliations status 'evu' and
	      # aktive to a single stedkode we want to register the status 'aktive'
	      # in cerebrum
              if studieprog2sko[row['studieprogramkode']] is not None:
                  aktiv_sted.append(int(studieprog2sko[row['studieprogramkode']]))
		  _process_affiliation(co.affiliation_student,
				       co.affiliation_status_student_aktiv, affiliations,
				       studieprog2sko[row['studieprogramkode']])
	elif dta_type in ('evu',):
            subtype = co.affiliation_status_student_evu
            if studieprog2sko[row['studieprogramkode']] in aktiv_sted:
                subtype = co.affiliation_status_student_aktiv
            _process_affiliation(co.affiliation_student,
                                 subtype, affiliations,
                                 studieprog2sko[row['studieprogramkode']])

    if etternavn is None:
        logger.debug("Ikke noe navn på %s" % fnr)
        no_name += 1 
        return

    # TODO: If the person already exist and has conflicting data from
    # another source-system, some mechanism is needed to determine the
    # superior setting.
    
    new_person = Factory.get('Person')(db)
    if fnr2person_id.has_key(fnr):
        new_person.find(fnr2person_id[fnr])

    new_person.populate(mx.DateTime.Date(year, mon, day), gender)

    new_person.affect_names(co.system_fs, co.name_first, co.name_last)
    new_person.populate_name(co.name_first, fornavn)
    new_person.populate_name(co.name_last, etternavn)

    if studentnr is not None:
        new_person.affect_external_id(co.system_fs,
                                      co.externalid_fodselsnr,
                                      co.externalid_studentnr)
        new_person.populate_external_id(co.system_fs, co.externalid_studentnr,
                                        studentnr)
    else:
        new_person.affect_external_id(co.system_fs,
                                      co.externalid_fodselsnr)
    new_person.populate_external_id(co.system_fs, co.externalid_fodselsnr, fnr)

    ad_post, ad_post_private, ad_street = _calc_address(person_info)
    for address_info, ad_const in ((ad_post, co.address_post),
                                   (ad_post_private, co.address_post_private),
                                   (ad_street, co.address_street)):
        # TBD: Skal vi slette evt. eksisterende adresse v/None?
        if address_info is not None:
            logger.debug("Populating address...")
            new_person.populate_address(co.system_fs, ad_const, **address_info)
    # if this is a new Person, there is no entity_id assigned to it
    # until written to the database.
    try:
        op = new_person.write_db()
    except Exception, e:
        logger.exception("write_db failed for person %s: %s", fnr, e)
        # Roll back in case of db exceptions:
        db.rollback()
        return
예제 #47
0
def process_person_callback(person_info):
    """Called when we have fetched all data on a person from the xml
    file.  Updates/inserts name, address and affiliation
    information."""
    global no_name
    try:
        fnr = fodselsnr.personnr_ok(
            "%06d%05d" %
            (int(person_info['fodselsdato']), int(person_info['personnr'])))
        fnr = fodselsnr.personnr_ok(fnr)
        logger.info("Process %s " % (fnr))
        (year, mon, day) = fodselsnr.fodt_dato(fnr)
        if (year < 1970
                and getattr(cereconf, "ENABLE_MKTIME_WORKAROUND", 0) == 1):
            # Seems to be a bug in time.mktime on some machines
            year = 1970
    except fodselsnr.InvalidFnrError:
        logger.warn("Ugyldig fødselsnr: %s" % fnr)
        return

    gender = co.gender_male
    if (fodselsnr.er_kvinne(fnr)):
        gender = co.gender_female

    etternavn = fornavn = None
    studentnr = None
    affiliations = []
    address_info = None
    aktiv_sted = []
    aktivemne_sted = []

    # Iterate over all person_info entries and extract relevant data
    if 'aktiv' in person_info:
        for row in person_info['aktiv']:
            if studieprog2sko[row['studieprogramkode']] is not None:
                aktiv_sted.append(int(
                    studieprog2sko[row['studieprogramkode']]))
                logger.debug("App2akrivts")
    if 'emnestud' in person_info:
        for row in person_info['emnestud']:
            if emne2sko[row['emnekode']] is not None:
                aktivemne_sted.append(int(emne2sko[row['emnekode']]))
                logger.debug('Add sko %s based on emne %s',
                             int(emne2sko[row['emnekode']]), row['emnekode'])

    for dta_type in person_info.keys():
        x = person_info[dta_type]
        p = x[0]
        if isinstance(p, str):
            continue
        # Get name
        if dta_type in (
                'fagperson',
                'opptak',
                'tilbud',
                'evu',
                'privatist_emne',
                'privatist_studieprogram',
                'alumni',
                'emnestud',
        ):
            etternavn = p['etternavn']
            fornavn = p['fornavn']
        if 'studentnr_tildelt' in p:
            studentnr = p['studentnr_tildelt']
        # Get affiliations
        if dta_type in ('fagperson', ):
            _process_affiliation(
                co.affiliation_tilknyttet, co.affiliation_tilknyttet_fagperson,
                affiliations,
                _get_sko(p, 'faknr', 'instituttnr', 'gruppenr',
                         'institusjonsnr'))
        elif dta_type in ('opptak', ):
            for row in x:
                subtype = co.affiliation_status_student_opptak
                if studieprog2sko[row['studieprogramkode']] in aktiv_sted:
                    subtype = co.affiliation_status_student_aktiv
                elif row['studierettstatkode'] == 'EVU':
                    subtype = co.affiliation_status_student_evu
                elif row['studierettstatkode'] == 'FULLFØRT':
                    subtype = co.affiliation_status_student_alumni
                elif int(row['studienivakode']) >= 900:
                    subtype = co.affiliation_status_student_drgrad
                elif _is_new_admission(row.get('dato_studierett_tildelt')):
                    subtype = co.affiliation_status_student_ny
                _process_affiliation(co.affiliation_student, subtype,
                                     affiliations,
                                     studieprog2sko[row['studieprogramkode']])
        elif dta_type in ('emnestud', ):
            for row in x:
                subtype = co.affiliation_status_student_emnestud
                # We may have some situations here where students get
                # emnestud and aonther affiliation to the same sko,
                # but this seems to work for now.
                try:
                    sko = emne2sko[row['emnekode']]
                except KeyError:
                    logger.warn("Fant ingen emner med koden %s", p['emnekode'])
                    continue
                if sko in aktiv_sted:
                    subtype = co.affiliation_status_student_aktiv
                _process_affiliation(co.affiliation_student, subtype,
                                     affiliations, sko)
        elif dta_type in ('privatist_studieprogram', ):
            _process_affiliation(co.affiliation_student,
                                 co.affiliation_status_student_privatist,
                                 affiliations,
                                 studieprog2sko[p['studieprogramkode']])
        elif dta_type in ('privatist_emne', ):
            try:
                sko = emne2sko[p['emnekode']]
            except KeyError:
                logger.warn("Fant ingen emner med koden %s" % p['emnekode'])
                continue
            _process_affiliation(co.affiliation_student,
                                 co.affiliation_status_student_privatist,
                                 affiliations, sko)
        elif dta_type in ('perm', ):
            _process_affiliation(co.affiliation_student,
                                 co.affiliation_status_student_aktiv,
                                 affiliations,
                                 studieprog2sko[p['studieprogramkode']])
        elif dta_type in ('tilbud', ):
            for row in x:
                _process_affiliation(co.affiliation_student,
                                     co.affiliation_status_student_tilbud,
                                     affiliations,
                                     studieprog2sko[row['studieprogramkode']])
        elif dta_type in ('evu', ):
            _process_affiliation(
                co.affiliation_student, co.affiliation_status_student_evu,
                affiliations,
                _get_sko(p, 'faknr_adm_ansvar', 'instituttnr_adm_ansvar',
                         'gruppenr_adm_ansvar'))
        else:
            logger.debug2("No such affiliation type: %s, skipping", dta_type)

    if etternavn is None:
        logger.debug("Ikke noe navn på %s" % fnr)
        no_name += 1
        return

    # TODO: If the person already exist and has conflicting data from
    # another source-system, some mechanism is needed to determine the
    # superior setting.

    new_person = Factory.get('Person')(db)
    if fnr in fnr2person_id:
        new_person.find(fnr2person_id[fnr])

    new_person.populate(mx.DateTime.Date(year, mon, day), gender)

    new_person.affect_names(co.system_fs, co.name_first, co.name_last)
    new_person.populate_name(co.name_first, fornavn)
    new_person.populate_name(co.name_last, etternavn)

    if studentnr is not None:
        new_person.affect_external_id(co.system_fs, co.externalid_fodselsnr,
                                      co.externalid_studentnr)
        new_person.populate_external_id(co.system_fs, co.externalid_studentnr,
                                        studentnr)
    else:
        new_person.affect_external_id(co.system_fs, co.externalid_fodselsnr)
    new_person.populate_external_id(co.system_fs, co.externalid_fodselsnr, fnr)

    ad_post, ad_post_private, ad_street = _calc_address(person_info)
    for address_info, ad_const in ((ad_post, co.address_post),
                                   (ad_post_private, co.address_post_private),
                                   (ad_street, co.address_street)):
        # TBD: Skal vi slette evt. eksisterende adresse v/None?
        if address_info is not None:
            logger.debug("Populating address %s for %s", ad_const, studentnr)
            new_person.populate_address(co.system_fs, ad_const, **address_info)
    # if this is a new Person, there is no entity_id assigned to it
    # until written to the database.
    try:
        op = new_person.write_db()
    except Exception, e:
        logger.exception("write_db failed for person %s: %s", fnr, e)
        # Roll back in case of db exceptions:
        db.rollback()
        return
예제 #48
0
    def new_project(self, input):
        """Create a given project.

        TODO: A lot of this code should be moved into e.g. TSD's OU mixin, or
        somewhere else to be usable both by various scripts and bofhd.

        @type input: dict
        @param input: The survey answers about the requested project.
        """
        pname = input['p_id']
        logger.info('New project: %s', pname)
        ou = self._create_ou(input)

        # Update the requestee for the project:
        pe = self._get_person()
        self._update_person(pe, input)

        # Give the respondent an affiliation to the project.
        # If the respondent sets himself as the Project Owner (responsible), it
        # gets status as the owner. Otherwise we give him PA status:
        # TBD: do we need to differentiate between owner and PA?
        status = co.affiliation_status_project_admin
        if self.fnr == input['rek_owner']:
            status = co.affiliation_status_project_owner
        pe.populate_affiliation(source_system=co.system_nettskjema,
                                ou_id=ou.entity_id,
                                status=status,
                                affiliation=co.affiliation_project)
        pe.write_db()

        # Check the responsible and give access to the project by an
        # affiliation:
        if self.fnr != input['rek_owner']:
            # TODO: Should we create a person with this ID or not?
            try:
                pe2 = self._get_person(input['rek_owner'],
                                       create_nonexisting=False)
                pe2.populate_affiliation(
                    source_system=co.system_nettskjema,
                    ou_id=ou.entity_id,
                    affiliation=co.affiliation_project,
                    status=co.affiliation_status_project_owner)
                # Note that no name or anything else is now set, so we wait
                # with the account.
                pe2.write_db()
            except Errors.NotFoundError:
                logger.warn("Project owner not found: %s", input['rek_owner'])

        # Give the PA an account:
        ac = self._create_account(pe, ou, input['pa_username'])

        # Fill the pre approve list with external ids:
        pre_approve_list = set()
        for fnr in set(input['p_persons'].split()):
            for fnr1 in set(fnr.split(',')):
                try:
                    fnr1 = fodselsnr.personnr_ok(fnr1)
                except fodselsnr.InvalidFnrError:
                    logger.debug("Ignoring invalid fnr: %s", fnr1)
                    continue
                except ValueError:
                    logger.debug("Ignoring invalid fnr: %s", fnr1)
                    continue
                pre_approve_list.add(fnr1)
        if pre_approve_list:
            logger.debug("Pre approvals: %s", ', '.join(pre_approve_list))
            ou.add_pre_approved_persons(pre_approve_list)
            ou.write_db()
        # TODO:How should we signal that a new project is waiting for approval?
        return True
예제 #49
0
    def approve_persons(self, input):
        """Let project owner and PAs approve more persons to their project.

        Sending this through Nettskjema is needed as we have no web GUI inside
        TSD in the beginning, and project admins need a way to approve people
        for their projects.
        """
        # Find the project:
        pid = input['p_id']
        ou.clear()
        ou.find_by_tsd_projectid(pid)
        logger.info('Approve persons for project: %s', pid)

        # Find the requestor
        pe = self._get_person()

        # The requestor must be owner or PA:
        if not list(
                pe.list_affiliations(
                    person_id=pe.entity_id,
                    affiliation=co.affiliation_project,
                    status=(co.affiliation_status_project_owner,
                            co.affiliation_status_project_admin),
                    ou_id=ou.entity_id)):
            # TODO: Delete the JSON file?
            raise BadInputError("Person %s is not PA of project %s" %
                                (pe.entity_id, pid))

        # Update contact info for PA:
        self._update_person(pe, input)

        # Try to find and add the given person to the project
        pe2 = Factory.get('Person')(db)
        pre_approvals = set()
        for fnr in set(input['p_persons'].split()):
            for fnr1 in set(fnr.split(',')):
                try:
                    fnr1 = fodselsnr.personnr_ok(fnr1)
                except fodselsnr.InvalidFnrError:
                    logger.debug("Ignoring invalid fnr: %s", fnr1)
                    continue
                # Find the person:
                try:
                    pe2 = self._get_person(fnr1, create_nonexisting=False)
                except Errors.NotFoundError:
                    logger.info(
                        "Person %s not found, added to pre approve list", fnr1)
                    pre_approvals.add(fnr1)
                    continue
                # Affiliate person to project:
                if not pe2.list_affiliations(
                        person_id=pe2.entity_id,
                        ou_id=ou.entity_id,
                        affiliation=co.affiliation_project):
                    logger.info("Approve person %s for project: %s", fnr1, pid)
                    pe2.populate_affiliation(
                        source_system=co.system_nettskjema,
                        ou_id=ou.entity_id,
                        affiliation=co.affiliation_project,
                        status=co.affiliation_status_project_member)
                # Remove pending aff, if set. Note that this also removes any
                # other pending statuses,
                # if we should create more of those in the future.
                pe2.delete_affiliation(ou_id=ou.entity_id,
                                       affiliation=co.affiliation_pending,
                                       source=co.system_nettskjema)
                pe2.write_db()

                # Find the member's project account, if it exist:
                accounts = self._get_project_account(pe2, ou)
                if not accounts:
                    logger.info(
                        "No project account for %s, added to approve list",
                        fnr1)
                    pre_approvals.add(fnr1)
                    continue
                ac.clear()
                ac.find(accounts[0]['account_id'])
                logger.info("Approving person %s PM to %s: %s ", fnr1,
                            ac.account_name, pid)

                # Update the account types:
                ac.set_account_type(ou.entity_id, co.affiliation_project)
                # TODO: check if this works if the type is already set:
                ac.del_account_type(ou.entity_id, co.affiliation_pending)
                ac.del_account_type(ou.entity_id, co.affiliation_pending)
                # Remove quarantine:
                ac.delete_entity_quarantine(co.quarantine_not_approved)
                ac.write_db()

                # Promote posix:
                if ou.is_approved():
                    pu = Factory.get('PosixUser')(db)
                    try:
                        pu.find(ac.entity_id)
                    except Errors.NotFoundError:
                        uid = pu.get_free_uid()
                        pu.populate(uid,
                                    None,
                                    None,
                                    co.posix_shell_bash,
                                    parent=ac,
                                    creator_id=systemaccount_id)
                        logger.debug("Promote POSIX, UID: %s", ac.entity_id,
                                     uid)
                        pu.write_db()

        # Store those not processed for later approval:
        if pre_approvals:
            logger.debug("Remaining non-existing persons: %d",
                         len(pre_approvals))
            ou.add_pre_approved_persons(pre_approvals)
            ou.write_db()
        return True
예제 #50
0
def process_person_callback(person_info):
    """Called when we have fetched all data on a person from the xml
    file.  Updates/inserts name, address and affiliation
    information."""
    global no_name
    try:
        fnr = fodselsnr.personnr_ok(
            "%06d%05d" % (int(person_info['fodselsdato']),
                          int(person_info['personnr'])))
        fnr = fodselsnr.personnr_ok(fnr)
        logger.info("Process %s " % (fnr))
        (year, mon, day) = fodselsnr.fodt_dato(fnr)
        if (year < 1970
                and getattr(cereconf, "ENABLE_MKTIME_WORKAROUND", 0) == 1):
            # Seems to be a bug in time.mktime on some machines
            year = 1970
    except fodselsnr.InvalidFnrError:
        logger.warn("Ugyldig fødselsnr: %s" % fnr)
        return

    gender = co.gender_male
    if(fodselsnr.er_kvinne(fnr)):
        gender = co.gender_female

    etternavn = fornavn = None
    studentnr = None
    affiliations = []
    address_info = None
    aktiv_sted = []
    aktivemne_sted = []

    # Iterate over all person_info entries and extract relevant data
    if 'aktiv' in person_info:
        for row in person_info['aktiv']:
            if studieprog2sko[row['studieprogramkode']] is not None:
                aktiv_sted.append(
                    int(studieprog2sko[row['studieprogramkode']]))
                logger.debug("App2akrivts")
    if 'emnestud' in person_info:
        for row in person_info['emnestud']:
            if emne2sko[row['emnekode']] is not None:
                aktivemne_sted.append(int(emne2sko[row['emnekode']]))
                logger.debug('Add sko %s based on emne %s',
                             int(emne2sko[row['emnekode']]), row['emnekode'])

    for dta_type in person_info.keys():
        x = person_info[dta_type]
        p = x[0]
        if isinstance(p, str):
            continue
        # Get name
        if dta_type in (
                'fagperson',
                'opptak',
                'tilbud',
                'evu',
                'privatist_emne',
                'privatist_studieprogram',
                'alumni',
                'emnestud', ):
            etternavn = p['etternavn']
            fornavn = p['fornavn']
        if 'studentnr_tildelt' in p:
            studentnr = p['studentnr_tildelt']
        # Get affiliations
        if dta_type in ('fagperson',):
            _process_affiliation(co.affiliation_tilknyttet,
                                 co.affiliation_tilknyttet_fagperson,
                                 affiliations,
                                 _get_sko(p,
                                          'faknr',
                                          'instituttnr',
                                          'gruppenr',
                                          'institusjonsnr'))
        elif dta_type in ('opptak', ):
            for row in x:
                subtype = co.affiliation_status_student_opptak
                if studieprog2sko[row['studieprogramkode']] in aktiv_sted:
                    subtype = co.affiliation_status_student_aktiv
                elif row['studierettstatkode'] == 'EVU':
                    subtype = co.affiliation_status_student_evu
                elif row['studierettstatkode'] == 'FULLFØRT':
                    subtype = co.affiliation_status_student_alumni
                elif int(row['studienivakode']) >= 900:
                    subtype = co.affiliation_status_student_drgrad
                elif _is_new_admission(row.get('dato_studierett_tildelt')):
                    subtype = co.affiliation_status_student_ny
                _process_affiliation(co.affiliation_student,
                                     subtype,
                                     affiliations,
                                     studieprog2sko[row['studieprogramkode']])
        elif dta_type in ('emnestud',):
            for row in x:
                subtype = co.affiliation_status_student_emnestud
                # We may have some situations here where students get
                # emnestud and aonther affiliation to the same sko,
                # but this seems to work for now.
                try:
                    sko = emne2sko[row['emnekode']]
                except KeyError:
                    logger.warn("Fant ingen emner med koden %s", p['emnekode'])
                    continue
                if sko in aktiv_sted:
                    subtype = co.affiliation_status_student_aktiv
                _process_affiliation(co.affiliation_student, subtype,
                                     affiliations, sko)
        elif dta_type in ('privatist_studieprogram',):
            _process_affiliation(co.affiliation_student,
                                 co.affiliation_status_student_privatist,
                                 affiliations,
                                 studieprog2sko[p['studieprogramkode']])
        elif dta_type in ('privatist_emne',):
            try:
                sko = emne2sko[p['emnekode']]
            except KeyError:
                logger.warn("Fant ingen emner med koden %s" % p['emnekode'])
                continue
            _process_affiliation(co.affiliation_student,
                                 co.affiliation_status_student_privatist,
                                 affiliations,
                                 sko)
        elif dta_type in ('perm',):
            _process_affiliation(co.affiliation_student,
                                 co.affiliation_status_student_aktiv,
                                 affiliations,
                                 studieprog2sko[p['studieprogramkode']])
        elif dta_type in ('tilbud',):
            for row in x:
                _process_affiliation(co.affiliation_student,
                                     co.affiliation_status_student_tilbud,
                                     affiliations,
                                     studieprog2sko[row['studieprogramkode']])
        elif dta_type in ('evu', ):
            _process_affiliation(co.affiliation_student,
                                 co.affiliation_status_student_evu,
                                 affiliations,
                                 _get_sko(p,
                                          'faknr_adm_ansvar',
                                          'instituttnr_adm_ansvar',
                                          'gruppenr_adm_ansvar'))
        else:
            logger.debug2("No such affiliation type: %s, skipping", dta_type)

    if etternavn is None:
        logger.debug("Ikke noe navn på %s" % fnr)
        no_name += 1
        return

    # TODO: If the person already exist and has conflicting data from
    # another source-system, some mechanism is needed to determine the
    # superior setting.

    new_person = Factory.get('Person')(db)
    if fnr in fnr2person_id:
        new_person.find(fnr2person_id[fnr])

    new_person.populate(mx.DateTime.Date(year, mon, day), gender)

    new_person.affect_names(co.system_fs, co.name_first, co.name_last)
    new_person.populate_name(co.name_first, fornavn)
    new_person.populate_name(co.name_last, etternavn)

    if studentnr is not None:
        new_person.affect_external_id(co.system_fs,
                                      co.externalid_fodselsnr,
                                      co.externalid_studentnr)
        new_person.populate_external_id(co.system_fs, co.externalid_studentnr,
                                        studentnr)
    else:
        new_person.affect_external_id(co.system_fs,
                                      co.externalid_fodselsnr)
    new_person.populate_external_id(co.system_fs, co.externalid_fodselsnr, fnr)

    ad_post, ad_post_private, ad_street = _calc_address(person_info)
    for address_info, ad_const in ((ad_post, co.address_post),
                                   (ad_post_private, co.address_post_private),
                                   (ad_street, co.address_street)):
        # TBD: Skal vi slette evt. eksisterende adresse v/None?
        if address_info is not None:
            logger.debug("Populating address %s for %s", ad_const, studentnr)
            new_person.populate_address(co.system_fs, ad_const, **address_info)
    # if this is a new Person, there is no entity_id assigned to it
    # until written to the database.
    try:
        op = new_person.write_db()
    except Exception, e:
        logger.exception("write_db failed for person %s: %s", fnr, e)
        # Roll back in case of db exceptions:
        db.rollback()
        return