def write_workplan(service, spid, a, workplan, stats, faction, travelmode='walking', nosave=False): from pprint import pformat if spid.find('/') > 0: chunks = spid.split('/') spid = chunks[5] # Use for spreadsheet rows planrows = [] travelmoji = { 'walking': u"\U0001F6B6", 'bicycling': u"\U0001F6B2", 'transit': u"\U0001F68D", 'driving': u"\U0001F697", } n = a.order() logger.info('portals:') for p in range(n): logger.info(' %d: %s', p, a.node[p]['name']) logger.info('orig_workplan:\n%s', pformat(a.orig_workplan)) logger.info('fixes:\n%s', pformat(a.fixes)) logger.info('workplan:\n%s', pformat(workplan)) logger.info('stats:\n%s', pformat(stats)) # Track which portals we've already captured # (easier than going through the list backwards) prev_p = None plan_at = 0 n_waypoints = 0 for p, q, f in workplan: plan_at += 1 # Are we at a different location than the previous portal? if p != prev_p: # How many keys do we need if/until we come back? ensurekeys = 0 totalkeys = 0 # Track when we leave this portal lastvisit = True same_p = True for fp, fq, ff in workplan[plan_at:]: if fp == p: # Are we still at the same portal? if same_p: continue if lastvisit: lastvisit = False ensurekeys = totalkeys else: # we're at a different portal same_p = False if fq == p: # Future link to this portal totalkeys += 1 mapurl = 'https://www.google.com/maps/dir/?api=1&destination=%s&travelmode=%s' % ( a.node[p]['pll'], travelmode) if 'special' in a.node[p]: special = a.node[p]['special'] else: special = None if prev_p is not None: dist = maxfield.get_portal_distance(prev_p, p) duration = maxfield.get_portal_time(prev_p, p) if dist > 40: if dist >= 500: nicedist = '%0.1f km (%s min)' % ( (dist / float(1000)), duration) else: nicedist = '%d m' % dist hyperlink = '=HYPERLINK("%s"; "%s")' % (mapurl, nicedist) planrows.append((u'▼', )) planrows.append((travelmoji[travelmode], hyperlink)) logger.info('-->Move to %s [%s]', a.node[p]['name'], nicedist) else: planrows.append((u'▼', )) else: hyperlink = '=HYPERLINK("%s"; "map")' % mapurl planrows.append((travelmoji[travelmode], hyperlink)) logger.info('-->Start at %s', a.node[p]['name']) logger.info('--|At %s', a.node[p]['name']) # Are we at a waypoint? if special in ('_w_start', '_w_end'): planrows.append(('W', a.node[p]['name'])) # Nothing else here prev_p = p n_waypoints += 1 continue planrows.append(('P', a.node[p]['name'])) # Are we at a blocker? if special == '_w_blocker': planrows.append(('X', 'destroy blocker')) logger.info('--|X: destroy blocker') # Nothing else here prev_p = p n_waypoints += 1 continue if totalkeys: if lastvisit: logger.info('--|H: ensure %d keys', totalkeys) planrows.append(('H', 'ensure %d keys' % totalkeys)) elif ensurekeys: logger.info('--|H: ensure %d keys (%d max)', ensurekeys, totalkeys) planrows.append( ('H', 'ensure %d keys (%d max)' % (ensurekeys, totalkeys))) else: logger.info('--|H: %d max keys needed', totalkeys) planrows.append(('H', '%d max keys needed' % totalkeys)) if lastvisit: totallinks = a.out_degree(p) + a.in_degree(p) planrows.append(('S', 'shields on (%d links)' % totallinks)) logger.info('--|S: shields on (%d out, %d in)', a.out_degree(p), a.in_degree(p)) prev_p = p if q is not None: # Add links/fields if f > 1: action = 'D' elif f == 1: action = 'F' else: action = 'L' planrows.append((action, u'▶%s' % a.node[q]['name'])) logger.info(' \\%s--> %s', action, a.node[q]['name']) totalkm = stats['dist'] / float(1000) logger.info('Total workplan distance: %0.2f km', totalkm) logger.info('Total workplan play time: %s (%s %s)', stats['nicetime'], stats['nicetraveltime'], travelmode) logger.info('Total AP: %s (%s without capturing)', stats['ap'], stats['ap'] - (a.order() * maxfield.CAPTUREAP)) if nosave: logger.info('Not saving spreadsheet per request.') return # title = 'Ingress: around %s (%s AP)' % (a.node[0]['name'], '{:,}'.format(totalap)) # logger.info('Setting spreadsheet title: %s', title) requests = list() # requests.append({ # 'updateSpreadsheetProperties': { # 'properties': { # 'title': title, # 'locale': 'en_US', # }, # 'fields': 'title', # } # }) dtitle = '%s %s (%0.2fkm/%dP/%sAP)' % ( travelmoji[travelmode], stats['nicetime'], totalkm, a.order() - n_waypoints, '{:,}'.format(stats['ap'])) logger.info('Adding "%s" sheet with %d actions', dtitle, len(workplan)) requests.append({'addSheet': { 'properties': { 'title': dtitle, } }}) body = { 'requests': requests, } res = service.spreadsheets().batchUpdate(spreadsheetId=spid, body=body).execute() sheet_ids = [] for blurb in res['replies']: if 'addSheet' in blurb: sheet_ids.append(blurb['addSheet']['properties']['sheetId']) # Now we generate a values update request updates = list() updates.append({ 'range': '%s!A1:B%d' % (dtitle, len(planrows)), 'majorDimension': 'ROWS', 'values': planrows, }) body = { 'valueInputOption': 'USER_ENTERED', 'data': updates, } service.spreadsheets().values().batchUpdate(spreadsheetId=spid, body=body).execute() logger.info('Resizing columns and adding colours') # now auto-resize all columns requests = [] colors = [ ('H', 1.0, 0.6, 0.4), # Hack ('S', 0.9, 0.7, 0.9), # Shield ('T', 0.9, 0.9, 0.9), # Travel ('P', 0.6, 0.6, 0.6), # Portal ('W', 0.6, 0.6, 0.6), # Waypoint ('X', 1.0, 0.6, 0.6), # Blocker ] if faction == 'res': colors += [ ('L', 0.6, 0.8, 1.0), # Link ('F', 0.3, 0.5, 1.0), # Field ('D', 0.2, 0.3, 0.8), # Double Field ] else: colors += [ ('L', 0.8, 1.0, 0.8), # Link ('F', 0.5, 1.0, 0.5), # Field ('D', 0.3, 0.8, 0.3), # Double Field ] for sid in sheet_ids: requests.append({ 'autoResizeDimensions': { 'dimensions': { 'sheetId': sid, 'dimension': 'COLUMNS', 'startIndex': 0, 'endIndex': 3, } } }) # set conditional formatting on the Workplan sheet my_range = { 'sheetId': sid, 'startRowIndex': 0, 'endRowIndex': len(planrows), 'startColumnIndex': 0, 'endColumnIndex': 1, } for text, red, green, blue in colors: requests.append({ 'addConditionalFormatRule': { 'rule': { 'ranges': [my_range], 'booleanRule': { 'condition': { 'type': 'TEXT_EQ', 'values': [{ 'userEnteredValue': text }] }, 'format': { 'backgroundColor': { 'red': red, 'green': green, 'blue': blue } } } }, 'index': 0 } }) body = { 'requests': requests, } service.spreadsheets().batchUpdate(spreadsheetId=spid, body=body).execute() logger.info('Spreadsheet generation done')
def make_png_steps(workplan, outdir, faction, plotdpi=96): logger.info('Generating step-by-step pngs of the workplan') if not os.path.isdir(outdir): os.mkdir(outdir) a = maxfield.active_graph ap = maxfield.portal_graph c_grn = (0.0, 1.0, 0.0, 0.3) c_blu = (0.0, 0.0, 1.0, 0.3) c_red = (1.0, 0.0, 0.0, 0.5) c_inv = (0.0, 0.0, 0.0, 0.0) portals = np.array([ap.nodes[i]['xy'] for i in ap.nodes()]).T fig = plt.figure(figsize=(11, 8), dpi=plotdpi) frames = list() ax = fig.add_subplot(1, 1, 1) ax.axis('off') ax.plot(portals[0], portals[1], 'ko') ax.set_title('Portals before capture', ha='center') filen = os.path.join(outdir, 'step_{0:03d}.png'.format(len(frames))) fig.savefig(filen) frames.append(filen) p = prev_p = prev_special = None seen_p = list() for p, q, f in workplan: if p != prev_p: torm = list() special = a.nodes[p]['special'] if special is None: # Colour this nodes captured p_coords = np.array(a.nodes[p]['xy']).T if faction == 'res': ax.plot(p_coords[0], p_coords[1], 'bo') else: ax.plot(p_coords[0], p_coords[1], 'go') if prev_p is not None: # Show travel edge if special == '_w_blocker': action = 'Destroy blocker' elif special in ('_w_start', '_w_end'): action = 'Travel to waypoint' else: if prev_special is None: torm = draw_edge(a, prev_p, p, ax, 'm:', directional=True) action = 'Capture' dist = maxfield.get_portal_distance(prev_p, p) if p not in seen_p: if dist > 40: ax.set_title('%s %s (%s m)' % (action, a.nodes[p]['name'], dist), ha='center') else: ax.set_title('%s %s' % (action, a.nodes[p]['name']), ha='center') else: if dist > 40: ax.set_title('Travel to %s (%s m)' % (a.nodes[p]['name'], dist), ha='center') else: ax.set_title('Move to %s' % a.nodes[p]['name'], ha='center') else: ax.set_title('Start at %s' % a.nodes[p]['name'], ha='center') filen = os.path.join(outdir, 'step_{0:03d}.png'.format(len(frames))) fig.savefig(filen) frames.append(filen) for art in torm: art.remove() prev_p = p prev_special = special if p not in seen_p: seen_p.append(p) if q is None: continue ax.set_title('Link to %s' % a.nodes[q]['name'], ha='center') # Draw the link edge torm = draw_edge(a, p, q, ax, 'k-', directional=True) fields = list() if f: # We'll display the new fields in red for tri in a.edges[p, q]['fields']: coords = np.array([a.nodes[v]['xy'] for v in tri]) fields.append( Polygon(shrink(coords.T).T, facecolor=c_red, edgecolor=c_inv)) for field in fields: torm.append(ax.add_patch(field)) filen = os.path.join(outdir, 'step_{0:03d}.png'.format(len(frames))) fig.savefig(filen) frames.append(filen) # remove the newly added edges and triangles from the graph for art in torm: art.remove() # redraw the new edges and fields in the final colour if faction == 'res': draw_edge(a, p, q, ax, 'b-') else: draw_edge(a, p, q, ax, 'g-') if f: # reset fields to faction color for field in fields: if faction == 'res': field.set_facecolor(c_blu) else: field.set_facecolor(c_grn) ax.add_patch(field) # Save final frame with all completed fields ax.set_title('Finish at %s' % a.nodes[p]['name'], ha='center') filen = os.path.join(outdir, 'step_{0:03d}.png'.format(len(frames))) fig.savefig(filen) frames.append(filen) logger.info('Saved step-by-step pngs into %s', outdir)