def bind_unic_to_position(self): unic_repo = Unic_username_repo(self.constr_lora) unic_list = unic_repo.get_unic_usernames('WHERE opus_id is NULL') pos_repo = Position_repo(self.constr_lora) unics_to_update = {} for unic_userid in unic_list: unic = unic_list[unic_userid] positions_related_to_cpr = pos_repo.get_positions( "WHERE person_ref = '%s'" % unic.cpr) pos_amount = len(positions_related_to_cpr) if pos_amount == 1: # hvis der kun er en stilling som passer til CPR nummeret på et unic username, bliver der et match. # det betyder at der godt kan kobles flere unic brugernavne på en stilling - men dette sker kun når de er på flere institutioner, og bør sorteres fra på anden vis unic.opus_id = list(positions_related_to_cpr.keys())[0] unics_to_update[unic_userid] = unic elif pos_amount > 1: # Når der er flere stillinger som kan matche et unic brugernavn, forsøger vi at matche en stilling som passer ud fra institutionens ID. # Er der flere end én laver vi et gæt på den stilling med flest timer, er der intet match så opretter vi ingen forbindelse los_id_to_match = self.institutions[unic.institution_nr] best_guess_for_match_position = None for opus_id in positions_related_to_cpr: pos = positions_related_to_cpr[opus_id] if Unic_to_position_service.is_unic_username_and_position_match( self, pos.los_id, los_id_to_match): if best_guess_for_match_position == None: best_guess_for_match_position = pos else: if best_guess_for_match_position.weekly_hours_numerator < pos.weekly_hours_numerator: best_guess_for_match_position = pos unic.opus_id = pos.opus_id unics_to_update[unic_userid] = unic else: continue unic_repo.update_unic_usernames(unics_to_update)
def link_user_to_position(self): ''' Funktion der kobler positions, hvis data kommer fra fra OPUS løn og personale med users, hvis data kommer fra AD. Hvis der er en AD bruger med OPUS ID, kan der oprettes forbindelse mellem de to. Ligeledes, hvis der har været en forbindelse, som ikke er der længere, skal linket mellem de to fjernes. IT har et redskab hvor de kan flytte opus_id på brugere, derfor skal vi også tjekke om opus_id på position og user stemmer over ens. ''' pos_repo = Position_repo(self.constr_lora) usr_repo = User_repo(self.constr_lora) positions = pos_repo.get_positions() users = usr_repo.get_users() positions_to_update = {} for opus_id in positions: position = positions[opus_id] if opus_id in users: user = users[opus_id] if position.uuid_userref == None or position.uuid_userref != user.uuid or position.opus_id != user.opus_id: position.uuid_userref = user.uuid position.updated = True positions_to_update[opus_id] = position else: if position.uuid_userref != None: # hvis brugeren ikke findes i dbo.users, betyder det at ad_brugeren er slettet fra position, i såfald skal brugere i "slettes køen" med uuid før uuid fjernes. position.ad_user_deleted = True positions_to_update[opus_id] = position pos_repo.update_positions(positions_to_update)
def handle_updated_persons(self): ''' Funktion som håndterer opdateringer af personer, hvis de har en IT bruger, skal der oprettes en besked om at deres stilling skal opdateres, og dermed komme med i køen. Det kan f.eks. være hvis man skifter navn, det skal opdateres i de forskellige IT systemer. Vi er kun interserede i de positions, der har en IT bruger fordi der ikke er nogen systemer at opdaterer dem i hvis de ikke har. Hvis en position er markeret som slettet eller opdateret i forvejen, ryger den allerede i køen, derfor er der ikke behov for at behandle disse ''' person_repo = Person_repo(self.constr_lora) updated_persons = person_repo.get_persons('WHERE [updated] = 1') if len(updated_persons) > 0: positions_to_update = {} persons_to_update = {} position_repo = Position_repo(self.constr_lora) # henter udelukkende de nødvendige positions, se funktion beskrivelse poss_with_usr = position_repo.get_positions( 'WHERE [uuid_userref] IS NOT NULL and [updated] = 0 and [deleted] = 0' ) for opus_id in poss_with_usr: pos = poss_with_usr[opus_id] if pos.person_ref in updated_persons: pos.updated = True positions_to_update[opus_id] = pos position_repo.update_positions(positions_to_update) for cpr in updated_persons: per = updated_persons[cpr] per.updated = False persons_to_update[cpr] = per person_repo.update_persons(persons_to_update)
def __init__(self, constr_lora): self.constr_lora = constr_lora org_repo = Orgunit_repo(constr_lora) pos_repo = Position_repo(constr_lora) unic_institution_repo = Unic_institution_repo(constr_lora) self.orgunits = org_repo.get_orgunits() self.positions = pos_repo.get_positions() self.institutions = unic_institution_repo.get_institutions()
def get_poisitions(self, orgs): ''' Funktion som finder de positions, der hører til en af de orgunits som skal synkroniseres med kalenda-greenbyte ''' pos_repo = Position_repo(self.lora_constr) poss = pos_repo.get_positions( 'WHERE [deleted] = 0 and [ad_user_deleted] = 0') result = {} for opus_id in poss: pos = poss[opus_id] if pos.los_id in orgs: result[opus_id] = pos return result
def create_org_json(self): result = Sts_collection_json() org_repo = Orgunit_repo(self.lora_constr) pos_repo = Position_repo(self.lora_constr) per_repo = Person_repo(self.lora_constr) usr_repo = User_repo(self.lora_constr) orgs = org_repo.get_orgunits() poss = pos_repo.get_positions( 'WHERE [uuid_userref] is not NULL and [deleted] = 0 and [ad_user_deleted] = 0' ) pers = per_repo.get_persons() usrs = usr_repo.get_users() for los_id in orgs: sofd_org = orgs[los_id] if sofd_org.uuid is None or len(sofd_org.uuid) < 1: continue sofd_manager = None jsman = None # den sidste del er pga de ledere der også er politikere som snyder med deres brugerkonti, det skal der egentligt rydes op i if sofd_org.manager_opus_id != None and int( sofd_org.manager_opus_id) in usrs: sofd_manager = usrs[int(sofd_org.manager_opus_id)] jsman = Manager_json(sofd_manager.uuid, sofd_manager.userid) jsorg = Orgunit_json(sofd_org.uuid, sofd_org.longname, sofd_org.parent_orgunit_uuid, jsman) result.add_org(jsorg) for opus_id in poss: sofd_pos = poss[opus_id] sofd_usr = usrs[opus_id] if sofd_usr.userid is None or len(sofd_usr.userid) < 1: continue sofd_per = pers[sofd_pos.person_ref] if sofd_pos.los_id not in orgs: continue sofd_org = orgs[sofd_pos.los_id] if sofd_org.uuid is None or len(sofd_org.uuid) < 1: continue json_pos = Position_json(sofd_pos.position_title, sofd_org.uuid) json_usr = User_json(sofd_pos.uuid_userref, sofd_usr.userid, sofd_per.firstname + ' ' + sofd_per.lastname, sofd_usr.email) json_usr.add_position(json_pos) result.add_user(json_usr) result = json.dumps(result.reprJSON(), cls=ComplexEncoder, ensure_ascii=False).encode('utf8') return result
def update_user_queue(self): ''' Funktion der bygger vores user queue ''' # sørger for at alle person opdateringer er gået igennem før funktionen kører User_queue_service.handle_updated_persons(self) position_repo = Position_repo(self.constr_lora) updated_positions = position_repo.get_positions( 'where [updated] = 1 or [deleted] = 1 or [ad_user_deleted] = 1') if len(updated_positions) > 0: positions_to_update = {} queue_items_to_insert = {} queue_repo = Queue_users_repo(self.constr_lora) i = 0 for opus_id in updated_positions: pos = updated_positions[opus_id] # deleted vejer tungere end updated, derfor bliver positions, som både er opdaterede og slettede - slettet if pos.deleted == True or pos.ad_user_deleted == True: if pos.uuid_userref != None: # hvis der er et uuid, er der en user og så skal STS org opdateres (False) queue_item = Queue_user(i, pos.uuid_userref, opus_id, 'Deleted', False) i += 1 queue_items_to_insert[i] = queue_item # nu kan UUID fjernes fra position pos.uuid_userref = None pos.ad_user_deleted = False positions_to_update[opus_id] = pos else: # Hvis der ikke er et uuid, er der ikke en user og så behøves STS ikke at blive opdateret (True) true er lidt snyd, men altså queue_item = Queue_user(i, 'none', opus_id, 'Deleted', True) i += 1 queue_items_to_insert[i] = queue_item elif pos.updated == True and pos.deleted == False and pos.ad_user_deleted == False: pos.updated = False if pos.uuid_userref != None: queue_item = Queue_user(i, pos.uuid_userref, opus_id, 'Updated', False) i += 1 queue_items_to_insert[i] = queue_item positions_to_update[opus_id] = pos queue_repo.insert_user_queue(queue_items_to_insert) position_repo.update_positions(positions_to_update)
def remove_deleted_managers_from_orgunits(self): ''' funktion som fjerner ledere, der ikke længere er ansat i organisationen ''' org_repo = Orgunit_repo(self.constr_lora) pos_repo = Position_repo(self.constr_lora) orgs = org_repo.get_orgunits('WHERE [manager_opus_id] IS NOT NULL') managers = pos_repo.get_positions( 'WHERE [is_manager] = 1 and [deleted] = 0') orgs_to_update = {} for los_id in orgs: org = orgs[los_id] if org.manager_opus_id in managers: continue else: org.manager_opus_id = None org.updated = True orgs_to_update[los_id] = org org_repo.update_orgunits(orgs_to_update)
def clean_user_queue(self): ''' Funktion der ryder op i [pyt].[positions] og [queue].[users_queue] tabellerne, efter synkroniseringerne ud til forskellige systemer er håndteret. ''' queue_repo = Queue_users_repo(self.constr_lora) users_and_positions_that_are_handled = queue_repo.get_completed_user_queues( ) pos_repo = Position_repo(self.constr_lora) deleted_positions = pos_repo.get_positions('WHERE [Deleted] = 1') for key in users_and_positions_that_are_handled: pu = users_and_positions_that_are_handled[key] # Tjekker om alle synkroniseringer, der skal gennemføres er kørt if pu.all_syncs_completed(): # hvis en bruger er slettet og denne er synkroniseret, kan den nu fjernes fra SOFD'en if pu.change_type == 'Deleted': if pu.opus_id in deleted_positions: pos_repo.delete_position(pu.opus_id) # sletter item fra køen queue_repo.delete_person(pu.system_id)
def set_nearest_manager(self): ''' funktion som sætter nærmeste leder på medarbejderne gennem rekursion hvor der ledes op i org hierakiet indtil der findes en organisation som har en leder. Det her skal køres efter de to andre manager scripts er kørt, fordi det tager udgangspunkt i organsiations tabellen og de ledere, som er påtrykt organisations enheder ''' org_repo = Orgunit_repo(self.constr_lora) pos_repo = Position_repo(self.constr_lora) orgs = org_repo.get_orgunits() positions = pos_repo.get_positions('WHERE [deleted] = 0') positions_to_update = {} for pkey in positions: position = positions[pkey] # fordi managers ikke skal have sig selv som leder skal vi lige finde det rigtige los_id, # hvis du er leder, starter vi på niveauet over dig. # dette skal stoppe ved borgmesteren los_id_for_recursion = position.los_id # hvis der er fucket op i løn, tjekker vi lige at der faktisk er en orgunit if los_id_for_recursion not in orgs: continue if position.is_manager == True: org_actual = orgs[los_id_for_recursion] if org_actual.niveau > 2: los_id_for_recursion = org_actual.parent_orgunit_los_id manager_id = Manager_setup_service.get_manager( self, los_id_for_recursion, orgs) manager_uuid = positions[manager_id].uuid_userref if position.manager_opus_id != manager_id or position.manager_uuid_userref != manager_uuid: position.manager_opus_id = manager_id position.manager_uuid_userref = manager_uuid position.updated = True positions_to_update[pkey] = position else: continue pos_repo.update_positions(positions_to_update)
def set_orgunit_manager(self): ''' funktion som tilføjer ledere til orgunits, mange orgunits vil ikke have en leder ''' org_repo = Orgunit_repo(self.constr_lora) pos_repo = Position_repo(self.constr_lora) orgs = org_repo.get_orgunits() managers = pos_repo.get_positions( 'WHERE [is_manager] = 1 and [deleted] = 0') orgs_to_update = {} for opus_id in managers: manager = managers[opus_id] if manager.los_id not in orgs: continue org = orgs[manager.los_id] if org.manager_opus_id == manager.opus_id: continue else: org.manager_opus_id = manager.opus_id org.updated = True orgs_to_update[org.los_id] = org org_repo.update_orgunits(orgs_to_update)
def update_positions(self): ''' Funktion der indsætter nye positions fra OPUS, opdaterer positions hvor der er forandringer og sletter positions som ikke længere er en del af vores lønsystem ''' pos_repo = Position_repo(self.constr_lora) sofd_positions = pos_repo.get_positions('WHERE [deleted] = 0') opus_positions = self.get_positions() positions_to_insert = {} positions_to_update = {} # key er opus_id (medarbejdernummer) for key in opus_positions: opus_pos = opus_positions[key] # hvis stillingen findes, tjekkes den for forandringer if key in sofd_positions: sofd_pos = sofd_positions[key] if opus_pos.los_id == sofd_pos.los_id and opus_pos.person_ref == sofd_pos.person_ref and opus_pos.position_title == sofd_pos.position_title \ and opus_pos.position_id == sofd_pos.position_id and opus_pos.position_title_short == sofd_pos.position_title_short and \ opus_pos.position_paygrade_text == sofd_pos.position_paygrade_text and opus_pos.is_manager == sofd_pos.is_manager and \ opus_pos.payment_method == sofd_pos.payment_method and opus_pos.payment_method_text == sofd_pos.payment_method_text and \ opus_pos.weekly_hours_numerator == sofd_pos.weekly_hours_numerator and opus_pos.weekly_hours_denominator == sofd_pos.weekly_hours_denominator \ and opus_pos.invoice_recipient == sofd_pos.invoice_recipient and opus_pos.pos_pnr == sofd_pos.pos_pnr and opus_pos.dsuser == sofd_pos.dsuser \ and opus_pos.start_date == sofd_pos.start_date and opus_pos.leave_date == sofd_pos.leave_date: # Når der ikke er forandringer, springer vi videre til næste position continue else: opus_pos.updated = True positions_to_update[key] = opus_pos # ellers indsættes en ny else: positions_to_insert[key] = opus_pos for key in sofd_positions: # hvis en nøgle (opus_id) er i SOFD men ikke i OPUS udtræk er det fordi stillingen er nedlagt if key not in opus_positions: pos = sofd_positions[key] pos.deleted = True positions_to_update[key] = pos pos_repo.insert_positions(positions_to_insert) pos_repo.update_positions(positions_to_update)
def sync_users(self): ''' funktion, der synkroniserer alle de users, som er i køen. der kigges på sts_org feltet fordi det er her vi registerer om et queue item er sendt til sykronisering eller ej. ''' queue_repo = Queue_users_repo(self.constr_lora) queue = queue_repo.get_user_queues("WHERE sts_org = 0") if (len(queue) > 0): synced_queue_items = {} usr_repo = User_repo(self.constr_lora) per_repo = Person_repo(self.constr_lora) pos_repo = Position_repo(self.constr_lora) orgs = self.org_repo.get_orgunits() usrs = usr_repo.get_users() pers = per_repo.get_persons() poses = pos_repo.get_positions() for system_id in queue: queue_item = queue[system_id] if queue_item.change_type == 'Updated': ''' Når en medarbejder forlader organisationen, bliver der typisk oprettet et "updated" event i opus, dagen før der oprettes et "deleted" event. Hvis synkroniseirngen af en eller anden årsag går galt. Eller begge events kommer samme dag (kmd levere kun data 5 dage om ugen, så det kan poole) vil denne "updated" stoppe synkroniseringen fordi stillingen slås op i pyt.positions når der skal opdateres. Her vil stillinge dog ikke længere være, fordi den også er deleted - og et deleted event fjerne stillingen når eventet er føjet til køen. Følgene IF sætning fikser det problem. ''' if queue_item.opus_id not in poses or queue_item.opus_id not in usrs: queue_item.change_type = 'Deleted' synced_queue_items[system_id] = queue_item continue pos = poses[queue_item.opus_id] if pos.person_ref not in pers: queue_item.change_type = 'Deleted' synced_queue_items[system_id] = queue_item continue usr = usrs[queue_item.opus_id] per = pers[pos.person_ref] org = orgs[pos.los_id] person_json = Person_json( per.get_firstname_including_displayname() + ' ' + per.get_lastname_including_displayname(), per.cpr) position_json = Position_json(org.uuid, org.longname) usr_json = User_json(pos.uuid_userref, usr.userid, usr.email, org.longname, queue_item.opus_id, usr.phone) usr_json.add_person(person_json) usr_json.add_position(position_json) json_to_submit = json.dumps( usr_json.reprJSON(), cls=ComplexEncoder, ensure_ascii=False).encode('utf8') result = Os2sync_sync_service.post_json( self, self.user_api_url, json_to_submit) if result == 200: queue_item.sts_org = True synced_queue_items[system_id] = queue_item # det følgende opdaterer status i DB per enhed, i stedet for at tage dem i et ruf. Kan bruges når der er MANGE i kø #if result == 200: # queue_item.sts_org = True # queue_repo.update_queue_user(queue_item) elif queue_item.change_type == 'Deleted': #print('deleted') end_point_url_delete = self.user_api_url + '/' + queue_item.uuid result = Os2sync_sync_service.delete_action( self, end_point_url_delete) if result == 200: queue_item.sts_org = True synced_queue_items[system_id] = queue_item # det følgende opdaterer status i DB per enhed, i stedet for at tage dem i et ruf. Kan bruges når der er MANGE i kø #if result == 200: # queue_item.sts_org = True # queue_repo.update_queue_user(queue_item) queue_repo.update_queue_users(synced_queue_items)
def build_people_and_positions_from_opusxml(self): ''' OPUS XML filen fra KMD indeholder to tags employee og orgunit. Denne funktion splitter "employee" tagget op i to dictionaries, som indeholder henholdsvis Person og Position objekter, der bliver kædet sammen med CPR. Der er også en reference til den orgunit som en position tilhører. I Person dictionary er CPR nøgle. I Position dictionary er OPUS ID (medarbejdernr) nøgle. Begge er unikke og ændre sig ikke. OBS: Et CPR nr kan i teorien godt ændre sig, hvis en person f.eks. skifter køn, men OPUS kan ikke håndtere den slags forandringer, derfor er det heller ikke et problem i dette script. Hvis OPUS engang bliver woke, skal scriptet her rettes til. ''' pos_repo = Position_repo(self.constr_lora) disabled_orgs = pos_repo.get_disabled_orgunits() for emp in self.root.findall('employee'): if emp.get('action') == None: cpr = emp.find('cpr').text opus_id = emp.get('id') los_id = int(emp.find('orgUnit').text) if los_id in disabled_orgs: # vi har nogle orgunits som indeholder ikke-medarbejdere, dem ønsker vi ikke i SOFD'en og de bliver sorteret fra her. continue userId = None if emp.find('userId') != None: userId = emp.find('userId').text # sætter en start dato fra de forskellige datapunkter vi har at gøre godt med. Det er lidt rodet fordi det er en manuel indtastning fra løn # Entry date er aldrig none, men tom når der ikke er en dato startdate = None if emp.find('entryDate').text != None: startdate = emp.find('entryDate').text elif emp.find('initialEntry') != None: startdate = emp.find('initialEntry').text elif emp.find('entryIntoGroup') != None: startdate = emp.find('entryIntoGroup').text if startdate == None: # Hvis der ikke er en start dato for en stilling, er den oprettet forkert i løn og kan ikke meldes til vores andre systemer, derfor ignorer vi den. print(opus_id) continue else: startdate = datetime.strptime(startdate, '%Y-%m-%d').date() leavedate = None if emp.find('leaveDate') != None: leavedate = emp.find('leaveDate').text per = Person(cpr, emp.find('firstName').text, emp.find('lastName').text, emp.find('address').text, emp.find('postalCode').text, emp.find('city').text, emp.find('country').text, True) pos = Position(opus_id, None, los_id, cpr, emp.find('cpr').get('suppId'), emp.find('position').text, emp.find('positionId').text, emp.find('positionShort').text, emp.find('payGradeText').text, emp.find('isManager').text, emp.find('workContract').text, emp.find('workContractText').text, emp.find('numerator').text, emp.find('denominator').text, emp.find('invoiceRecipient').text, emp.find('productionNumber').text, userId, startdate, leavedate, None, None, True, False, False) self.persons[cpr] = per self.positions[int(opus_id)] = pos elif emp.get('action') == 'leave': # OPUS filen indeholder alle stillinger der har eksisteret, vi sorterer de nedlagte fra her. continue