def __init__( self, id=None, creation_time=None, scope='', remote_origin='127.0.0.1', subject_id='', object_id='', ): self.id, self.id_bytes = generate_or_parse_id(id) if None == creation_time: creation_time = time.time() self.creation_time = int(creation_time) self.creation_datetime = datetime.fromtimestamp( self.creation_time, timezone.utc, ) self.scope = str(scope) self.remote_origin = ip_address(remote_origin) self.subject_id, self.subject_id_bytes = parse_id(subject_id) self.subject = None self.object_id, self.object_id_bytes = parse_id(object_id) self.object = None
def __init__( self, id=None, placement_time=None, subject_id='', user_id='', sticker_id='', position_x=0.0, position_y=0.0, rotation=0.0, scale=0.0, ): self.id, self.id_bytes = generate_or_parse_id(id) if None == placement_time: placement_time = time.time() self.placement_time = int(placement_time) self.placement_datetime = datetime.fromtimestamp( self.placement_time, timezone.utc, ) self.subject_id, self.subject_id_bytes = generate_or_parse_id(subject_id) self.user_id, self.user_id_bytes = parse_id(user_id) self.sticker_id, self.sticker_id_bytes = parse_id(sticker_id) self.position_x = float(position_x) self.position_y = float(position_y) self.rotation = float(rotation) self.scale = float(scale) self.sticker = None
def get_subject_sticker_placement_counts(self, subject_ids): if list != type(subject_ids): subject_ids = [subject_ids] conditions = [] for subject_id in subject_ids: subject_id, subject_id_bytes = parse_id(subject_id) conditions.append( self.sticker_placements.c.subject_id == subject_id_bytes ) statement = self.sticker_placements.select().where( or_(*conditions) ).with_only_columns( [ self.sticker_placements.c.subject_id, func.count(self.sticker_placements.c.id), ] ).group_by( self.sticker_placements.c.subject_id ) result = self.connection.execute(statement).fetchall() subject_sticker_placement_counts = {} for row in result: subject_id, count = row subject_id, subject_id_bytes = parse_id(subject_id) subject_sticker_placement_counts[subject_id] = count return subject_sticker_placement_counts
def __init__( self, id=None, creation_time=None, remote_origin='192.0.2.0', scope='', reason='', note='', expiration_time=0, view_time=0, created_by_user_id='', user_id='', ): self.id, self.id_bytes = generate_or_parse_id(id) current_time = time.time() if None == creation_time: creation_time = current_time self.creation_time = int(creation_time) self.creation_datetime = datetime.fromtimestamp( self.creation_time, timezone.utc, ) self.remote_origin = ip_address(remote_origin) self.scope = scope self.reason = reason self.note = note self.expiration_time = int(expiration_time) self.expiration_datetime = datetime.fromtimestamp( self.expiration_time, timezone.utc, ) self.view_time = int(view_time) self.view_datetime = datetime.fromtimestamp( self.view_time, timezone.utc, ) self.created_by_user_id, self.created_by_user_id_bytes = parse_id( created_by_user_id) self.created_by_user = None self.user_id, self.user_id_bytes = parse_id(user_id) self.user = None self.expired = False if self.expiration_time and self.expiration_time < current_time: self.expired = True
def generate_or_parse_id(id): if not id: id_bytes = uuid.uuid4().bytes id = base64_url_encode(id_bytes) else: id, id_bytes = parse_id(id) return (id, id_bytes)
def __contains__(self, needle): if hasattr(needle, 'id'): return needle.id in self.ids_to_objects try: id, id_bytes = parse_id(needle) except TypeError: return False except ValueError: return False else: return id in self.ids_to_objects
def __init__( self, id=None, receive_time=None, user_id='', sticker_id='', ): self.id, self.id_bytes = generate_or_parse_id(id) if None == receive_time: receive_time = time.time() self.receive_time = int(receive_time) self.receive_datetime = datetime.fromtimestamp( self.receive_time, timezone.utc, ) self.user_id, self.user_id_bytes = parse_id(user_id) self.sticker_id, self.sticker_id_bytes = parse_id(sticker_id) self.sticker = None
def get(self, needle): if hasattr(needle, 'id'): if needle not in self: return None return self.ids_to_objects[needle.id] try: id, id_bytes = parse_id(needle) except TypeError: return None except ValueError: return None else: if id in self: return self.ids_to_objects[id] return None
def remove(self, needle): if hasattr(needle, 'id') and needle.id in self.ids_to_objects: del self.ids_to_objects[needle.id] self.ids.remove(needle.id) return try: id, id_bytes = parse_id(needle) except TypeError: return except ValueError: return else: if id_bytes in self: del self.ids_to_objects[id] self.ids.remove(id)
def edit_tier_permissions( self, client_id, tier_id, length, shareable, group_bits, ): #TODO i don't care about allowing independent length and shareable for each permission #TODO but this is where it would go client_id, client_id_bytes = parse_id(client_id) tier_id = int(tier_id) length = int(length) if shareable: shareable = 1 else: shareable = 0 self.connection.execute(self.tier_permissions.delete().where( and_( self.tier_permissions.c.client_id == client_id_bytes, self.tier_permissions.c.tier_id == tier_id, ))) permissions = [] for scope, bits in group_bits.items(): if bytes == type(bits): bits = int.from_bytes(bits, 'big') else: bits = int(bits) if 0 == bits: continue permissions.append({ 'client_id': client_id_bytes, 'tier_id': tier_id, 'length': length, 'shareable': shareable, 'scope': str(scope), 'group_bits': bits, }) if permissions: # sqlite needs smaller chunks for mass insertion i = 0 per_insert = 50 while permissions: current_chunk = permissions[:50] self.connection.execute( self.tier_permissions.insert().values(current_chunk)) del permissions[:50]
def __init__( self, id=None, client_id='', campaign_id=0, tier_id=0, user_id=0, amount_cents=0, last_fulfill_time=0, last_charge_time=0, last_charge_status='', lifetime_support_cents=0, pledge_relationship_start_time=0, name='', ): self.id, self.id_bytes = generate_or_parse_id(id) self.client_id, self.client_id_bytes = parse_id(client_id) self.campaign_id = int(campaign_id) self.tier_id = int(tier_id) self.user_id = int(user_id) self.amount_cents = int(amount_cents) self.last_fulfill_time = int(last_fulfill_time) self.last_fulfill_datetime = datetime.fromtimestamp( self.last_fulfill_time, timezone.utc, ) self.last_charge_time = int(last_charge_time) self.last_charge_datetime = datetime.fromtimestamp( self.last_charge_time, timezone.utc, ) self.last_charge_status = str(last_charge_status) self.lifetime_support_cents = int(lifetime_support_cents) self.pledge_relationship_start_time = int( pledge_relationship_start_time) self.pledge_relationship_start_datetime = datetime.fromtimestamp( self.pledge_relationship_start_time, timezone.utc, ) self.name = str(name)
def get_user_unique_sticker_placement_counts(self, user_id): user_id = get_id_bytes(user_id) statement = self.sticker_placements.select().where( self.sticker_placements.c.user_id == user_id ).with_only_columns( [ self.sticker_placements.c.sticker_id, func.count(distinct(self.sticker_placements.c.subject_id)), ] ).group_by( self.sticker_placements.c.sticker_id ) result = self.connection.execute(statement).fetchall() unique_sticker_placement_counts = {} for row in result: sticker_id, count = row sticker_id, sticker_id_bytes = parse_id(sticker_id) unique_sticker_placement_counts[sticker_id] = count return unique_sticker_placement_counts
def set_members_last_fulfill_time(self, member_ids, last_fulfill_time=None): if not last_fulfill_time: last_fulfill_time = time.time() if list != type(member_ids): member_ids = [member_ids] conditions = [] for member_id in member_ids: try: member_id, member_id_bytes = parse_id(member_id) except: # raise instead of consuming bad ids? continue conditions.append(self.members.c.id == member_id_bytes) if not conditions: # raise instead of early return for no valid ids? return self.connection.execute(self.members.update().values( last_fulfill_time=int(last_fulfill_time)).where(or_(*conditions)))
def refresh_client(self, client, redirect_uri): response = self.client_request( ('https://www.patreon.com/api/oauth2/v2/campaigns' + '?' + urllib.parse.quote_plus('fields[campaign]') + '=' + 'creation_name'), client, redirect_uri) # parse included data campaigns = {} for row in response['data']: if 'campaign' == row['type']: campaigns[int(row['id'])] = row['attributes']['creation_name'] # there should only be one campaign per client, right? campaign_id, creation_name = next(iter(campaigns.items())) # set campaign id/creation name for this client self.connection.execute(self.clients.update().values( campaign_id=int(campaign_id), creation_name=str(creation_name), ).where(self.clients.c.id == client.id_bytes)) benefits = {} tiers = {} for campaign_id in campaigns.keys(): # first page response = { 'links': { 'next': ('https://www.patreon.com/api/oauth2/v2/campaigns/' + str(campaign_id) + '?include=' + 'tiers' + ',benefits' + '&' + urllib.parse.quote_plus('fields[benefit]') + '=' + 'title' + ',rule_type' + ',next_deliverable_due_date' + '&' + urllib.parse.quote_plus('fields[tier]') + '=' + 'title' + ',amount_cents' + ',unpublished_at') } } while ('links' in response and 'next' in response['links'] and response['links']['next']): response = self.client_request(response['links']['next'], client, redirect_uri) if ('data' in response and 'campaign' == response['data']['type'] and 'included' in response): # non-tier pledge tiers[uuid.uuid4()] = { 'id': 0, 'client_id': client.id_bytes, 'campaign_id': campaign_id, 'amount_cents': 1, 'title': 'Non-tier Pledge', 'amount_cents': 1, 'unpublished': 0, } for resource in response['included']: if ('attributes' not in resource or 'id' not in resource or 'type' not in resource): continue resource_id = int(resource['id']) if 'benefit' == resource['type']: # parse string datetime into timestamp try: parsed = dateutil.parser.parse( resource['attributes'] ['next_deliverable_due_date']) except ValueError: next_deliverable_due_time = 0 except TypeError: next_deliverable_due_time = 0 else: next_deliverable_due_time = int( parsed.timestamp()) benefits[resource_id] = { 'id': resource_id, 'client_id': client.id_bytes, 'campaign_id': campaign_id, 'title': str(resource['attributes']['title']), 'rule_type': str(resource['attributes']['rule_type']), 'next_deliverable_due_time': next_deliverable_due_time, } elif 'tier' == resource['type']: tiers[resource_id] = { 'id': resource_id, 'client_id': client.id_bytes, 'campaign_id': campaign_id, 'title': str(resource['attributes']['title']), 'amount_cents': int(resource['attributes']['amount_cents']), 'unpublished': 0, } if ('unpublished_at' in resource['attributes'] and resource['attributes']['unpublished_at']): tiers[resource_id]['unpublished'] = 1 members = {} # first page response = { 'links': { 'next': ('https://www.patreon.com/api/oauth2/v2/campaigns/' + str(campaign_id) + '/members' + '?include=' + 'currently_entitled_tiers' + ',user' + '&' + urllib.parse.quote_plus('fields[user]') + '=' + 'full_name' + '&' + urllib.parse.quote_plus('fields[member]') + '=' + 'patron_status' + ',pledge_relationship_start' + ',lifetime_support_cents' + ',currently_entitled_amount_cents' + ',last_charge_date' + ',last_charge_status') } } while ('links' in response and 'next' in response['links'] and response['links']['next']): response = self.client_request(response['links']['next'], client, redirect_uri) if ('data' not in response or 'included' not in response): continue user_ids_to_names = {} for resource in response['included']: if ('id' not in resource or 'type' not in resource or 'user' != resource['type'] or 'attributes' not in resource or 'full_name' not in resource['attributes']): continue resource_id = int(resource['id']) user_ids_to_names[resource_id] = resource['attributes'][ 'full_name'] for member in response['data']: if ('id' not in member or 'type' not in member or 'member' != member['type'] or 'relationships' not in member or 'user' not in member['relationships'] or 'data' not in member['relationships']['user'] or 'user' != member['relationships']['user']['data']['type'] or 'attributes' not in member or 'currently_entitled_amount_cents' not in member['attributes'] or 'last_charge_date' not in member['attributes'] or 'last_charge_status' not in member['attributes'] or 'lifetime_support_cents' not in member['attributes'] or 'patron_status' not in member['attributes'] or 'active_patron' != member['attributes']['patron_status'] or 'pledge_relationship_start' not in member['attributes']): continue # parse member uuid member_uuid = uuid.UUID(member['id']) member_id, member_id_bytes = parse_id(member_uuid.bytes) if ('currently_entitled_tiers' not in member['relationships'] or 'data' not in member['relationships']['currently_entitled_tiers'] or not member['relationships'] ['currently_entitled_tiers']['data'] or 'tier' != member['relationships'] ['currently_entitled_tiers']['data'][0]['type'] or 'id' not in member['relationships'] ['currently_entitled_tiers']['data'][0]): tier_id = 0 else: tier_id = int( member['relationships']['currently_entitled_tiers'] ['data'][0]['id']) user_id = int( member['relationships']['user']['data']['id']) if tier_id < 1: tier_id = 0 # parse string datetime into timestamp try: parsed = dateutil.parser.parse( member['attributes']['pledge_relationship_start']) except ValueError: pledge_relationship_start_time = 0 except TypeError: pledge_relationship_start_time = 0 else: pledge_relationship_start_time = int( parsed.timestamp()) try: parsed = dateutil.parser.parse( member['attributes']['last_charge_date']) except ValueError: last_charge_time = 0 except TypeError: last_charge_time = 0 else: last_charge_time = int(parsed.timestamp()) members[member_id] = { 'id': member_id_bytes, 'client_id': client.id_bytes, 'campaign_id': campaign_id, 'tier_id': tier_id, 'user_id': user_id, 'amount_cents': int(member['attributes'] ['currently_entitled_amount_cents']), 'last_charge_time': last_charge_time, 'last_charge_status': str(member['attributes']['last_charge_status']), 'lifetime_support_cents': int(member['attributes']['lifetime_support_cents']), 'pledge_relationship_start_time': pledge_relationship_start_time, 'name': '', } if user_id in user_ids_to_names: members[member_id]['name'] = user_ids_to_names[user_id] # get existing member last_fulfill_time values to add before insertion fulfilled_members = self.search_members( filter={'last_fulfilled_after': 0}) # delete stale info and insert new infor for this client for table in ['benefits', 'tiers', 'members']: self.connection.execute( getattr(self, table).delete().where( getattr(self, table).c.client_id == client.id_bytes)) if benefits: self.connection.execute(self.benefits.insert().values( list(benefits.values()))) if tiers: self.connection.execute(self.tiers.insert().values( list(tiers.values()))) if members: # not using indexed dict for insert members = list(members.values()) # add last_fulfill_time to members that were previously fulfilled for member in members: if member['id'] in fulfilled_members: fulfilled_member = fulfilled_members.get(member['id']) member[ 'last_fulfill_time'] = fulfilled_member.last_fulfill_time # sqlite needs smaller chunks for mass insertion i = 0 per_insert = 50 while members: current_chunk = members[:50] self.connection.execute( self.members.insert().values(current_chunk)) del members[:50]