def test_svg(self): def mock_open(name, mode): self.assertEqual(mode, "rb") return stream class MockOle: def __init__(ole, file): self.assertIs(file, stream) super().__init__() def openstream(ole, name): self.assertEqual(name, "FileHeader") return stream class mock_os: def stat(fileno): return None stream = BytesIO() stream.fileno = lambda: stream sch = ( b"\x00", b"|RECORD=31|FONTIDCOUNT=1|SIZE1=10|FONTNAME1=Times New Roman" b"|SYSTEMFONT=1" b"|AREACOLOR=16317695|BORDERON=T|CUSTOMX=|CUSTOMY=" b"|DISPLAY_UNIT=4|HOTSPOTGRIDON=T|HOTSPOTGRIDSIZE=" b"|SNAPGRIDON=T|SNAPGRIDSIZE=|VISIBLEGRIDON=T" b"|VISIBLEGRIDSIZE=10|ISBOC=T|SHEETNUMBERSPACESIZE=4" b"|USEMBCS=T\x00", b"|RECORD=15|LOCATION.X=100|LOCATION.Y=200|XSIZE=40|YSIZE=30" b"|COLOR=7846673|AREACOLOR=3381725|ISSOLID=T|OWNERPARTID=-1" b"|UNIQUEID=\x00", ) for list in sch: stream.write(len(list).to_bytes(4, "little")) stream.write(list) stream.seek(0) output = StringIO() with patch("altium.open", mock_open), \ patch("altium.OleFileIO", MockOle), \ patch("altium.os", mock_os), \ redirect_stdout(output): altium.convert("dummy.SchDoc", svg.Renderer) output = XML(output.getvalue()) SVG = "{http://www.w3.org/2000/svg}" self.assertEqual(output.tag, SVG + "svg") for [dimension, expected] in (("width", 11.506), ("height", 7.606)): with self.subTest(dimension): value = output.get(dimension) self.assertTrue(value.endswith("in")) self.assertAlmostEqual(float(value[:-2]), expected, 3) for [name, value] in ( ("viewBox", "-0.3,-760.3 1150.6,760.6"), ("stroke-width", "1"), ): with self.subTest(name): self.assertEqual(output.get(name), value) [style, defs, border, sheet] = output self.assertEqual(style.tag, SVG + "style") self.assertEqual(defs.tag, SVG + "defs") self.assertEqual(border.tag, SVG + "g") self.assertCountEqual(border.items(), ( ("transform", "translate(0, -760)"), )) self.assertEqual(sheet.tag, SVG + "rect") self.assertCountEqual(sheet.items(), ( ("transform", "translate(100, -200)"), ("width", "40"), ("height", "30"), ("stroke-width", "0.6"), ("class", "solid"), ("style", "fill: #DD9933; stroke: #11BB77"), ))
def request(self, method, url, body=None, headers=None, credentials=None, num_redirects=0): if url in self.perm_redirects: url = self.perm_redirects[url] method = method.upper() if headers is None: headers = {} headers.setdefault('Accept', 'application/json') headers['User-Agent'] = self.user_agent cached_resp = None if method in ('GET', 'HEAD'): cached_resp = self.cache.get(url) if cached_resp is not None: etag = cached_resp[1].get('etag') if etag: headers['If-None-Match'] = etag if body is None: headers.setdefault('Content-Length', '0') else: if isinstance(body, str): body = body.encode('utf-8') elif isinstance(body, (dict, list, tuple)): body = json.encode(body).encode('utf-8') headers.setdefault('Content-Type', 'application/json') if isinstance(body, bytes): headers.setdefault('Content-Length', str(len(body))) else: headers['Transfer-Encoding'] = 'chunked' authorization = basic_auth(credentials) if authorization: headers['Authorization'] = authorization path_query = urlunsplit(('', '') + urlsplit(url)[2:4] + ('',)) conn = self._get_connection(url) def _try_request_with_retries(retries): while True: try: return _try_request() except socket.error as e: ecode = e.args[0] if ecode not in self.retryable_errors: raise try: delay = next(retries) except StopIteration: # No more retries, raise last socket error. raise e time.sleep(delay) conn.close() def _try_request(): try: conn.putrequest(method, path_query, skip_accept_encoding=True) for header in headers: conn.putheader(header, headers[header]) conn.endheaders() if body is not None: if isinstance(body, str): conn.send(body.encode('utf-8')) elif isinstance(body, bytes): conn.send(body) else: # assume a file-like object and send in chunks while True: chunk = body.read(CHUNK_SIZE) encoding = getattr(body, 'encoding') or 'utf-8' if not chunk: break conn.send(b''.join( map(lambda item: item.encode(encoding), ['%x\r\n' % len(chunk), chunk, '\r\n']) )) conn.send(b'0\r\n\r\n') return conn.getresponse() except BadStatusLine as e: # httplib raises a BadStatusLine when it cannot read the status # line saying, "Presumably, the server closed the connection # before sending a valid response." # Raise as ECONNRESET to simplify retry logic. if e.line == '' or e.line == "''": raise socket.error(errno.ECONNRESET) else: raise resp = _try_request_with_retries(iter(self.retry_delays)) status = resp.status # Handle conditional response if status == 304 and method in ('GET', 'HEAD'): resp.read() self._return_connection(url, conn) status, msg, data = cached_resp if data is not None: data = StringIO(data) return status, msg, data elif cached_resp: del self.cache[url] # Handle redirects if status == 303 or \ method in ('GET', 'HEAD') and status in (301, 302, 307): resp.read() self._return_connection(url, conn) if num_redirects > self.max_redirects: raise RedirectLimit('Redirection limit exceeded') location = resp.getheader('location') if status == 301: self.perm_redirects[url] = location elif status == 303: method = 'GET' return self.request(method, location, body, headers, num_redirects=num_redirects + 1) data = None streamed = False # Read the full response for empty responses so that the connection is # in good state for the next request if method == 'HEAD' or resp.headers.get('content-length') == '0' or \ status < 200 or status in (204, 304): resp.read() self._return_connection(url, conn) # Buffer small non-JSON response bodies elif int(resp.headers.get('content-length', sys.maxsize)) < CHUNK_SIZE: data = resp.read().decode('utf-8') self._return_connection(url, conn) # For large or chunked response bodies, do not buffer the full body, # and instead return a minimal file-like object else: data = ResponseBody(resp, lambda: self._return_connection(url, conn)) streamed = True # Handle errors if status >= 400: ctype = resp.headers.get('content-type') if data is not None and 'application/json' in ctype: data = json.decode(data) error = data.get('error'), data.get('reason') elif method != 'HEAD': error = resp.read() self._return_connection(url, conn) else: error = '' if status == 401: raise Unauthorized(error) elif status == 404: raise ResourceNotFound(error) elif status == 409: raise ResourceConflict(error) elif status == 412: raise PreconditionFailed(error) else: raise ServerError((status, error)) # Store cachable responses if not streamed and method == 'GET' and 'etag' in resp.msg: self.cache[url] = (status, resp.msg, data) if len(self.cache) > CACHE_SIZE[1]: self._clean_cache() if not streamed and data is not None: data = StringIO(data) return status, resp.msg, data
def download_subst(date): settings = Settings.objects.all()[0] params = {'gpid': settings.gpid, 'gsh': settings.gsh, 'action': 'switch', 'date': date, '_LJSL': '2052'} serverResponse = url_request('https://lo3gdynia.edupage.org/gcall', {'Cookie': 'PHPSESSID=' + settings.phpsessid}, params).read().decode('UTF-8') log.debug(serverResponse) jsdb_start = serverResponse.index('ttdb.fill({') + 10 jsdb = serverResponse[jsdb_start: serverResponse.find('"}}});', jsdb_start) + 4] jsdb = StringIO(jsdb) jsdb = json.load(jsdb) teachers = jsdb.get('teachers', {}) classes = jsdb.get('classes', {}) subjects = jsdb.get('subjects', {}) classrooms = jsdb.get('classrooms', {}) periods = jsdb.get('periods', {}) subType = jsdb.get('substitution_types', {}) breaks = jsdb.get('breaks', {}) for i in ['teachers', 'classes', 'subjects', 'classrooms', 'periods', 'breaks']: exec(i + '["None"] = ""') # Pobieranie czerwonej notatki try: note_start = serverResponse.index('.innerHTML="', serverResponse.index('.innerHTML="') + 1) + len( '.indexHTML="') note = serverResponse[note_start: jsdb_start - 10] note = note[0: note.index('";gi')] note = note.replace('\\n', '') note = note.replace('\\"', '"') note = note.replace('<br /> <br />', '<br />') except ValueError: note = "" subst = serverResponse[serverResponse.find('dt.DataSource(') + 14:serverResponse.find(');var dt = new')] subst = StringIO(subst) subst = json.load(subst) # zastepstwa = [] zastepstwa = {} for zastepstwo in subst: status = {'new_przedmiot': [], 'new_nauczyciel': [], 'old_nauczyciel': [], 'old_przedmiot': [], 'new_sala': [], 'old_sala': [], 'old_klasa': [], 'new_klasa': [], 'przedmiot': [], 'lekcja': [], 'notka': '', 'klasa': [], } for key in zastepstwo: if key == 'cancelled': status['anulowano'] = zastepstwo[key] elif key == 'note': status['notka'] = zastepstwo[key] elif key == 'substitution_typeid': status['typ'] = subType.get(zastepstwo[key], "") elif key == 'period': if type(periods) is list: status['lekcja'] = periods[int(zastepstwo[key])] elif type(periods) is str: status['lekcja'] = zastepstwo[key] else: status['lekcja'] = periods.get(zastepstwo[key], 'None') elif key == 'subjectid': status['przedmiot'] = [subjects[str(zastepstwo[key])]] elif key == 'subjectids': status['przedmiot'] = [subjects[str(s)] for s in zastepstwo[key]] elif key == 'teacherid': status['nauczyciel'] = [teachers[str(zastepstwo[key])]] elif key == 'teacherids': status['nauczyciel'] = [teachers[str(s)] for s in zastepstwo[key]] elif key == 'classid': status['klasa'] = [classes[str(zastepstwo[key])]] elif key == 'classids': status['klasa'] = [classes[str(s)] for s in zastepstwo[key]] elif key == 'classroomid': status['sala'] = [classrooms[str(zastepstwo[key])]] elif key == 'classroomids': status['sala'] = [classrooms[str(s)] for s in zastepstwo[key]] elif key == 'changes': for z in zastepstwo[key]: if z['column'] == 'teacherid' or z['column'] == 'teacherids': status['old_nauczyciel'].append(teachers[str(z.get('old'))]) n = teachers.get(str(z.get('new'))) if n not in [None, '']: status['new_nauczyciel'].append(n) elif z['column'] == 'classroomid' or z['column'] == 'classroomids': status['old_sala'].append(classrooms[str(z.get('old'))]) s = classrooms.get(str(z.get('new'))) if s not in [None, '']: status['new_sala'].append(s) elif z['column'] == 'subjectid' or z['column'] == 'subjectids': status['old_przedmiot'].append(subjects[str(z.get('old'))]) p = subjects.get(str(z.get('new'))) if p not in [None, '']: status['new_przedmiot'].append(p) elif z['column'] == 'classid' or z['column'] == 'classids': status['old_klasa'].append(classes[str(z.get('old'))]) c = classes.get(str(z.get('new'))) if c is not None: status['new_klasa'].append(c) log.debug('Klasa: ' + str(status.get('klasa', 'None')) + '\t' + 'Lekcja: ' + str( status.get('lekcja', 'None')) + '\n' + 'Nauczyciel: ' + str(status['nauczyciel']) + ' -> ' + str(status['new_nauczyciel']) + '\n' + 'Przedmiot :' + str(status.get('przedmiot', 'None')) + ' -> ' + str(status['new_przedmiot']) + '\n' + 'Sala :' + str(status['sala']) + ' -> ' + str(status['new_sala']) + '\n' + 'Typ zastępstwa: ' + str(status.get('typ', 'None')) + '\n\n') if len(status['klasa']) == 0: status['klasa'].append({'name': ''}) status['przerwa'] = breaks.get(zastepstwo.get('break')) status['klasa'] = sorted(status['klasa'], key=lambda s: s['name']) k = "" for s in status['klasa']: for l in status['old_klasa']: if s['name'] == l['name']: k += '<s>' + s['name'] + '</s>, ' break else: k += s['name'] + ', ' k = k[0:-2] status['displayname'] = k k = "" for s in status['klasa']: k += s['name'] + ', ' k = k[0:-2] try: zastepstwa[k].append(status) except KeyError: zastepstwa[k] = [status] posortowane = dict(sorted(zastepstwa.items())) return {'dane': posortowane, 'notka': note}