Codebeispiele: Ausgangszustand
This commit is contained in:
commit
25c2405f35
101
h1wsutils.py
Normal file
101
h1wsutils.py
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# @author Dr. Wolfgang Wiedermann
|
||||||
|
#
|
||||||
|
# Webservice-Handling für HISinOne
|
||||||
|
#
|
||||||
|
# Abhängigkeiten:
|
||||||
|
# * zeep (pip install zeep oder python -m pip install zeep)
|
||||||
|
#
|
||||||
|
# Doku der HIS zu Webservices im Bereich Bewerbung:
|
||||||
|
# * https://wiki.his.de/mediawiki/index.php/APP-Webservices-HISinOne
|
||||||
|
#
|
||||||
|
|
||||||
|
class H1WebServiceUtils:
|
||||||
|
#
|
||||||
|
# Attribute
|
||||||
|
#
|
||||||
|
user = None
|
||||||
|
password = None
|
||||||
|
host = "localhost"
|
||||||
|
wsdl_folder = "wsdl"
|
||||||
|
service_names = []
|
||||||
|
proxies = {}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Konstruktor
|
||||||
|
#
|
||||||
|
def __init__(self, user, password, host, wsdl_folder, services):
|
||||||
|
self.user = user
|
||||||
|
self.password = password
|
||||||
|
self.host = host
|
||||||
|
self.wsdl_folder = wsdl_folder
|
||||||
|
self.service_names = services
|
||||||
|
|
||||||
|
#
|
||||||
|
# Einzelne WSDL laden und relevante Teile ersetzen
|
||||||
|
#
|
||||||
|
def __download_wsdl(self, wsdl):
|
||||||
|
global HOST
|
||||||
|
import urllib.request
|
||||||
|
import re
|
||||||
|
|
||||||
|
txt = ""
|
||||||
|
new_file = "{0}/{1}.wsdl".format(self.wsdl_folder, wsdl)
|
||||||
|
urllib.request.urlretrieve("https://{0}.kdv-fh-bayern.de/qisserver/services2/{1}?wsdl".format(self.host, wsdl), new_file)
|
||||||
|
|
||||||
|
with open(new_file) as f:
|
||||||
|
txt = f.read()
|
||||||
|
txt = re.sub(r"http://[^/]+:8080/", ("https://{0}.kdv-fh-bayern.de/".format(self.host)), txt)
|
||||||
|
txt = re.sub(r"https://[^/]+:8443/", ("https://{0}.kdv-fh-bayern.de/".format(self.host)), txt)
|
||||||
|
txt = re.sub(r"http://localhost/", ("https://{0}.kdv-fh-bayern.de/".format(self.host)), txt)
|
||||||
|
with open(new_file, "w+") as f:
|
||||||
|
f.write(txt)
|
||||||
|
|
||||||
|
#
|
||||||
|
# WSDL-Beschreibung von HISinOne laden
|
||||||
|
#
|
||||||
|
def download_wsdls(self):
|
||||||
|
import os
|
||||||
|
# Falls nötig, Ordner für WSDL-Dateien anlegen
|
||||||
|
if not os.path.isdir(self.wsdl_folder):
|
||||||
|
os.makedirs(self.wsdl_folder)
|
||||||
|
# Falls nötig, WSDL-Dateien laden
|
||||||
|
for service in self.service_names:
|
||||||
|
if not os.path.isfile("{0}/{1}.wsdl".format(self.wsdl_folder, service)):
|
||||||
|
self.__download_wsdl(service)
|
||||||
|
|
||||||
|
#
|
||||||
|
# Proxy für einzelnen Service laden
|
||||||
|
#
|
||||||
|
def get_proxy(self, service):
|
||||||
|
from zeep import Client
|
||||||
|
from zeep.wsse.username import UsernameToken
|
||||||
|
|
||||||
|
if service not in self.proxies.keys():
|
||||||
|
svc = Client("./{0}/{1}.wsdl".format(self.wsdl_folder, service), wsse=UsernameToken(self.user, self.password))
|
||||||
|
self.proxies[service] = svc
|
||||||
|
return self.proxies[service]
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Typ aus WSDL-Definition laden
|
||||||
|
#
|
||||||
|
def get_type(self, service, typename):
|
||||||
|
return self.proxies[service].get_type("ns0:{0}".format(typename))
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Passwort ermitteln (entweder erfragen oder aus Passwort-Save)
|
||||||
|
#
|
||||||
|
def get_password(appname, username):
|
||||||
|
import getpass
|
||||||
|
import keyring
|
||||||
|
|
||||||
|
# Passwort sinnvoll behandeln!
|
||||||
|
if keyring.get_password(appname, username) is None:
|
||||||
|
print("Bitte geben Sie das Passwort für den Benutzer {0} an.".format(username))
|
||||||
|
p = getpass.getpass()
|
||||||
|
keyring.set_password(appname, username, p)
|
||||||
|
|
||||||
|
return keyring.get_password(appname, username)
|
219
incomings_to_stu.py
Normal file
219
incomings_to_stu.py
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
from datetime import datetime
|
||||||
|
from h1wsutils import H1WebServiceUtils
|
||||||
|
|
||||||
|
# Some configuration Data statically added (for examples only!)
|
||||||
|
USER = "mo_webservice_user"
|
||||||
|
PASSWD = "geheim"
|
||||||
|
HOST = "hisinone-7350-s"
|
||||||
|
WSDL_FOLDER = "wsdl"
|
||||||
|
|
||||||
|
|
||||||
|
SERVICES = (
|
||||||
|
"CourseOfStudyService",
|
||||||
|
"PersonService",
|
||||||
|
"PersonAddressService",
|
||||||
|
"StudentService201812",
|
||||||
|
"StudentService",
|
||||||
|
"KeyvalueService"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
h1util = H1WebServiceUtils(USER, PASSWD, HOST, WSDL_FOLDER, SERVICES)
|
||||||
|
h1util.download_wsdls()
|
||||||
|
|
||||||
|
# Preparing proxies
|
||||||
|
cos_svc = h1util.get_proxy("CourseOfStudyService")
|
||||||
|
person_svc = h1util.get_proxy("PersonService")
|
||||||
|
person_address_svc = h1util.get_proxy("PersonAddressService")
|
||||||
|
student2_svc = h1util.get_proxy("StudentService201812")
|
||||||
|
student_svc = h1util.get_proxy("StudentService")
|
||||||
|
value_svc = h1util.get_proxy("KeyvalueService")
|
||||||
|
|
||||||
|
#help(person_svc.service.updatePerson)
|
||||||
|
Person = person_svc.get_type("ns0:PersonExisting")
|
||||||
|
PersonInfo = person_svc.get_type("ns0:PersoninfoDto")
|
||||||
|
StudyBeforeDTO = student_svc.get_type("ns0:StudyBeforeDto")
|
||||||
|
EntranceQualificationDto = student_svc.get_type("ns0:EntranceQualificationDto")
|
||||||
|
Examplan70 = student_svc.get_type("ns0:Examplan70")
|
||||||
|
ExamrelationDto = student_svc.get_type("ns0:ExamrelationDto")
|
||||||
|
Examimport70 = student_svc.get_type("ns0:Examimport70")
|
||||||
|
Person70 = student_svc.get_type("ns0:Person70")
|
||||||
|
|
||||||
|
COUNTRIES = value_svc.service.getAllExtended(valueClass="CountryValue", lang="de")
|
||||||
|
#print(COUNTRIES)
|
||||||
|
TERMTYPES = value_svc.service.getAllExtended(valueClass="TermTypeValue", lang="de")
|
||||||
|
print(TERMTYPES)
|
||||||
|
|
||||||
|
TERM_WS = [t for t in TERMTYPES if t["uniquename"] == "WiSe"][0]
|
||||||
|
TERM_SS = [t for t in TERMTYPES if t["uniquename"] == "SoSe"][0]
|
||||||
|
|
||||||
|
def get_country_id_by_iso2(isocode):
|
||||||
|
now = datetime.date(datetime.now())
|
||||||
|
result = [
|
||||||
|
c["id"]
|
||||||
|
for c in COUNTRIES
|
||||||
|
if c["iso3166_1_alpha_2"] == isocode
|
||||||
|
and (c["validFrom"] < now and now < c["validTo"])
|
||||||
|
]
|
||||||
|
if len(result) == 1:
|
||||||
|
return result[0]
|
||||||
|
else:
|
||||||
|
raise RuntimeError(f"The given isocode {isocode} could not be resolved exactly")
|
||||||
|
|
||||||
|
# Start of example: importing incoming students to hisinone stu
|
||||||
|
|
||||||
|
sample_student = {
|
||||||
|
"course_of_study_uniquename": "97|271|-|-|H|-|-|P|-|9|",
|
||||||
|
"surname": "Mustermann",
|
||||||
|
"firstname": "Max",
|
||||||
|
"private_email": "max.mustermann@kdv.bayern",
|
||||||
|
"gender": "M", # one of D, M, U, W (see KeyvalueService with valueClass=GenderValue)
|
||||||
|
"home_post_address": {
|
||||||
|
"street": "Rainerstraße 28",
|
||||||
|
"postcode": "5020",
|
||||||
|
"city": "Salzburg",
|
||||||
|
"country": "A", # At that Point its country.uniquename from hisinone
|
||||||
|
#-> using the Mapping in COUNTRIES that can be resolved from iso too (its your decision what to use)
|
||||||
|
"addresstag": "home" # not mandatory, but could be set
|
||||||
|
},
|
||||||
|
"birthdate": "2000-02-01",
|
||||||
|
"birthcity": "Dornbirn",
|
||||||
|
"country": "AT", # Here we use the ISO 3166.1 Alpha 2 Code
|
||||||
|
"nationality_country": "AT", # Here we use the ISO 3166.1 Alpha 2 Code
|
||||||
|
"home_university_first_term_year": 2021,
|
||||||
|
"home_university_country_iso": "CZ",
|
||||||
|
"entrance_qualification_country_iso": "CZ",
|
||||||
|
"entrance_qualification_grade": 1.3,
|
||||||
|
"entrance_qualification_date": "12.12.2020",
|
||||||
|
"entrance_qualification_year": "2020"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 1. Fetching the course information from HISinOne
|
||||||
|
tmp_courses = cos_svc.service.findCourseOfStudy201812(uniquename=sample_student["course_of_study_uniquename"])
|
||||||
|
if(len(tmp_courses) != 1):
|
||||||
|
raise Exception("Invalid course_of_study given")
|
||||||
|
|
||||||
|
course_of_study = tmp_courses[0]
|
||||||
|
|
||||||
|
#print(course_of_study)
|
||||||
|
|
||||||
|
# 2. Creating a preliminary student object in HISinOne
|
||||||
|
student_id = student2_svc.service.createCandidateStudent202012(
|
||||||
|
surname=sample_student["surname"],
|
||||||
|
firstname=sample_student["firstname"],
|
||||||
|
gender=sample_student["gender"],
|
||||||
|
courseOfStudyIds=[
|
||||||
|
course_of_study["courseofstudyId"]
|
||||||
|
],
|
||||||
|
generateRegnumber=True,
|
||||||
|
studentstatus="H", # To be checked
|
||||||
|
studystatus="N", # to be confirmed by german moveon usergroup, could be E as well but maybe will be constant for all exchangestudents
|
||||||
|
addresstag="home", # see KeyvalueService with valueClass = 'AddresstagValue'
|
||||||
|
|
||||||
|
# Attention, addresses outside of germany at the are producing an error at the moment
|
||||||
|
# this is a bug in hisinone which will be solved in next version of HISinOne:
|
||||||
|
#
|
||||||
|
# KDV internal tickt: 4538,
|
||||||
|
# Ticket at HIS https://hiszilla.his.de/hiszilla/show_bug.cgi?id=291198
|
||||||
|
#
|
||||||
|
# Workaround is not to use this attribute
|
||||||
|
#postaddress=sample_student["home_post_address"]
|
||||||
|
# and do 2.2. instead
|
||||||
|
)
|
||||||
|
|
||||||
|
print(student_id)
|
||||||
|
|
||||||
|
# 2.1. fetching missing IDs
|
||||||
|
student_detail = student2_svc.service.readStudentByStudentId(studentId=student_id)
|
||||||
|
|
||||||
|
person_id = student_detail["personId"]
|
||||||
|
registrationnumber = student_detail["registrationnumber"]
|
||||||
|
print(f"person_id={person_id}, registrationnumber={registrationnumber}")
|
||||||
|
|
||||||
|
# 2.2. Adding post address within a separate call: (Workaround from https://hiszilla.his.de/hiszilla/show_bug.cgi?id=291198)
|
||||||
|
address_id = person_address_svc.service.createPostaddress(
|
||||||
|
personId=person_id,
|
||||||
|
notificationCategory="STU", # Use STU for primary and STUALL for all additional ones in case of incomings
|
||||||
|
postaddress=sample_student["home_post_address"]
|
||||||
|
)
|
||||||
|
print(f"AddressID: {address_id}")
|
||||||
|
|
||||||
|
# 2.3. Adding Information about date and place of birth, nationality and so on
|
||||||
|
# PersonService.readPerson201912 -> https://hisinone-7350-s.kdv-fh-bayern.de/qisserver/services2/PersonService?wsdl#op.id28
|
||||||
|
# PersonService.savePerson -> https://hisinone-7350-s.kdv-fh-bayern.de/qisserver/services2/PersonService?wsdl#op.id33
|
||||||
|
person = person_svc.service.readPerson(id=person_id)
|
||||||
|
print(person)
|
||||||
|
|
||||||
|
person.allfirstnames=person.firstname
|
||||||
|
#person.academicdegree=... if needed
|
||||||
|
person.dateofbirth=sample_student["birthdate"]
|
||||||
|
person.gender=sample_student["gender"]
|
||||||
|
person.birthcity=sample_student["birthcity"]
|
||||||
|
person.country=sample_student["country"]
|
||||||
|
|
||||||
|
if person.personinfo is None:
|
||||||
|
pinfo = PersonInfo(familyStatusId=1, hasDoneService=False, nationalityId=get_country_id_by_iso2(sample_student["nationality_country"]))
|
||||||
|
#pinfo.nationality=staat
|
||||||
|
person.personinfo = pinfo
|
||||||
|
else:
|
||||||
|
#person.personinfo.nationality=staat
|
||||||
|
person.personinfo.familyStatusId=1
|
||||||
|
person.personinfo.hasDoneService=False
|
||||||
|
person.personinfo.nationalityId=get_country_id_by_iso2(sample_student["nationality_country"])
|
||||||
|
|
||||||
|
person_svc.service.updatePerson(person)
|
||||||
|
|
||||||
|
# 2.4. Adding private email address
|
||||||
|
eaddress_id = person_address_svc.service.createEmail(
|
||||||
|
personId=person_id,
|
||||||
|
notificationCategory="STUALL",
|
||||||
|
email={
|
||||||
|
"emailValue": sample_student["private_email"],
|
||||||
|
"eaddresstype": "email",
|
||||||
|
"addresstag": "privat"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
print(eaddress_id)
|
||||||
|
|
||||||
|
# 2.5. Add information about the university (just country) the exchange student ist coming from
|
||||||
|
# for more options see definition at https://hisinone-7350-s.kdv-fh-bayern.de/qisserver/services2/StudentService?wsdl#op.id20
|
||||||
|
study_before = StudyBeforeDTO(
|
||||||
|
firstTermYear=datetime.now().year,
|
||||||
|
firstTermTermTypeValueId=TERM_WS["id"], # Set it by real term type
|
||||||
|
ownuniversityTermYear=sample_student["home_university_first_term_year"],
|
||||||
|
countryId=get_country_id_by_iso2(sample_student["home_university_country_iso"])
|
||||||
|
)
|
||||||
|
student_svc.service.saveStudyBeforeForPerson(studyBefore=study_before, personId=person_id)
|
||||||
|
|
||||||
|
# 2.6. adding some entrance qualification information
|
||||||
|
# (in Germany its called "Hochschulzugangsberechtigung" or "HZB")
|
||||||
|
ENTRANCE_QUALIFICATION_TYPES = value_svc.service.getAllExtended(valueClass="EntranceQualificationTypeValue", lang="de")
|
||||||
|
#print(ENTRANCE_QUALIFICATION_TYPES)
|
||||||
|
eq_foreign_country = [
|
||||||
|
eq
|
||||||
|
for eq in ENTRANCE_QUALIFICATION_TYPES
|
||||||
|
if eq["astat"] == '79' # use 79 if there is no information about this in MoveOn, if there is some, use correct information
|
||||||
|
][0]
|
||||||
|
|
||||||
|
exam_import = Examimport70(
|
||||||
|
countryId=get_country_id_by_iso2(sample_student["entrance_qualification_country_iso"]),
|
||||||
|
foreignGrade=sample_student["entrance_qualification_grade"]
|
||||||
|
)
|
||||||
|
|
||||||
|
examrelation = ExamrelationDto(workstatusId=1)
|
||||||
|
examplan = Examplan70(
|
||||||
|
dateOfWork=sample_student["entrance_qualification_date"],
|
||||||
|
year=sample_student["entrance_qualification_year"],
|
||||||
|
personId=person_id,
|
||||||
|
unitId=1, # Can be used als constant for entrance_qualifications
|
||||||
|
defaultExamrelation=examrelation,
|
||||||
|
examimport=exam_import
|
||||||
|
)
|
||||||
|
entrance_qualification = EntranceQualificationDto(
|
||||||
|
entranceQualificationTypeId=eq_foreign_country["id"],
|
||||||
|
examplan=examplan
|
||||||
|
)
|
||||||
|
|
||||||
|
# Webservicerole needs additional right cm.app.entrancequalification.EDIT_ENTRANCEQUALIFICATION to perform this operation
|
||||||
|
student_svc.service.saveHZB(eq=entrance_qualification, ausinland=1)
|
106
outgoings_from_stu.py
Normal file
106
outgoings_from_stu.py
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
import re
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
from h1wsutils import H1WebServiceUtils
|
||||||
|
|
||||||
|
# Some configuration Data statically added (for examples only!)
|
||||||
|
USER = "mo_webservice_user"
|
||||||
|
PASSWD = "geheim"
|
||||||
|
HOST = "hisinone-7350-s"
|
||||||
|
WSDL_FOLDER = "wsdl"
|
||||||
|
|
||||||
|
|
||||||
|
SERVICES = (
|
||||||
|
"StayAbroadService",
|
||||||
|
"PersonService",
|
||||||
|
"PersonAddressService",
|
||||||
|
"StudentService201812",
|
||||||
|
"StudentService",
|
||||||
|
"KeyvalueService"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
h1util = H1WebServiceUtils(USER, PASSWD, HOST, WSDL_FOLDER, SERVICES)
|
||||||
|
h1util.download_wsdls()
|
||||||
|
|
||||||
|
# Preparing proxies
|
||||||
|
abroad_svc = h1util.get_proxy("StayAbroadService")
|
||||||
|
person_svc = h1util.get_proxy("PersonService")
|
||||||
|
person_address_svc = h1util.get_proxy("PersonAddressService")
|
||||||
|
student2_svc = h1util.get_proxy("StudentService201812")
|
||||||
|
student_svc = h1util.get_proxy("StudentService")
|
||||||
|
value_svc = h1util.get_proxy("KeyvalueService")
|
||||||
|
|
||||||
|
# Input data for example, has to be replaced with values from real system
|
||||||
|
# in move on environment
|
||||||
|
#USERNAME_FROM_SHIBBOLETH = "albrecht4@beispielhochschule.de"
|
||||||
|
#FOREIGN_COUNTRY_ISO = "AU" # iso3166_1_alpha_2
|
||||||
|
|
||||||
|
# Alternative ones for testing
|
||||||
|
USERNAME_FROM_SHIBBOLETH = "bauer2@beispielhochschule.de"
|
||||||
|
FOREIGN_COUNTRY_ISO = "DK" # iso3166_1_alpha_2
|
||||||
|
|
||||||
|
# Some simple sample for validating and handling the fully qualified username from Shibboleth
|
||||||
|
UNIVERSITY_SUFFIX_SHIBBOLETH_REGEX = "^(?P<username>[a-zA-Z0-9]+)@beispielhochschule.de$"
|
||||||
|
|
||||||
|
matches = re.match(UNIVERSITY_SUFFIX_SHIBBOLETH_REGEX, USERNAME_FROM_SHIBBOLETH)
|
||||||
|
if not matches:
|
||||||
|
print("Invalid User, not allowed to be used in this context")
|
||||||
|
exit(code=1)
|
||||||
|
|
||||||
|
username = matches["username"]
|
||||||
|
|
||||||
|
# Finding person by username
|
||||||
|
person_ids = person_svc.service.findPerson(
|
||||||
|
username=username,
|
||||||
|
studyTermYear=datetime.now().year # To focus the problem that usernames can be reused for new persons after some time of inactivity
|
||||||
|
)
|
||||||
|
if len(person_ids) != 1:
|
||||||
|
print(f"ERROR: user assignment invalid or not existent, please check it in HISinOne ({len(person_ids)})")
|
||||||
|
exit(code=2)
|
||||||
|
|
||||||
|
person_id = person_ids[0]
|
||||||
|
|
||||||
|
# Fetching information about the user
|
||||||
|
student_info = student2_svc.service.readStudentByPersonId(
|
||||||
|
person_id,
|
||||||
|
withDegreePrograms=True,
|
||||||
|
withAddresses=True
|
||||||
|
)
|
||||||
|
print(student_info)
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# Then continue to work in MoveOn
|
||||||
|
#
|
||||||
|
#
|
||||||
|
|
||||||
|
# After student is back from exchange semester, write back statistics data to HISinOne
|
||||||
|
|
||||||
|
# Those calls you can use to see what values are available within HISinOne
|
||||||
|
#STAY_ABROAD_TYPES = value_svc.service.getAll(valueClass="StayAbroadTypeValue", lang="de")
|
||||||
|
#print(STAY_ABROAD_TYPES)
|
||||||
|
|
||||||
|
#MOBILITY_PROGRAMS = value_svc.service.getAll(valueClass="MobilityProgramValue", lang="de")
|
||||||
|
#print(MOBILITY_PROGRAMS)
|
||||||
|
|
||||||
|
# In our sample case, the person did studies on a foreign university
|
||||||
|
STAY_ABROAD_TYPE = "01"
|
||||||
|
RESEARCH_FACILITY_TYPE="UNI"
|
||||||
|
# and he did this via ERASMUS which means an EU-Program
|
||||||
|
MOBILITY_PROGRAM = "01"
|
||||||
|
|
||||||
|
# Note:
|
||||||
|
# calling webservice user needs the right cs.psv.stayabroad.EDIT_STAYABROAD within hisinone!
|
||||||
|
result = abroad_svc.service.createStayAbroad(
|
||||||
|
personId=person_id,
|
||||||
|
country=FOREIGN_COUNTRY_ISO, # iso3166_1_alpha_2 of the country the student has been to
|
||||||
|
numberOfMonth=7,
|
||||||
|
startdate="2022-01-15",
|
||||||
|
enddate="2022-07-15",
|
||||||
|
stayAbroadTypeValue=STAY_ABROAD_TYPE,
|
||||||
|
mobilityProgramValue=MOBILITY_PROGRAM,
|
||||||
|
researchFacilityName="Sampleuniversity of Denmark",
|
||||||
|
researchFacilityTypeValue=RESEARCH_FACILITY_TYPE
|
||||||
|
)
|
Loading…
Reference in New Issue
Block a user