def test_second_step_import_encoding_form(self): """Test importing csv data with special chars in header and content""" form = importform.ImportFormSecondStep(self.container, self.layer["request"]) annotations = IAnnotations(self.container) annotation = annotations[importform.ANNOTATION_KEY] = PersistentDict() annotation["has_header"] = True annotation["separator"] = u";" csv = StringIO() lines = [ [u"猫".encode("utf8"), u"èè".encode("utf8"), u"ùù".encode("utf8")], ["", u"kéy1".encode("utf8"), u"Kèy 1".encode("utf8")], [ u"kéy1".encode("utf8"), u"kéy1.1".encode("utf8"), u"猫".encode("utf8") ], ] for line in lines: csv.write(";".join(line) + "\n") csv.seek(0) annotation["source"] = NamedBlobFile( data=csv.read(), contentType=u"text/csv", filename=u"test.csv", ) form.update() exception = None try: render = form.render() except UnicodeDecodeError as e: exception = e self.assertIsNone(exception) self.assertTrue(u"Column {0}".format(u"猫") in render)
def test_second_step_basic_delimiter(self): """Test edge case related to csv delimiter""" form = importform.ImportFormSecondStep(self.container, self.layer["request"]) annotations = IAnnotations(self.container) annotation = annotations[importform.ANNOTATION_KEY] = PersistentDict() annotation["has_header"] = False annotation["separator"] = u"," csv = StringIO() lines = [ ["", "key1", "Key 1"], ["key1", "key1.1", '"Key 1,1"'], ["key1.1", "key1.1.1", '"Key 1.1.1"'], ] for line in lines: csv.write(",".join(line) + "\n") csv.seek(0) annotation["source"] = NamedBlobFile( data=csv.read(), contentType=u"text/csv", filename=u"test.csv", ) exception = None try: form.update() except Exception as e: exception = e self.assertIsNone(exception)
def test_1k_fields(self): from cStringIO import StringIO from bson.objectid import ObjectId import random COLUMNS = 1000 LAST_COL_IDX = COLUMNS - 1 csv = StringIO() for col in xrange(COLUMNS): csv.write(('COLUMN%s' % col)) csv.write('\t') if col != LAST_COL_IDX else csv.write('\n') _from = 10 * 1000 * 1000 * 1000 _to = _from * 2 for row in (1, 2): for col in xrange(COLUMNS): last = col == LAST_COL_IDX if col < COLUMNS / 2: csv.write('%s' % random.randint(_from, _to)) else: csv.write(unicode(ObjectId())) if not last: csv.write('\t') else: csv.write('\n') csv.seek(0) name = 'Test1000Columns' data_loader = CsvDataLoader(csv, sep=CsvDataLoader.TAB) dataset = self.user.account.datasets.add_dataset( self.user, name, data_loader) data_cls = dataset.get_data_class() self.assertEqual(data_cls.objects.count(), 2)
def test_second_step_import_single_column(self): """Test importing csv data""" form = importform.ImportFormSecondStep(self.container, self.layer["request"]) annotations = IAnnotations(self.container) annotation = annotations[importform.ANNOTATION_KEY] = PersistentDict() annotation["has_header"] = False annotation["separator"] = u";" csv = StringIO() lines = [ ["", "key1", "Key 1"], ["", "key2", "Key 2"], ] for line in lines: csv.write(";".join(line) + "\n") csv.seek(0) annotation["source"] = NamedBlobFile( data=csv.read(), contentType=u"text/csv", filename=u"test.csv", ) data = { "column_0": None, "column_1": "identifier", "column_2": None, "decimal_import": False, "allow_empty": False, } form._import(data) self.assertEqual(2, len(self.container)) self.assertEqual(["key1", "key2"], sorted( [e.identifier for e in self.container.values()])) self.assertEqual(["key1", "key2"], sorted([e.title for e in self.container.values()]))
def test_second_step_basic_encoding(self): """Ensure that form can be displayed even with special characters""" form = importform.ImportFormSecondStep(self.container, self.layer["request"]) annotations = IAnnotations(self.container) annotation = annotations[importform.ANNOTATION_KEY] = PersistentDict() annotation["has_header"] = False annotation["separator"] = u";" csv = StringIO() lines = [ ["null", "key1", "key1.1", "Key 1.1", "informations"], [ "null", "", u"key1 éà$€".encode("utf8"), u"Key 1 éà$€".encode("utf8"), u"informations éà$€".encode("utf8"), ], ] for line in lines: csv.write(";".join(line) + "\n") csv.seek(0) annotation["source"] = NamedBlobFile( data=csv.read(), contentType=u"text/csv", filename=u"test.csv", ) exception = None try: form.update() except UnicodeDecodeError as e: exception = e self.assertIsNone(exception)
def test_first_step_validate_line_columns_nok(self): """Ensure that every lines have the same number of columns""" request = self.layer["request"] csv = StringIO() lines = [ ["", "key1", "Key 1"], ["key1", "key1.1"], ["key1.1", "key1.1.1", "Key 1.1.1", "foo"], ] for line in lines: csv.write(";".join(line) + "\n") csv.seek(0) source = FileUpload( type( "obj", (object, ), { "file": csv, "filename": "foo.csv", "headers": "text/csv" }, )()) request.form = { "form.buttons.continue": u"continuer", "form.widgets.separator": [u";"], "form.widgets.separator-empty-marker": u"1", "form.widgets.source": source, "form.widgets.has_header": u"False", } form = importform.ImportFormFirstStep(self.container, request) form.update() data, errors = form.extractData() self.assertEqual(1, len(errors)) self.assertTrue("Lines 2, 3" in translate(errors[0].error.message))
def test_first_step_validate_csv_encoding_nok(self): """Ensure that we can decode csv file""" request = self.layer["request"] csv = StringIO() lines = [ [u"猫", u"èè", u"ùù"], ["", "key1", "Key 1"], [u"猫", u"ààà", u"ééé"], ] for line in lines: csv.write(";".join(line).encode("utf-16") + "\n") csv.seek(0) source = FileUpload( type( "obj", (object, ), { "file": csv, "filename": "foo.csv", "headers": "text/csv" }, )()) request.form = { "form.buttons.continue": u"continuer", "form.widgets.separator": [u";"], "form.widgets.separator-empty-marker": u"1", "form.widgets.source": source, "form.widgets.has_header": u"True", } form = importform.ImportFormFirstStep(self.container, request) form.update() data, errors = form.extractData() self.assertEqual(1, len(errors)) self.assertEqual("File encoding is not utf8", errors[0].error.message)
def test_second_step_optional_columns_data_ok(self): """Test validation of optional columns data""" request = self.layer["request"] request.form = { "form.buttons.import": u"Importer", "form.widgets.column_0": u"parent_identifier", "form.widgets.column_1": u"identifier", "form.widgets.column_2": u"title", "form.widgets.column_3": u"informations", "form.widgets.decimal_import": u"False", "form.widgets.allow_empty": u"False", } annotations = IAnnotations(self.container) annotation = annotations[importform.ANNOTATION_KEY] = PersistentDict() annotation["has_header"] = False annotation["separator"] = u";" csv = StringIO() lines = [ ["", "key1", "Key 1", "infos"], ["key1", "key1.1", "Key 1.1", ""], ["key1.1", "key1.1.1", "Key 1.1.1", ""], ] for line in lines: csv.write(";".join(line) + "\n") csv.seek(0) annotation["source"] = NamedBlobFile( data=csv.read(), contentType=u"text/csv", filename=u"test.csv", ) form = importform.ImportFormSecondStep(self.container, request) form.updateFieldsFromSchemata() form.updateWidgets() data, errors = form.extractData() self.assertEqual(0, len(errors))
async def export_members(self, msg, message): server = message.server if not self.check_server(server): return csv = io.BytesIO() csv.write('#discord_id\n'.encode()) for member in server.members: discord_id = str(member) csv.write('{}\n'.format(discord_id).encode()) csv.seek(0) member_count = len(server.members) filename = 'members-{}.csv'.format(self.config['servers'][server.name]['db']) msg = '{mention} Here is the list of all {count} members in this Discord server'\ .format(mention=message.author.mention, count=member_count) try: await self.client.send_file(message.channel, csv, filename=filename, content=msg) print ('Sent member list ({})'.format(member_count)) except Exception as e: print ('ERROR: Failed to send member list ({})'.format(member_count)) raise e csv.close()
def _csv(self): """Return a fake csv with data""" csv = StringIO() lines = [ ["", "001", "First", "", "F1", '"Folder \n1\n"'], ["001", "001.1", "first 1", "F1", "F1.1", "Folder 1.1 "], ["001", "001.2", "second 1", "F1", "F1.2", "Folder 1.2"], ["", "002", "Second", "", "F2", "Folder 2"], ["002", "002.1", "first 2", "F2", "F2.1A", "Folder 2.1 A"], ["002", "002.1", "first 2", "F2", "F2.1B", "Folder 2.1 B"], ] for line in lines: csv.write(";".join(line) + "\n") csv.seek(0) return csv
def _complex_csv(self): """Return a fake csv with a more complex structure""" csv = StringIO() lines = [ # ["Code Folder", "Code Subfolder", "Folder Title", "Subfolder Title"], ["001", "001, 001.1", '"Folder 1\n"', '"Subfolder \n1.1"'], ["001", "001.2", "", "Subfolder 1.2"], ["001", "", "", "Subfolder 1.3"], ["002", "002.1", "Folder 2", "Subfolder 2.1"], ["002", "002.1", "Folder 2", "Subfolder 2.2"], ["002", "", "", "Subfolder 2.3"], ] for line in lines: csv.write(";".join(line) + "\n") csv.seek(0) return csv
def _csv(self): """Return a fake csv with data""" csv = StringIO() lines = [ ["", "key1", "Key 1"], ["key1", "key1.1", "Key 1.1"], ["key1.1", "key1.1.1", "Key 1.1.1"], ["key1", "key1.2", "Key 1.2"], ["key1", "key1.3", "Key 1.3"], ["", "key2", "Key 2"], ["key2", "key2.1", "Key 2.1"], ] for line in lines: csv.write(";".join(line) + "\n") csv.seek(0) return csv
def test_second_step_import_encoding(self): """Test importing csv data with special chars in header and content""" form = importform.ImportFormSecondStep(self.container, self.layer["request"]) annotations = IAnnotations(self.container) annotation = annotations[importform.ANNOTATION_KEY] = PersistentDict() annotation["has_header"] = True annotation["separator"] = u";" csv = StringIO() lines = [ [u"猫".encode("utf8"), u"èè".encode("utf8"), u"ùù".encode("utf8")], ["", u"kéy1".encode("utf8"), u"Kèy 1".encode("utf8")], [ u"kéy1".encode("utf8"), u"kéy1.1".encode("utf8"), u"猫".encode("utf8") ], ] for line in lines: csv.write(";".join(line) + "\n") csv.seek(0) annotation["source"] = NamedBlobFile( data=csv.read(), contentType=u"text/csv", filename=u"test.csv", ) data = { "column_0": "parent_identifier", "column_1": "identifier", "column_2": "title", "decimal_import": False, "allow_empty": False, } form._import(data) self.assertEqual(1, len(self.container)) self.assertEqual([u"kéy1"], [e.identifier for e in self.container.values()]) key1 = self.container.get_by("identifier", u"kéy1") self.assertEqual(1, len(key1)) self.assertEqual([u"kéy1.1"], [e.identifier for e in key1.values()]) key1_1 = key1.get_by("identifier", u"kéy1.1") self.assertEqual(u"猫", key1_1.title)
def test_second_step_columns_data_format_nok(self): """Test validation of columns data format""" request = self.layer["request"] request.form = { "form.buttons.import": u"Importer", "form.widgets.column_0": u"identifier", "form.widgets.column_1": u"title", "form.widgets.decimal_import": u"selected", "form.widgets.allow_empty": u"False", } annotations = IAnnotations(self.container) annotation = annotations[importform.ANNOTATION_KEY] = PersistentDict() annotation["has_header"] = False annotation["separator"] = u";" csv = StringIO() lines = [ ["-1", "key1"], [".10", "key2"], ["-1.1", "key3"], ["-1 11", "Key4"], ] for line in lines: csv.write(";".join(line) + "\n") csv.seek(0) annotation["source"] = NamedBlobFile( data=csv.read(), contentType=u"text/csv", filename=u"test.csv", ) form = importform.ImportFormSecondStep(self.container, request) form.updateFieldsFromSchemata() form.updateWidgets() data, errors = form.extractData() self.assertEqual(1, len(errors)) self.assertEqual( "Bad format values: Line 4, col 1: '-1 11'", translate(errors[0].error.message), )
def representative_days(self, timeseries, output_path, n=24, solver=None, timelimit=60.0): """ Get representative days from a time series. :param output_path: Path were plots are stored. :param timeseries: Pandas DataFrame with timestamp as index. :param n: Number of representative days to select. :param solver: Solver name. :param timelimit: Time limit [seconds.] :return: Dictionary with the representative days and the weights. The sum of the weights is equal to the number of days in the time series. """ # Dump the timeseries in CSV. csv = StringIO() timeseries.to_csv(csv) csv.seek(0) # Launch daysxtractor xtractor_data = daysxtractor.parseData(csv) if solver is None: daySelector = daysxtractor.SamplingDaysSelector( numberRepresentativeDays=n, timelimit=timelimit, verbose=True) else: daySelector = daysxtractor.MIPDaysSelector( numberRepresentativeDays=n, timelimit=timelimit, solverName=solver, verbose=True) days = daySelector.selectDays(xtractor_data) # Error measures bins = Bins(xtractor_data, daySelector.binsPerTimeSeries) representativeBins = Bins() representativeBins.createFromRepresentativeDays(bins, days) logging.info("\nError measures:") logging.info("\t- Bins population:") for p in bins.labelRanges(): populationMin, populationMax = bins.population(p) logging.info("\t\t%s: min=%.2f%%, max=%.2f%%" % (bins.labels[p].name, populationMin * 100.0, populationMax * 100.0)) logging.info("\t- Normalized root-mean-square error:") for p in bins.labelRanges(): logging.info("\t\t%s: %.2f%%" % (bins.labels[p].name, bins.nrmsError(p, representativeBins) * 100.0)) logging.info("\t- Relative area error:") for p in bins.labelRanges(): logging.info( "\t\t%s: %.2f%%" % (bins.labels[p].name, bins.relativeAreaError(p, representativeBins) * 100.0)) # Plots for label in xtractor_data.labels: xtractor_data.plotRepresentativeTimeseries(label, days, pathPrefix=output_path) for p in bins.labelRanges(): if bins.labels[p].name == "Conso": LD_nrmse = bins.nrmsError(p, representativeBins) return days
def main(): #create socket serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #get local machine name host = socket.gethostname() port = 9999 #bind the port serversocket.bind((host, port)) #queue up requests serversocket.listen(5) #create leap controller and listener listener = SampleListener() controller = Leap.Controller() controller.add_listener(listener) #true if leap is connected connected = listener.on_connect(controller) #set last frame oldFrame, leftWristPosition, leftWristRotation, rightWristPosition, rightWristRotation, IndexLeftData, ThumbLeftData, MiddleLeftData, RingLeftData, PinkyLeftData, IndexRightData, ThumbRightData, MiddleRightData, RingRightData, PinkyRightData = listener.on_frame(controller) #establish connection clientsocket,addr = serversocket.accept() #create csv dir = os.path.dirname(os.path.realpath(__file__)) csv = open(str(dir.replace("\\","/")) + "/animDataServer.csv", 'w+b') #set some variables for counting and checking frameCount = 0 #parse frame data to frame number and wrist position and send to the client pc while True: if not msvcrt.kbhit(): #print listener.on_frame(controller) if oldFrame != listener.on_frame(controller)[0]: if str(leftWristPosition) != '0': #if we have legit values continue if (leftWristPosition[0]==0) and (leftWristPosition[1]==0) and (leftWristPosition[2]==0): #write 0's if we cant find wrist csv.write(('"leftWrist"')+","+str(frameCount)+","+"0"+","+"0"+","+"0"+","+"0"+","+"0"+","+"0"+"\n") #clientsocket.send(('leftWrist')+","+str(frameCount)+","+"0"+","+"0"+","+"0"+","+"0"+","+"0"+","+"0") #clientsocket.sendall('leftWrist'+","+str(frameCount)+","+"0"+","+"0"+","+"0"+","+"0"+","+"0"+","+"0"+","+"\n") else: leftWristPosition = re.sub('[()]', '', str(leftWristPosition)) leftWristPosition = re.sub(r'\s+', '', str(leftWristPosition)) csv.write(('"leftWrist"')+","+str(frameCount)+","+str(leftWristPosition)+","+str(360-leftWristRotation[1])+","+str(leftWristRotation[0])+","+str(leftWristRotation[2])+","+"\n") #clientsocket.sendall('leftWrist'+","+str(frameCount)+","+str(leftWristPosition)+","+str(round(360-leftWristRotation[1],2))+","+str(round(leftWristRotation[0],2))+","+str(round(leftWristRotation[2],2))+","+"\n") for i in range(0,4): csv.write(('"Left Thumb "')+str(ThumbLeftData[i][0])+":"+","+str(frameCount)+","+str(ThumbLeftData[i][1][0])+","+str(ThumbLeftData[i][1][1])+","+str(ThumbLeftData[i][1][2])+"\n") #clientsocket.sendall('Left Thumb '+str(ThumbLeftData[i][0])+":"+","+str(frameCount)+","+str(round(ThumbLeftData[i][1][0],2))+","+str(round(ThumbLeftData[i][1][1],2))+","+str(round(ThumbLeftData[i][1][2],2))+","+"\n") for i in range(0,4): csv.write(('"Left Index "')+str(IndexLeftData[i][0])+":"+","+str(frameCount)+","+str(IndexLeftData[i][1][0])+","+str(IndexLeftData[i][1][1])+","+str(IndexLeftData[i][1][2])+"\n") #clientsocket.sendall('Left Index '+str(IndexLeftData[i][0])+":"+","+str(frameCount)+","+str(round(IndexLeftData[i][1][0],2))+","+str(round(IndexLeftData[i][1][1],2))+","+str(round(IndexLeftData[i][1][2],2))+","+"\n") for i in range(0,4): csv.write(('"Left Middle "')+str(MiddleLeftData[i][0])+":"+","+str(frameCount)+","+str(MiddleLeftData[i][1][0])+","+str(MiddleLeftData[i][1][1])+","+str(MiddleLeftData[i][1][2])+"\n") #clientsocket.sendall('Left Middle '+str(MiddleLeftData[i][0])+":"+","+str(frameCount)+","+str(round(MiddleLeftData[i][1][0],2))+","+str(round(MiddleLeftData[i][1][1],2))+","+str(round(MiddleLeftData[i][1][2],2))+","+"\n") for i in range(0,4): csv.write(('"Left Ring "')+str(RingLeftData[i][0])+":"+","+str(frameCount)+","+str(RingLeftData[i][1][0])+","+str(RingLeftData[i][1][1])+","+str(RingLeftData[i][1][2])+"\n") #clientsocket.sendall('Left Ring '+str(RingLeftData[i][0])+":"+","+str(frameCount)+","+str(round(RingLeftData[i][1][0],2))+","+str(round(RingLeftData[i][1][1],2))+","+str(round(RingLeftData[i][1][2],2))+","+"\n") for i in range(0,4): csv.write(('"Left Pinky "')+str(PinkyLeftData[i][0])+":"+","+str(frameCount)+","+str(PinkyLeftData[i][1][0])+","+str(PinkyLeftData[i][1][1])+","+str(PinkyLeftData[i][1][2])+"\n") #clientsocket.sendall('Left Pinky '+str(PinkyLeftData[i][0])+":"+","+str(frameCount)+","+str(round(PinkyLeftData[i][1][0],2))+","+str(round(PinkyLeftData[i][1][1],2))+","+str(round(PinkyLeftData[i][1][2],2))+","+"\n") if (str(rightWristPosition) != '0'): #if we have legit values continue if (rightWristPosition[0]==0) and (rightWristPosition[1]==0) and (rightWristPosition[2]==0): csv.write(('"rightWrist"')+","+str(frameCount)+","+"0"+","+"0"+","+"0"+","+"0"+","+"0"+","+"0"+"\n") #clientsocket.send(('rightWrist')+","+str(frameCount)+","+"0"+","+"0"+","+"0"+","+"0"+","+"0"+","+"0") else: #write wrist location and rotations and bone rotations rightWristPosition = re.sub('[()]', '', str(rightWristPosition)) rightWristPosition = re.sub(r'\s+', '', str(rightWristPosition)) csv.write(('"rightWrist"')+","+str(frameCount)+","+str(rightWristPosition)+","+str(360-rightWristRotation[1])+","+str(rightWristRotation[0])+","+str(rightWristRotation[2])+","+"\n") #clientsocket.send(('rightWrist')+","+str(frameCount)+","+str(rightWristPosition)+","+str(360-rightWristRotation[1])+","+str(rightWristRotation[0])+","+str(rightWristRotation[2])) for i in range(0,4): csv.write(('"Right Thumb "')+str(ThumbRightData[i][0])+":"+","+str(frameCount)+","+str(ThumbRightData[i][1][0])+","+str(ThumbRightData[i][1][1])+","+str(ThumbRightData[i][1][2])+"\n") for i in range(0,4): csv.write(('"Right Index "')+str(IndexRightData[i][0])+":"+","+str(frameCount)+","+str(IndexRightData[i][1][0])+","+str(IndexRightData[i][1][1])+","+str(IndexRightData[i][1][2])+"\n") for i in range(0,4): csv.write(('"Right Middle "')+str(MiddleRightData[i][0])+":"+","+str(frameCount)+","+str(MiddleRightData[i][1][0])+","+str(MiddleRightData[i][1][1])+","+str(MiddleRightData[i][1][2])+"\n") for i in range(0,4): csv.write(('"Right Ring "')+str(RingRightData[i][0])+":"+","+str(frameCount)+","+str(RingRightData[i][1][0])+","+str(RingRightData[i][1][1])+","+str(RingRightData[i][1][2])+"\n") for i in range(0,4): csv.write(('"Right Pinky "')+str(PinkyRightData[i][0])+":"+","+str(frameCount)+","+str(PinkyRightData[i][1][0])+","+str(PinkyRightData[i][1][1])+","+str(PinkyRightData[i][1][2])+"\n") oldFrame, leftWristPosition, leftWristRotation, rightWristPosition, rightWristRotation, IndexLeftData, ThumbLeftData, MiddleLeftData, RingLeftData, PinkyLeftData, IndexRightData, ThumbRightData, MiddleRightData, RingRightData, PinkyRightData = listener.on_frame(controller) frameCount += 1 else: print listener.on_disconnect(controller) break #handle data transfer csv.seek(0) data = csv.read(1024) while (data): print 'Sending Leap Motion data...' clientsocket.send(data) data = csv.read(1024) csv.close() print "Sent Data and Closing" os.remove(str(dir.replace("\\","/")) + "/animDataServer.csv") clientsocket.shutdown(socket.SHUT_WR)
def test_second_step_import_decimal_basic(self): """Test importing csv data with decimal codes""" form = importform.ImportFormSecondStep(self.container, self.layer["request"]) annotations = IAnnotations(self.container) annotation = annotations[importform.ANNOTATION_KEY] = PersistentDict() annotation["has_header"] = False annotation["separator"] = u";" csv = StringIO() lines = [ ["100", "Key 1"], ["100.1", "Key 1.1"], ["100.2", "Key 1.2"], ["200", "Key 2"], ["200.1", "Key 2.1"], ["200.10", "Key 2.10"], ] for line in lines: csv.write(";".join(line) + "\n") csv.seek(0) annotation["source"] = NamedBlobFile( data=csv.read(), contentType=u"text/csv", filename=u"test.csv", ) data = { "column_0": "identifier", "column_1": "title", "decimal_import": True, "allow_empty": False, } form._import(data) self.assertEqual(2, len(self.container)) self.assertEqual(["1", "2"], sorted( [e.identifier for e in self.container.values()])) code_1 = self.container.get_by("identifier", "1") self.assertEqual("1", code_1.title) self.assertEqual(1, len(code_1)) self.assertEqual(["10"], [e.identifier for e in code_1.values()]) code_10 = code_1.get_by("identifier", "10") self.assertEqual("10", code_10.title) self.assertEqual(1, len(code_10)) self.assertEqual(["100"], [e.identifier for e in code_10.values()]) code_100 = code_10.get_by("identifier", "100") self.assertEqual("Key 1", code_100.title) self.assertEqual(2, len(code_100)) self.assertEqual( ["100.1", "100.2"], sorted([e.identifier for e in code_100.values()]), ) self.assertEqual( ["Key 1.1", "Key 1.2"], sorted([e.title for e in code_100.values()]), ) code_2 = self.container.get_by("identifier", "2") self.assertEqual("2", code_2.title) self.assertEqual(1, len(code_2)) self.assertEqual(["20"], [e.identifier for e in code_2.values()]) code_20 = code_2.get_by("identifier", "20") self.assertEqual("20", code_20.title) self.assertEqual(1, len(code_20)) self.assertEqual(["200"], [e.identifier for e in code_20.values()]) code_200 = code_20.get_by("identifier", "200") self.assertEqual("Key 2", code_200.title) self.assertEqual(1, len(code_200)) self.assertEqual(["200.1"], [e.identifier for e in code_200.values()]) code_2001 = code_200.get_by("identifier", "200.1") self.assertEqual("Key 2.1", code_2001.title) self.assertEqual(1, len(code_2001)) self.assertEqual(["200.10"], [e.identifier for e in code_2001.values()]) self.assertEqual(["Key 2.10"], [e.title for e in code_2001.values()])