Exemple #1
0
def gen_text_general_info(xml, r):
    from dateutil.relativedelta import relativedelta

    total_notes = 0
    for tap_note_scores in xml.iter("TapNoteScores"):
        total_notes += sum(int(e.text) for e in tap_note_scores)
    total_notes_string = util.abbreviate(total_notes, min_precision=3)

    scores = list(iter_scores(xml))
    num_charts = len(list(xml.iter("Chart")))
    hours = sum(float(s.findtext("SurviveSeconds")) / 3600 for s in scores)
    first_play_date = min([parsedate(s.findtext("DateTime")) for s in scores])
    duration = relativedelta(datetime.now(), first_play_date)

    grades = count_nums_grades(xml)
    # ~ grades_string_1 = ", ".join(f"{name}: {grades[name]}" for name in ("AAAA", "AAA", "AA"))
    # ~ grades_string_2 = ", ".join(f"{name}: {grades[name]}" for name in ("A", "B", "C", "D"))
    grades_string = ", ".join(f"{name}: {grades[name]}"
                              for name in "AAAA AAA AA A B C D".split())
    grade_names = list(reversed(util.grade_names))

    best_aaa = (None, 0)
    best_aaaa = (None, 0)
    for score in iter_scores(xml):
        wifescore = float(score.findtext("SSRNormPercent"))
        skillset_ssrs = score.find("SkillsetSSRs")
        if skillset_ssrs is None: continue
        overall = float(skillset_ssrs.findtext("Overall"))

        if wifescore < util.AAA_THRESHOLD:
            pass  # we don't care about sub-AAA scores
        elif wifescore < util.AAAA_THRESHOLD:
            if overall > best_aaa[1]:
                best_aaa = (score, overall)
        else:
            if overall > best_aaaa[1]:
                best_aaaa = (score, overall)

    def get_score_desc(score, overall) -> str:
        if score is None: return "[none]"
        chart = util.find_parent_chart(xml, score)
        dt = score.findtext("DateTime")
        wifescore = float(score.findtext("SSRNormPercent"))
        pack = chart.get("Pack")
        song = chart.get("Song")
        return f"{overall:.2f}, {wifescore*100:.2f}% - \"{song}\" ({pack}) - {dt[:10]}"

    return "<br>".join([
        f"You started playing {duration.years} years {duration.months} months ago",
        f"Total hours spent playing: {round(hours)} hours",
        f"Number of scores: {len(scores)}",
        f"Number of unique files played: {num_charts}",
        f"Grades: {grades_string}",
        # ~ f"Grades: {grades_string_1}",
        # ~ f"{util.gen_padding_from('Grades: ')}{grades_string_2}",
        f"Total arrows hit: {total_notes_string}",
        f"Best AAA: {get_score_desc(best_aaa[0], best_aaa[1])}",
        f"Best AAAA: {get_score_desc(best_aaaa[0], best_aaaa[1])}",
    ])
Exemple #2
0
def gen_week_skillsets(xml):
    # returns an integer week from 0-51
    def week_from_score(score) -> int:
        datetime = parsedate(score.findtext("DateTime"))
        week = datetime.isocalendar()[1]
        return week

    chronological_scores = sorted(iter_scores(xml),
                                  key=lambda s: s.findtext("DateTime"))

    week_start_datetimes: List[datetime] = []
    diffsets: List[List[float]] = []

    for week, scores_in_week in util.groupby(chronological_scores,
                                             week_from_score):
        diffset = [0, 0, 0, 0, 0, 0, 0]
        for score in scores_in_week:
            skillset_ssrs = score.find("SkillsetSSRs")
            if skillset_ssrs is None: continue
            diffs = [float(diff.text) for diff in skillset_ssrs[1:]]
            main_diff = diffs.index(max(diffs))
            diffset[main_diff] += 1

        total = sum(diffset)
        if total == 0: continue
        diffset = [diff / total * 100 for diff in diffset]

        year = scores_in_week[0].findtext("DateTime")[:4]
        week_start_datetime = datetime.strptime(f"{year} {week} {0}",
                                                "%Y %W %w")

        diffsets.append(diffset)
        week_start_datetimes.append(week_start_datetime)

    return (week_start_datetimes, diffsets)
Exemple #3
0
def calc_median_score_increase(xml):
    from statistics import median

    score_increases = []

    for chart in xml.iter("ScoresAt"):
        # Chronologically sorted scores
        scores = sorted(iter_scores(chart),
                        key=lambda s: s.findtext("DateTime"))

        for i in range(0, len(scores) - 1):
            datetime_1 = parsedate(scores[i].findtext("DateTime"))
            datetime_2 = parsedate(scores[i + 1].findtext("DateTime"))
            time_delta = datetime_2 - datetime_1
            play_time = float(scores[i].findtext("SurviveSeconds"))
            idle_time = time_delta.total_seconds() - play_time

            # If the same chart is played twice within 60 seconds
            if idle_time < 60:
                score_1 = float(scores[i].findtext("SSRNormPercent"))
                score_2 = float(scores[i + 1].findtext("SSRNormPercent"))
                score_increase = 100 * (score_2 - score_1)
                score_increases.append(score_increase)

    if len(score_increases) == 0:
        return 0
    else:
        return median(score_increases)
Exemple #4
0
def divide_into_sessions(xml):
    if cache("sessions_division_cache"):
        return cache("sessions_division_cache")

    session_end_threshold = timedelta(hours=1)

    scores = list(iter_scores(xml))
    datetimes = [parsedate(s.find("DateTime").text) for s in scores]
    zipped = zip(scores, datetimes)
    zipped = sorted(zipped, key=lambda pair: pair[1])

    # zipped is a list of chronologically sorted (score object, datetime) tuples

    prev_score_datetime = zipped[0][1]  # first datetime
    current_session = [
        zipped[0]
    ]  # list of (score object, datetime) tuples in current session
    sessions = [
    ]  # list of sessions where every session is like `current_session`
    for score, score_datetime in zipped[1:]:
        score_interval = score_datetime - prev_score_datetime
        # check if timedelta between two scores is too high
        if score_interval > session_end_threshold:
            sessions.append(current_session)
            current_session = []
        current_session.append((score, score_datetime))
        prev_score_datetime = score_datetime
    sessions.append(current_session)

    return cache("sessions_division_cache", sessions)
Exemple #5
0
def count_nums_grades(xml):
    grades = []
    for score in util.iter_scores(xml):
        percent = float(score.findtext("SSRNormPercent"))
        grade = sum(percent >= t for t in util.grade_thresholds) - 1
        grades.append(util.grade_names[grade])
    return Counter(grades)
Exemple #6
0
def calc_average_hours_per_day(xml, timespan=timedelta(days=365 / 2)):
    scores = sorted(iter_scores(xml), key=lambda s: s.findtext("DateTime"))

    total_hours = 0
    for score in scores:
        total_hours += float(score.findtext("SurviveSeconds")) / 3600

    return total_hours / timespan.days
Exemple #7
0
def gen_most_played_charts(xml, num_charts):
    charts_num_plays = []
    for chart in xml.iter("Chart"):
        score_filter = lambda s: float(s.findtext("SSRNormPercent")) > 0.5
        num_plays = len([s for s in iter_scores(chart) if score_filter(s)])
        if num_plays > 0: charts_num_plays.append((chart, num_plays))

    charts_num_plays.sort(key=lambda pair: pair[1], reverse=True)
    return charts_num_plays[:num_charts]
Exemple #8
0
def find_longest_combo(xml):
    max_combo_chart = None
    max_combo = 0
    for chart in xml.iter("Chart"):
        for score in iter_scores(chart):
            combo = int(score.findtext("MaxCombo"))
            if combo > max_combo:
                max_combo = combo
                max_combo_chart = chart
    return max_combo_chart, max_combo
Exemple #9
0
def gen_wifescore_frequencies(xml):
    # e.g. the 0.70 bucket corresponds to all scores between 0.70 and 0.71 (not 0.695 and 0.705!)
    frequencies = {percent: 0 for percent in range(70, 100)}

    for score in iter_scores(xml):
        wifescore = float(score.findtext("SSRNormPercent"))
        percent = round(wifescore * 100)
        if percent in frequencies:
            frequencies[percent] += 1
    return list(frequencies.keys()), list(frequencies.values())
Exemple #10
0
def gen_plays_by_hour(xml):
    num_plays = [0] * 24
    for score in iter_scores(xml):
        datetime = parsedate(score.find("DateTime").text)
        num_plays[datetime.hour] += 1

    # I tried to use a datetime as key (would be nicer to display), but
    # it doesn't play nicely with matplotlib, so we need to use an
    # integer to represent the hour of the day.
    #return {time(hour=i): num_plays[i] for i in range(24)}
    return list(range(24)), num_plays
Exemple #11
0
def generate_pack_likings(xml, months):
    likings = {}
    for chart in xml.iter("Chart"):
        num_relevant_plays = 0
        for score in iter_scores(chart):
            if util.score_within_n_months(score, months):
                num_relevant_plays += 1
        pack = chart.get("Pack")

        if pack not in likings: likings[pack] = 0
        likings[pack] += num_relevant_plays

    return likings
Exemple #12
0
def gen_hours_per_skillset(xml):
    hours = [0, 0, 0, 0, 0, 0, 0]

    for score in iter_scores(xml):
        skillset_ssrs = score.find("SkillsetSSRs")
        if skillset_ssrs is None: continue
        diffs = [float(diff.text) for diff in skillset_ssrs[1:]]
        main_diff = diffs.index(max(diffs))

        length_hours = float(score.findtext("SurviveSeconds")) / 3600
        hours[main_diff] += length_hours

    return hours
Exemple #13
0
def calculate_total_wifescore(xml, months=6):
    weighted_sum = 0
    num_notes_sum = 0
    for score in iter_scores(xml):
        if not util.score_within_n_months(score, months): continue

        num_notes = util.num_notes(score)
        num_notes_sum += util.num_notes(score)

        wifescore = float(score.findtext("SSRNormPercent"))
        weighted_sum += wifescore * num_notes

    try:
        return weighted_sum / num_notes_sum
    except ZeroDivisionError:
        return 0
Exemple #14
0
def gen_plays_per_week(xml):
    datetimes = [parsedate(s.findtext("DateTime")) for s in iter_scores(xml)]
    datetimes.sort()

    weeks = {}
    week_end = datetimes[0]
    week_start = week_end - timedelta(weeks=1)
    i = 0
    while i < len(datetimes):
        if datetimes[i] < week_end:
            weeks[week_start] += 1
            i += 1
        else:
            week_start += timedelta(weeks=1)
            week_end += timedelta(weeks=1)
            weeks[week_start] = 0

    return (list(weeks.keys()), list(weeks.values()))
Exemple #15
0
def gen_idle_time_buckets(xml):
    # Each bucket is 5 seconds. Total 10 minutes is tracked
    buckets = [0] * 600

    a, b = 0, 0

    scores = []
    for scoresat in xml.iter("ScoresAt"):
        rate = float(scoresat.get("Rate"))
        scores.extend(((score, rate) for score in iter_scores(scoresat)))

    # Sort scores by datetime, oldest first
    scores.sort(key=lambda pair: pair[0].findtext("DateTime"))

    last_play_end = None
    for score, rate in scores:
        a += 1
        datetime = util.parsedate(score.findtext("DateTime"))
        survive_seconds = float(score.findtext("SurviveSeconds"))
        #print(survive_seconds, rate)
        length = timedelta(seconds=survive_seconds * rate)

        #print("Datetime:", datetime)
        #print("Play length:", str(length)[:-7], "(according to SurviveSeconds)")
        if last_play_end is not None:
            idle_time = datetime - last_play_end
            if idle_time >= timedelta():
                bucket_index = int(idle_time.total_seconds() // 5)
                if bucket_index < len(buckets):
                    buckets[bucket_index] += 1
            else:
                #print("Negative idle time!")
                b += 1

        last_play_end = datetime + length
        #print("Finished", last_play_end)
        #print()

    # ~ keys = [i * 5 for i in range(len(buckets))]
    keys = range(len(buckets))
    return (keys, buckets)
Exemple #16
0
def gen_hours_per_week(xml):
    scores = iter_scores(xml)
    pairs = [(s, parsedate(s.findtext("DateTime"))) for s in scores]
    pairs.sort(key=lambda pair: pair[1])  # Sort by datetime

    weeks = {}
    week_end = pairs[0][1]  # First (earliest) datetime
    week_start = week_end - timedelta(weeks=1)
    i = 0
    while i < len(pairs):
        score, datetime = pairs[i][0], pairs[i][1]
        if datetime < week_end:
            score_seconds = float(score.findtext("SurviveSeconds")) or 0
            weeks[week_start] += score_seconds / 3600
            i += 1
        else:
            week_start += timedelta(weeks=1)
            week_end += timedelta(weeks=1)
            weeks[week_start] = 0

    return (list(weeks.keys()), list(weeks.values()))
Exemple #17
0
def map_scores(xml,
               mapper,
               *mapper_args,
               discard_errors=True,
               brush_color_over_10_notes=None):
    x, y = [], []
    ids = []
    if brush_color_over_10_notes:
        brushes = []
    for score in iter_scores(xml):
        if discard_errors:
            try:
                value = (mapper)(score, *mapper_args)
            except Exception:
                continue
        else:
            value = (mapper)(score, *mapper_args)
        if value is None: continue

        x.append(parsedate(score.findtext("DateTime")))
        y.append(value)
        ids.append(score)
        if brush_color_over_10_notes:
            tap_note_scores = score.find("TapNoteScores")
            if tap_note_scores:
                judgements = ["Miss", "W1", "W2", "W3", "W4", "W5"]
                total_notes = sum(
                    int(tap_note_scores.findtext(x)) for x in judgements)
            else:
                total_notes = 500  # just assume 100 as a default yolo

            brushes.append(
                brush_color_over_10_notes if total_notes > 10 else "#AAAAAA")

    if brush_color_over_10_notes:
        return (((x, y), ids), brushes)
    else:
        return ((x, y), ids)