1 module webuntis; 2 3 import std.stdio; 4 import std.array; 5 import std.algorithm; 6 import std.exception; 7 import std.net.curl; 8 import std.json; 9 import std.format; 10 import std.conv; 11 import std.string; 12 import std.process; 13 import std.datetime; 14 15 import objects; 16 import timetable; 17 18 pragma(lib,"curl"); 19 20 class Session 21 { 22 private string username; 23 private string password; 24 private string sessionID; 25 private string url; 26 private string client; 27 28 private Teacher[] teacherCache; 29 private Subject[] subjectCache; 30 31 this(SessionConfiguration conf) 32 { 33 this.username = conf.username; 34 this.password = conf.password; 35 this.url = format("https://%s.webuntis.com/WebUntis/jsonrpc.do?school=%s",conf.server,conf.school); 36 this.client = conf.client; 37 } 38 public void login() 39 { 40 auto params = format("{\"user\":\"%s\",\"password\":\"%s\",\"client\":\"%s\"}", 41 username,password,client); 42 auto req = Request(to!string(Clock.currTime().toUnixTime()),"authenticate",params); 43 auto response = sendRequest(req.toJSON()); 44 try 45 { 46 sessionID = response["result"]["sessionId"].str; 47 } 48 catch (JSONException ex) 49 { 50 throw new WebUntisException(format("Login Error: %s",response["error"]["message"].str)); 51 } 52 } 53 public void logout() 54 { 55 auto params = "{}"; 56 auto req = Request(to!string(Clock.currTime().toUnixTime()),"logout",params); 57 auto response = sendRequest(req.toJSON()); 58 try 59 { 60 cast(void) response["result"].isNull(); 61 } 62 catch (JSONException ex) 63 { 64 throw new WebUntisException(format("Login Error: %s",response["error"]["message"].str)); 65 } 66 } 67 68 public SchoolClass[] getClasses() 69 { 70 auto params = "{}"; 71 auto req = Request(to!string(Clock.currTime().toUnixTime()),"getKlassen",params); 72 auto response = sendRequest(req.toJSON()); 73 SchoolClass[] classes; 74 try 75 { 76 foreach(class_;response["result"].array) 77 { 78 auto newClass = SchoolClass( 79 to!int(class_["id"].integer), 80 class_["name"].str, 81 class_["longName"].str, 82 class_["active"].type == JSON_TYPE.TRUE 83 ); 84 classes ~= newClass; 85 } 86 } 87 catch (JSONException ex) 88 { 89 throw new WebUntisException(format("Classes Error: %s",response["error"]["message"].str)); 90 } 91 return classes; 92 } 93 94 public Timetable getTimetable(int startDate,int endDate,int class_) 95 { 96 teacherCache = getTeachers(); 97 subjectCache = getSubjects(); 98 auto params = format("{\"id\":%s, 99 \"type\":1, 100 \"startDate\":\"%s\", 101 \"endDate\":\"%s\"}",class_,startDate,endDate); 102 103 auto req = Request(to!string(Clock.currTime().toUnixTime()),"getTimetable",params); 104 auto response = sendRequest(req.toJSON()); 105 Timetable t; 106 ClassUnit[] units; 107 try 108 { 109 t = Timetable(); 110 foreach(rawUnit;response["result"].array) 111 { 112 auto unit = ClassUnit( 113 subjectCache.filter!(x => x.id == rawUnit["su"][0]["id"].integer).front, 114 teacherCache.filter!(x => x.id == rawUnit["te"][0]["id"].integer).front, 115 to!int(rawUnit["startTime"].integer), 116 to!int(rawUnit["date"].integer) 117 ); 118 units ~= unit; 119 } 120 } 121 catch (JSONException ex) 122 { 123 throw new WebUntisException(format("Timetable Error: %s",response["error"]["message"].str)); 124 } 125 126 bool startTimeComp(ClassUnit x, ClassUnit y) @safe pure nothrow { return x.startTime > y.startTime; } 127 for (int date = startDate; date < endDate; date++) 128 { 129 auto dayunits = array(units.filter!(x => x.date == date)); 130 SchoolDay day; 131 day.units ~= dayunits; 132 t.days ~= day; 133 } 134 return t; 135 } 136 137 public Teacher[] getTeachers() 138 { 139 auto params = "{}"; 140 auto req = Request(to!string(Clock.currTime().toUnixTime()),"getTeachers",params); 141 auto response = sendRequest(req.toJSON()); 142 Teacher[] teachers; 143 try 144 { 145 foreach(teacher;response["result"].array) 146 { 147 //ID,Name,foreName,longName,active 148 auto newTeacher = Teacher( 149 to!int(teacher["id"].integer), 150 teacher["name"].str, 151 teacher["foreName"].str, 152 teacher["longName"].str, 153 teacher["active"].type == JSON_TYPE.TRUE 154 ); 155 teachers ~= newTeacher; 156 } 157 } 158 catch (JSONException ex) 159 { 160 throw new WebUntisException(format("Teachers Error: %s",response["error"]["message"].str)); 161 } 162 return teachers; 163 } 164 165 public Subject[] getSubjects() 166 { 167 auto params = "{}"; 168 auto req = Request(to!string(Clock.currTime().toUnixTime()),"getSubjects",params); 169 auto response = sendRequest(req.toJSON()); 170 Subject[] subjects; 171 try 172 { 173 foreach(subject;response["result"].array) 174 { 175 //ID,Name,longName,alternateName,active 176 auto newSubject = Subject( 177 to!int(subject["id"].integer), 178 subject["name"].str, 179 subject["longName"].str, 180 subject["alternateName"].str, 181 subject["active"].type == JSON_TYPE.TRUE 182 ); 183 subjects ~= newSubject; 184 } 185 } 186 catch (JSONException ex) 187 { 188 throw new WebUntisException(format("Subjects Error: %s",response["error"]["message"].str)); 189 } 190 return subjects; 191 } 192 193 public Room[] getRooms() 194 { 195 auto params = "{}"; 196 auto req = Request(to!string(Clock.currTime().toUnixTime()),"getRooms",params); 197 auto response = sendRequest(req.toJSON()); 198 Room[] rooms; 199 try 200 { 201 foreach(room;response["result"].array) 202 { 203 //ID,Name,longName,active 204 auto newRoom = Room( 205 to!int(room["id"].integer), 206 room["name"].str, 207 room["longName"].str, 208 room["active"].type == JSON_TYPE.TRUE 209 ); 210 rooms ~= newRoom; 211 } 212 } 213 catch (JSONException ex) 214 { 215 throw new WebUntisException(format("Subjects Error: %s",response["error"]["message"].str)); 216 } 217 return rooms; 218 } 219 220 private JSONValue sendRequest(JSONValue data) 221 { 222 string reqbody = data.toString(); 223 auto client = HTTP(); 224 client.addRequestHeader("Content-Type","application/json"); 225 client.postData = reqbody; 226 227 if (sessionID != null) 228 { 229 client.addRequestHeader("Cookie",format("JSESSIONID=%s",sessionID)); 230 } 231 232 // Response handling setup 233 string response = post(url,reqbody,client).dup; 234 // Parsing and returning the JSON 235 return parseJSON(response); 236 } 237 } 238 239 240 struct Request 241 { 242 string id; 243 string method; 244 string params; 245 string jsonrpc = "2.0"; 246 JSONValue toJSON() 247 { 248 string jsonString = chomp(format( 249 "{ 250 \"id\":\"%s\", 251 \"method\":\"%s\", 252 \"params\": %s , 253 \"jsonrpc\":\"%s\" 254 }", 255 id, 256 method, 257 params, 258 jsonrpc 259 )); 260 JSONValue value = parseJSON(jsonString); 261 return value; 262 } 263 } 264 265 struct SessionConfiguration 266 { 267 string username; 268 string password; 269 string server; 270 string school; 271 string client; 272 } 273 274 /** 275 Exception thrown on Webuntis Errors 276 */ 277 class WebUntisException : Exception 278 { 279 this(string msg) 280 { 281 super(msg); 282 } 283 } 284 285 // All Testing happens HERE 286 unittest 287 { 288 SessionConfiguration sconf = SessionConfiguration( 289 environment["wuuser"], 290 "wrongpassword", 291 environment["wuserver"], 292 environment["wuschool"], 293 "WebUntis API dlang wrapper"); 294 295 Session s = new Session(sconf); 296 297 writeln("Begin of Tests"); 298 writeln("---------------------------------------"); 299 300 writeln("Testing method access before login"); 301 assertThrown!WebUntisException(s.getClasses()); 302 assertThrown!WebUntisException(s.getTeachers()); 303 assertThrown!WebUntisException(s.logout()); 304 writeln("OK"); 305 306 writeln("---------------------------------------"); 307 308 writeln("Testing Login with wrong password"); 309 assertThrown!WebUntisException(s.login()); 310 writeln("OK"); 311 312 writeln("---------------------------------------"); 313 314 // Setting the real password 315 sconf.password = environment["wupassword"]; 316 s = new Session(sconf); 317 318 writeln("Testing Login with right password"); 319 assertNotThrown!WebUntisException(s.login()); 320 writeln("OK"); 321 322 writeln("---------------------------------------"); 323 324 writeln("Testing Teachers"); 325 auto teachers = s.getTeachers(); 326 assert(teachers.length > 0); 327 writef("Found %s teachers\n",teachers.length); 328 writef("For Example: %s\n",teachers[$/2].longName); 329 330 writeln("OK"); 331 332 writeln("---------------------------------------"); 333 334 writeln("Testing Subjects"); 335 auto subjects = s.getSubjects(); 336 assert(subjects.length > 0); 337 writef("Found %s subjects\n",subjects.length); 338 writef("For Example: %s\n",subjects[$/2].longName); 339 340 writeln("OK"); 341 342 writeln("---------------------------------------"); 343 344 writeln("Testing Rooms"); 345 auto rooms = s.getRooms(); 346 assert(subjects.length > 0); 347 writef("Found %s Rooms\n",rooms.length); 348 writef("For Example: %s\n",rooms[$/2].longName); 349 350 writeln("OK"); 351 352 writeln("---------------------------------------"); 353 354 writeln("Testing Classes"); 355 auto classes = s.getClasses(); 356 assert(classes.length > 0); 357 writef("Found %s classes\n",classes.length); 358 writef("For Example %s\n",classes[$/2].name); 359 writeln("OK"); 360 361 writeln("---------------------------------------"); 362 363 writeln("Testing Timetable"); 364 auto table = s.getTimetable(20150925,20150926,37); 365 writeln("OK"); 366 367 writeln("---------------------------------------"); 368 s.logout(); 369 }