def loadAndCompareProfiles(prof1, prof2): prof1_data = js.load_json(prof1) if prof1_data is None: print("Failed to load profile: {}".format(prof1)) prof2_data = js.load_json(prof2) if prof2_data is None: print("Failed to load profile: {}".format(prof2))
def printPartitionSetInfo(partitionDir): """ Given a directory containing all the json for a partition this function will print it out. """ partitions = [] partition_to_users = {} partition_type = "" for fname in os.listdir(partitionDir): (filebase, ext) = fname.rsplit('.',1) if ext == 'ditto': continue if fname == "user_map.json": data = js.load_json(os.path.join(partitionDir, fname)) for user,partition in data.items(): if partition not in partition_to_users: partition_to_users[partition] = [] partition_to_users[partition].append(user) partition_type = data['partitioner'] continue # Load the json fname = os.path.join(partitionDir,fname) try: data = js.load_json(fname) except Exception as e: print("Failed to load json: {}".format(fname)) sys.exit(2) pnum = int(filebase) if len(partitions) <= pnum: partitions += [None] * (pnum - len(partitions) + 1) partitions[pnum] = data # Now print all of them print("\n---- Partitioner Type: {} ----\n".format(partition_type)) for index,partition in enumerate(partitions): if partition is None: continue print("Partition Number {}".format(index)) print("\tSource profile: {}".format(partition['source'])) print("\tFly time #: {}".format(len(partition['fly_times']))) print("\tPress time #: {}". format(len(partition['press_times']))) print("\tWPM: {}".format(partition['wpm'])) print("\tUsers in partition: {}".format(partition_to_users[index]))
def loadProfiles(*filepaths): profiles = {} user_name_re = re.compile("(.+?)_reduced.json") for path in filepaths: fname = os.path.basename(path) # Load the profile's json data try: prof_data = js.load_json(path) except Exception as e: print("Failed to load json: {}".format(e)) sys.exit(-1) # Verify it has already been reduced print("Doing json format pre-check on {}".format(path)) if not expectedJsonFormat(prof_data): print("Bad profile format: '{}'".format(path)) print("The profile data likely hasn't been reduced yet") sys.exit(1) if 'prof_name' in prof_data: fname = prof_data['prof_name'] else: m = user_name_re.search(fname) if m is None: print("Cannot create internal name for profile. Either use the format of the reducer or provide a 'prof_name' key in the profile") sys.exit(1) fname = m.group(1) profiles[fname] = prof_data return profiles
def printReducedProfileInfo(*profiles): for profile in profiles: if "user_map.json" in profile: continue all_data = js.load_json(profile) pdata = all_data wpm = 0 if 'text' in all_data: if 'wpm' in pdata: wpm = pdata['wpm'] pdata = all_data['text'] fly_total_float = 0.0 fly_total = num_fly = 0 max_fly = -1 (max_fly_from, max_fly_to) = (-1,-1) for from_key, to_keys in pdata['fly_times'].items(): for to_key, time in to_keys.items(): #if type(to_key) in (str,unicode) and to_key.endswith("_stdv"): # continue if time > max_fly: max_fly = time max_fly_from = from_key max_fly_to = to_key num_fly += 1 fly_total_float += time fly_total += int(time) print("\n=======================\n") print("Profile Name: {}".format(profile)) if 'username' in all_data and all_data['username'] is not None: print("User name: {}".format(all_data['username'])) print("# collected fly: {} (unique pairs)".format(pdata['num_fly_times'])) print("# collected press: {} (unique keys)".format(pdata['num_press_times'])) print("# filtered fly: {} (range: {})".format(pdata['filtered_fly'], pdata['irq_range_fly'])) print("# filtered press: {} (range: {})".format(pdata['filtered_press'], pdata['irq_range_press'])) print("Mean fly time: {} ms".format(pdata['fly_mean'])) print("Standard Dev. fly time: {} ms".format(pdata['fly_stdv'])) print("Mean press time: {} ms".format(pdata['press_mean'])) print("Standard Dev. press time: {} ms".format(pdata['press_stdv'])) if wpm > 0: print("Words Per Minute: {}".format(wpm)) print("Max fly time from {} -> {} = {} ms".format(max_fly_from, max_fly_to, max_fly)) print("Max fly time as char {} -> {}".format(getCharFromJSCode(max_fly_from), getCharFromJSCode(max_fly_to))) print("Recalculated mean fly: {} ms".format(fly_total / num_fly)) print("Recalculated mean fly (float): {} ms".format(fly_total_float / num_fly))
def compareDittoAndJson(ditto, profile): # First read in the ditto dprofile and create a python # dictionary ditto_as_map = {'fly_times':{}, 'press_times':{}} unitSize = sizeofStruct(FSProfileStruct) with open(ditto, "rb") as dprofile: sBytes = dprofile.read(unitSize) total_fly_times = [] total_press_times = [] while sBytes: struct = buildStructFromBytes(FSProfileStruct(), sBytes) if struct.time_type == FSProfileTimeType.FLY_TIME: from_key = JSCodeFromDittoSC(struct.from_key) to_key = JSCodeFromDittoSC(struct.to_key) if from_key not in ditto_as_map['fly_times']: ditto_as_map['fly_times'][from_key] = {} ditto_as_map['fly_times'][from_key][to_key] = struct.time_in_ms elif struct.time_type == FSProfileTimeType.PRESS_TIME: key = JSCodeFromDittoSC(struct.from_key) ditto_as_map['press_times'][key] = struct.time_in_ms else: print("!! ---- Unrecognized FSProfileTimeType ( {} ) ---- !!".format(struct.time_type)) sBytes = dprofile.read(unitSize) # Load the regular dprofile profile_data = js.load_json(profile) if profile_data is None: print("Failed to load dprofile " + profile) sys.exit(2) # Do the comparison compareProfiles(ditto_as_map, profile_data, timeToInt=True)
def json_to_ditto(in_profile, out_profile): if not out_profile.endswith(".ditto"): print("Warning: ditto profiles usually end with .ditto") json_profile = load_json(in_profile) ditto_profile = open(out_profile, "wb") if "text" in json_profile: json_profile = json_profile["text"] if (json_profile is None) or (ditto_profile is None): print("Failed to load required data(json: {}, out: {})".format(json_profile, ditto_profile)) return False stdv_re = re.compile("^[0-9]+_stdv$") # print out info on the size of the input profile sz = 0 for k, v in json_profile["fly_times"].items(): for k2, v2 in v.items(): if stdv_re.match(k2): continue sz += 1 print("# fly times in input profile = " + str(sz)) total_fly = total_press = 0 fly_time_total = press_time_total = 0 # When we can't map from the js to the ditto code for a key we throw it out. This can # have an impact on the overall fly and press times for a profile thrown_fly = [] thrown_press = [] num_stdv = 0 unit = FSProfileStruct() unit.setTimeType(FSProfileTimeType.FLY_TIME) for from_key, to_key_info in json_profile["fly_times"].items(): for to_key, time in to_key_info.items(): if stdv_re.match(to_key): # We don't include any stdv info in ditto profiles right now num_stdv += 1 continue mapped_key = DittoSCFromJSCode(int(from_key)) if mapped_key < 0: unmapped_key_warn(from_key) thrown_fly.append(int(time)) continue unit.setFromKey(mapped_key) mapped_key = DittoSCFromJSCode(int(to_key)) if mapped_key < 0: unmapped_key_warn(to_key) thrown_fly.append(int(time)) continue unit.setToKey(mapped_key) unit.setKeyTimeMs(int(time)) writeStruct(ditto_profile, unit) total_fly += 1 fly_time_total += int(time) ## Setup for adding press times unit.setTimeType(FSProfileTimeType.PRESS_TIME) for key, time in json_profile["press_times"].items(): if stdv_re.match(key): # Once again, we don't include stdv right now num_stdv += 1 continue # For press times Ditto just takes the 'from' key but to be safe # incase of future changes I just set both to be the same mapped_key = DittoSCFromJSCode(int(key)) if mapped_key < 0: # They will already get the warning in fly times # unmapped_key_warn(key) thrown_press.append(int(time)) continue unit.setFromKey(mapped_key) unit.setToKey(mapped_key) unit.setKeyTimeMs(int(time)) writeStruct(ditto_profile, unit) total_press += 1 press_time_total += int(time) ditto_profile.close() print("\nFinished converting source profile '{}' to ditto profile '{}'".format(in_profile, out_profile)) print("\t# Fly Times: {}".format(total_fly)) print("\t# Press Times: {}".format(total_press)) print("\tFly time average: {}".format(fly_time_total / total_fly)) print("\tPress time average: {}".format(press_time_total / total_press)) if len(thrown_fly) > 0: print("\tAverage of unmapped fly times: {}".format(sum(thrown_fly) / len(thrown_fly))) if len(thrown_press) > 0: print("\tAverage of unmapped press times: {}".format(sum(thrown_press) / len(thrown_press))) print("\tIgnored {} standard deviation entries".format(num_stdv))