def update(self, fields, updateDbs=True): from server.models.ssuser import SSUser if fields.get("content"): self.content = fields.get("content") if fields.get("summary"): self.summary = self.content["summary"] = fields.get("summary") if fields.get("broken"): self.broken = fields.get("broken") if fields.get("dbs"): self.dbs = list(set(self.dbs.extend(fields.get("dbs")))) self.modified = datetime.now() # update the correct user db if self.publishData.private: db = SSUser.privateDb(self.createdBy) else: db = SSUser.publicDb(self.createdBy) self.store(core.connect(db)) core.replicate(db, "shiftspace/shared") # update followers and groups if updateDbs: for db in self.publishData.dbs: dbtype, dbid = db.split("/") if dbtype == "group": from server.models.group import Group Group.read(dbid).updateShift(self) return Shift.joinData(self, self.createdBy)
def publish(self, id): # NOTE: should maybe take publishData url parameter - David 9/5/2009 loggedInUser = helper.getLoggedInUser() theShift = Shift.read(id, loggedInUser) if not theShift: return error("Resource does not exist.", ResourceDoesNotExistError) if theShift.type != "shift": return error("Resource is not of type shift", ResourceTypeError) publishData = json.loads(helper.getRequestBody()) # convert targets to actual database references if publishData.get("targets"): from server.models.group import Group from server.models.ssuser import SSUser theUser = SSUser.read(loggedInUser) targets = publishData["targets"] # convert short names to group ids shortNames = [target[1:] for target in targets if target[0] == "&"] groupIds = Group.shortNamesToIds(shortNames) # convert user name to user ids userNames = [target[1:] for target in targets if target[0] == "@"] userIds = SSUser.namesToIds(userNames) # create list of dbs being published to dbs = [Group.db(groupId) for groupId in groupIds] dbs.extend([SSUser.db(userId) for userId in userIds]) # validate writeable = theUser.writeable() if set(writeable) != set(dbs): return error("Operation not permitted. You don't have permission to publish to some of these gruops", PermissionError) publishData["dbs"] = dbs return data(theShift.publish(publishData))
def publish(self, id): # NOTE: should maybe take publishData url parameter - David 9/5/2009 loggedInUser = helper.getLoggedInUser() theShift = Shift.read(id, loggedInUser) if not theShift: return error("Resource does not exist.", ResourceDoesNotExistError) if theShift.type != "shift": return error("Resource is not of type shift", ResourceTypeError) publishData = json.loads(helper.getRequestBody()) # convert targets to actual database references if publishData.get("targets"): from server.models.group import Group from server.models.ssuser import SSUser theUser = SSUser.read(loggedInUser) targets = publishData["targets"] # convert short names to group ids shortNames = [target[1:] for target in targets if target[0] == "&"] groupIds = Group.shortNamesToIds(shortNames) # convert user name to user ids userNames = [target[1:] for target in targets if target[0] == "@"] userIds = SSUser.namesToIds(userNames) # create list of dbs being published to dbs = [Group.db(groupId) for groupId in groupIds] # validate groups writeable = theUser.writeable() if not set(dbs).issubset(set(writeable)): return error( "Operation not permitted. You don't have permission to publish to some of these groups", PermissionError) # TODO: validate against blocked users - David 2/15/10 dbs.extend([SSUser.db(userId) for userId in userIds]) publishData["dbs"] = dbs return data(theShift.publish(publishData))
def setup_user_by_token(): user_token = request.headers.get('X-Wigo-User-Key') if user_token: try: user_id = get_user_id_for_key(user_token) # the current user should always get a fresh copy of themself model_cache.invalidate(user_id) user = User.find(user_id) g.user = user group = getattr(g, 'group', None) if group and not user.location_locked: # if a group was passed in via geo, switch the users group user.group_id = group.id elif user.group_id: # if the user has a group defined, use it group = Group.find(user.group_id) g.group = group else: # we need a group, so default to boston group = Group.find(code='boston') user.group_id = group.id g.group = group if not user.location_locked and hasattr(g, 'latitude') and hasattr( g, 'longitude'): if user.modified <= (datetime.utcnow() - timedelta(minutes=30)): user.latitude = round(g.latitude, 3) user.longitude = round(g.longitude, 3) platform = request.headers.get('X-Wigo-Device') if not platform: platform = request.user_agent.platform if platform: platform = platform.lower() if platform in ('android', 'iphone', 'ipad'): user.set_custom_property('platforms', [platform]) if user.is_changed(): user.save() agent.add_custom_parameter('user_id', user.id) if user.group_id: agent.add_custom_parameter('group_code', group.code) except DoesNotExist: pass
def create_group(self, group_name: str, group_pin: str) -> Group: if group_name in self.data.group: raise DuplicateResourceIdException(group_name, 'group') g = Group(group_name, group_pin, date.today()) self.data.group[group_name] = g self.save() return g
def post(self): json = request.get_json() if 'city_id' in json: group = get_group_by_city_id(json['city_id']) elif 'group_id' in json: group = Group.find(json['group_id']) else: group = g.group try: event = Event({ 'name': json.get('name'), 'group_id': group.id, 'owner_id': g.user.id, 'privacy': json.get('privacy') or 'public' }) if 'date' in json: date = parse(json.get('date')) event.date = group.get_day_start(date) event.expires = group.get_day_end(date) else: event.date = group.get_day_start() event.expires = group.get_day_end() if 'latitude' in json: event.latitude = json.get('latitude') if 'longitude' in json: event.longitude = json.get('longitude') event.save() return self.serialize_list(Event, [event]) except AlreadyExistsException, e: return self.handle_already_exists_exception(e)
def addToGroups(self, groupDbs): from server.models.group import Group # NOTE - do we need to delete from user/private? - David 11/12/09 for db in groupDbs: dbtype, dbid = db.split("/") theGroup = Group.read(dbid) theGroup.addShift(self)
def create(cls, userId, groupId, otherId, level): from server.models.ssuser import SSUser from server.models.group import Group db = core.connect() if not groupId: raise MissingGroupError if not userId: raise MissingCreatorError if Permission.readByUserAndGroup(otherId, groupId): raise PermissionAlreadyExistsError adminable = [ row.value for row in Permission.by_adminable(db, key=userId).rows ] allowed = groupId in adminable if not allowed: theUser = SSUser.read(userId) allowed = theUser.isAdmin() if not allowed: theGroup = Group.read(groupId) allowed = theUser.isOwnerOf(theGroup) if not allowed: raise CreateEventPermissionError json = { "createdBy": userId, "userId": otherId, "groupId": groupId, "level": level } newPermission = Permission(**utils.clean(json)) newPermission.store(db) return newPermission
def create(cls, userId, groupId, otherId, level): from server.models.ssuser import SSUser from server.models.group import Group db = core.connect() if not groupId: raise MissingGroupError if not userId: raise MissingCreatorError if Permission.readByUserAndGroup(otherId, groupId): raise PermissionAlreadyExistsError adminable = [row.value for row in Permission.by_adminable(db, key=userId).rows] allowed = groupId in adminable if not allowed: theUser = SSUser.read(userId) allowed = theUser.isAdmin() if not allowed: theGroup = Group.read(groupId) allowed = theUser.isOwnerOf(theGroup) if not allowed: raise CreateEventPermissionError json = { "createdBy": userId, "userId": otherId, "groupId": groupId, "level": level } newPermission = Permission(**utils.clean(json)) newPermission.store(db) return newPermission
def adminable(cls, userId, dbname=True): from server.models.group import Group db = core.connect() ids = core.values(Permission.by_adminable(db, key=userId)) if dbname: return [Group.db(id) for id in ids] else: return ids
def apply(self, query, name, value): if name == 'group_code': try: group = Group.find(code=value) return query.group(group) except DoesNotExist: pass return query.where(**{name: value})
def create(self): loggedInUser = helper.getLoggedInUser() jsonData = helper.getRequestBody() if jsonData != "": theData = json.loads(jsonData) theData['createdBy'] = loggedInUser return data(Group.create(theData)) else: return error("No data for group.", NoDataError)
def members(self, id): from server.models.ssuser import SSUser theGroup = Group.read(id) loggedInUser = helper.getLoggedInUser() theUser = SSUser.read(loggedInUser) if theUser.isAdminOf(theGroup): return data(theGroup.members()) else: return error("You don't have permission to view this groups members", PermissionError)
def join(self, id): from server.models.ssuser import SSUser theGroup = Group.read(id) loggedInUser = helper.getLoggedInUser() theUser = SSUser.read(loggedInUser) if theUser.canJoin(theGroup): theUser.join(theGroup) return data(theGroup) else: return error("Operation not permitted. You don't have permission to join this group.", PermissionError)
def get(self, group_id): group = Group.find(group_id) cities = get_close_cities(group.latitude, group.longitude) return [{ 'city_id': c.city_id, 'name': c.name, 'population': int(c.population) } for c in cities]
def makeAdmin(self, id, userId): from server.models.ssuser import SSUser theGroup = Group.read(id) theUser = SSUser.read(helper.getLoggedInUser()) if theUser.isAdminOf(theGroup): otherUser = SSUser.read(userId) theGroup.setPrivilege(otherUser, 3) return ack else: return error("You don't have permission to promote members of this group to admin.", PermissionError)
def testPublishToGroupAndUser(self): json = shiftJson() json["createdBy"] = self.fakemary.id newShift = Shift.create(json) json = groupJson() json["createdBy"] = self.fakemary.id newGroup = Group.create(json) newPerm = Permission.create("shiftspace", newGroup.id, self.fakejohn.id, level=1) publishData = { "dbs": [Group.db(newGroup.id), SSUser.db(self.fakebob.id)] } newShift.publish(publishData) # should exist in subscriber's feed db = core.connect("shiftspace/shared") theShift = Shift.load(db, newShift.id) self.assertEqual(theShift.summary, newShift.summary) newGroup.delete() # should exist in shiftspace/shared # TODO: inbox if user is peer - David 11/18/09 theShift = Shift.load(core.connect("shiftspace/shared"), newShift.id) self.assertEqual(theShift.summary, newShift.summary)
def create_group(self, group_name, group_pin) -> Group: g = Group(group_name, group_pin, date.today()) sql = ''' INSERT INTO `groups` (group_name, group_pin, date_created) VALUE (%s, %s, %s); ''' val = (g.group_name, g.group_pin, g.date_created) try: self._execute_insert_query(sql, val) except Exception as e: raise ResourceAccessException(g.group_name, e) return g
def inviteUsers(self, id, users): from server.models.ssuser import SSUser loggedInUser = helper.getLoggedInUser() groupAdmin = SSUser.read(loggedInUser) theGroup = Group.read(id) if groupAdmin.isAdminOf(theGroup): db = core.connect() users = SSUser.all(db, keys=json.loads(users)) for user in users: groupAdmin.inviteUser(theGroup, user) return data(theGroup) else: return error("Operation not permitted. You don't have permission to modify this group", PermissionError)
def update(self, id): from server.models.ssuser import SSUser loggedInUser = helper.getLoggedInUser() theUser = SSUser.read(loggedInUser) theGroup = Group.read(id) jsonData = helper.getRequestBody() if jsonData != "": if theUser.isAdminOf(theGroup): groupData = json.loads(jsonData) return data(theGroup.update(groupData)) else: return error("You don't have permission to update this group", PermissionError) else: return error("No data for group.", NoDataError)
def testPublishToGroup(self): json = shiftJson() json["createdBy"] = self.fakemary.id newShift = Shift.create(json) json = groupJson() json["createdBy"] = self.fakemary.id newGroup = Group.create(json) # make sure fakemary owns the group newPerm = Permission.readByUserAndGroup(self.fakemary.id, newGroup.id) self.assertTrue(newPerm.level == 4) # create read permission for fakejohn newPerm = Permission.create("shiftspace", newGroup.id, self.fakejohn.id, level=1) fakejohn = SSUser.read(self.fakejohn.id) self.assertTrue(Group.db(newGroup.id) in fakejohn.readable()) publishData = { "dbs": [Group.db(newGroup.id)] } newShift.publish(publishData) # should exists in shiftspace/shared db = core.connect("shiftspace/shared") theShift = Shift.load(db, newShift.id) self.assertEqual(theShift.summary, newShift.summary) newGroup.delete()
def info(self, id): from server.models.ssuser import SSUser # TODO: bulk call - David 12/13/2009 theGroup = Group.read(id) memberCount = theGroup.memberCount() adminCount = theGroup.adminCount() shiftCount = theGroup.shiftCount() info = { "memberCount": memberCount, "adminCount": adminCount, "shiftCount": shiftCount } theUser = SSUser.read(helper.getLoggedInUser()) info["isAdmin"] = theUser.isAdminOf(theGroup) return data(info)
def test_update_user_group(): from server.models.group import Group from server.models.user import User with client() as c: user = User.find(key='test') g = user.group user.group_id = Group.find(code='san_diego').id user.save() assert User.find(key='test').group.name == 'San Diego' # posting with geo should change the users group resp = api_post(c, user, '/api/users/me', { 'bio': '321' }, lat=42.3584, lon=-71.0598) assert User.find(key='test').group.name == 'Boston'
def deletedbs(): """ Delete the databases, the database path must have been set first for this to work. """ from server.models import core from server.models.ssuser import SSUser from server.models.group import Group # delete all core dbs and user and group dbs server = core.server() [group.delete() for group in core.objects(Group.all(core.connect()))] [user.delete() for user in core.objects(SSUser.all(core.connect()))] del server["shiftspace/public"] del server["shiftspace/shared"] del server["shiftspace/messages"] del server["shiftspace/master"] #[comment.deleteInstance() for comment in core.object(Comment.all(core.connect()))] # cleanup, remove any empty folders (left from deleted users try: fh = open("config/conf.json") except: print "config/conf.json does not exist. Set the path the database first." sys.exit(2) conf = json.loads(fh.read()) if conf.get("dbpath"): userdbdir = os.path.join(conf["dbpath"], "user") if os.path.exists(userdbdir): for file in os.listdir(userdbdir): filepath = os.path.join(userdbdir, file) if os.path.isdir(filepath): os.rmdir(filepath) os.rmdir(userdbdir) grpdbdir = os.path.join(conf["dbpath"], "group") if os.path.exists(grpdbdir): os.rmdir(grpdbdir) ssdbdir = os.path.join(conf["dbpath"], "shiftspace") if os.path.exists(ssdbdir): os.rmdir(ssdbdir)
def send_friend_invites(user_id, event_id): try: user = User.find(user_id) event = Event.find(event_id) except DoesNotExist: return if event.is_expired or not user.is_attending(event): return groups = {} for friend in user.friends_iter(): if wigo_db.sorted_set_is_member( skey('event', event_id, user, 'invited'), friend.id): continue if friend.group_id: friend_group = groups.get(friend.group_id) if friend_group is None: friend_group = Group.find(friend.group_id) groups[friend.group_id] = friend_group distance = Location.getLatLonDistance( (user.group.latitude, user.group.longitude), (friend_group.latitude, friend_group.longitude)) if distance > 160: # > 160km, 100 miles continue try: invite = Invite() invite.user_id = user.id invite.invited_id = friend.id invite.event_id = event_id invite.save() except ValidationException, e: logger.warn('error creating invite, {}'.format(e.message))
def new_group(group_id): group = Group.find(group_id) logger.info('new group {} created, importing events'.format( group.name.encode('utf-8'))) num_imported = 0 imported = set() min = epoch(group.get_day_end() - timedelta(days=7)) with wigo_db.transaction(commit_on_select=False): for close_group in get_close_groups(group.latitude, group.longitude, 100): if close_group.id == group.id: continue for event in Event.select().group(close_group).min(min): # only import the events the group actually owns if event.group_id != close_group.id: continue # no double imports if event.id not in imported: event.update_global_events(group=group) imported.add(event.id) num_imported += 1 for event in Event.select().key(skey('global', 'events')).min(min): if event.id not in imported: event.update_global_events(group=group) imported.add(event.id) num_imported += 1 logger.info('imported {} events into group {}'.format( num_imported, group.name.encode('utf-8'))) group.track_meta('last_event_change', expire=None) group.status = 'active' group.save()
def client(): from web import app from server.db import wigo_db, redis from server.models.group import Group from server.models.user import User from server.models import model_cache from server.models import cache_maker as model_cache_maker from server.models.group import cache_maker as group_cache_maker from server.rest import cache_maker as rest_cache_maker from server.models.location import WigoCity assert isinstance(wigo_db.redis, MockRedis) assert isinstance(redis, MockRedis) logging.getLogger().setLevel(level=logging.FATAL) logging.getLogger('web').setLevel(level=logging.FATAL) logging.getLogger('wigo').setLevel(level=logging.FATAL) model_cache.clear() group_cache_maker.clear() model_cache_maker.clear() rest_cache_maker.clear() app.debug = True wigo_db.redis.flushdb() def zscan_iter(name, match=None, count=None): cursor = '0' while cursor != 0: cursor, data = wigo_db.redis.zscan(name, cursor=cursor, match=match, count=count) for item in data: yield item wigo_db.redis.zscan_iter = zscan_iter def new_id(): global NEXT_ID next_id = NEXT_ID NEXT_ID += 1 return next_id wigo_db.gen_id = new_id city = WigoCity(city_id=4930956, name='Boston', lat=42.3584, lon=-71.0598) city.save(wigo_db.redis) city = WigoCity(city_id=5391811, name='San Diego', lat=32.7153, lon=-117.157) city.save(wigo_db.redis) boston = Group({ 'name': 'Boston', 'code': 'boston', 'city_id': 4930956, 'latitude': 42.3584, 'longitude': -71.0598 }).save() san_diego = Group({ 'name': 'San Diego', 'code': 'san_diego', 'city_id': 5391811, 'latitude': 32.7153, 'longitude': -117.157 }).save() u = User({ 'username': '******', 'group_id': boston.id, 'facebook_id': 'xxx1', 'facebook_token': 'xxx1', 'facebook_token_expires': datetime.utcnow() + timedelta(days=7), 'email': '*****@*****.**', 'key': 'test' }).save() u = User({ 'username': '******', 'group_id': boston.id, 'facebook_id': 'xxx2', 'facebook_token': 'xxx2', 'facebook_token_expires': datetime.utcnow() + timedelta(days=7), 'email': '*****@*****.**', 'key': 'test2' }).save() u = User({ 'username': '******', 'group_id': boston.id, 'facebook_id': 'xxx3', 'facebook_token': 'xxx3', 'facebook_token_expires': datetime.utcnow() + timedelta(days=7), 'email': '*****@*****.**', 'key': 'test3' }).save() with app.test_client() as client: yield client
def updateInGroups(self, groupDbs): from server.models.group import Group for db in groupDbs: dbtype, dbid = db.split("/") theGroup = Group.read(dbid) theGroup.updateShift(self)
def read(self, id): # return only public groups theGroup = Group.read(id) return data(theGroup)
def group(self): if self.group_id: from server.models.group import Group return Group.find(self.group_id) return None
def setup_request(): g.user = None g.group = None if is_request_secure( ) and request.environ.get('wsgi.url_scheme') != 'https': request.environ['wsgi.url_scheme'] = 'https' if request.path.startswith('/api/swagger') or request.path.startswith( '/admin'): agent.ignore_transaction() api_key = request.headers.get('X-Wigo-API-Key') if not api_key and 'key' in request.args: request.args = request.args.copy() api_key = request.args.pop('key') if api_key: g.api_key = api_key api_version = request.headers.get('X-Wigo-API-Version') if not api_version: api_version = '1000000000.0.0' try: g.api_version = Version(api_version) except: raise ValidationException('Invalid version number', 'X-Wigo-API-Version') # check api key auth if request.path.startswith('/api/hooks/'): # webhooks do their own auth pass elif request.path.startswith('/api/swagger'): pass elif request.path.startswith('/api') and api_key != app.config['API_KEY']: abort(403, message='Bad API key') # resolve by lat/long geolocation = request.headers.get('Geolocation') if geolocation: parsed_geo = urlparse(geolocation) if parsed_geo.scheme == 'geo': lat, lon = parsed_geo.path.split(',') lat, lon = float(lat), float(lon) if lat and lon: g.latitude, g.longitude = float(lat), float(lon) try: g.group = Group.find(lat=g.latitude, lon=g.longitude) except DoesNotExist: logger.info( 'could not resolve group from geo, lat={}, lon={}'. format(g.latitude, g.longitude)) city_id = request.headers.get('X-Wigo-City-ID') if city_id: g.group = Group.find(city_id=int(city_id)) group_id = request.headers.get('X-Wigo-Group-ID') if group_id: g.group = Group.find(int(group_id)) # setup the user after the geo lookup, since the user might need to update its group setup_user_by_token()
def testGroupDb(self): json = groupJson() json["createdBy"] = self.fakemary newGroup = Group.create(json) self.assertEqual(Group.db(newGroup.id), "group/%s" % newGroup.id) newGroup.deleteInstance()
def groups(self, start=None, end=None, limit=25): loggedInUser = helper.getLoggedInUser() return data(Group.groups(start, end, limit, userId=loggedInUser))
def event_related_change(group_id, event_id, is_global=False, deleted=False): from server.db import redis lock = redis.lock('locks:group_event_change:{}:{}'.format( group_id, event_id), timeout=120) if lock.acquire(blocking=False): try: agent.add_custom_parameter('group_id', group_id) logger.debug('recording event change in group {}'.format(group_id)) if not deleted: try: event = Event.find(event_id) event.deleted = False except DoesNotExist: event = Event({'id': event_id, 'group_id': group_id}) event.deleted = True else: event = Event({'id': event_id, 'group_id': group_id}) event.deleted = True group = Group.find(group_id) with wigo_db.transaction(commit_on_select=False): # add to the time in case other changes come in while this lock is taken, # or in case the job queues get backed up group.track_meta('last_event_change', time() + EVENT_CHANGE_TIME_BUFFER) if is_global or event.is_global: groups_to_add_to = get_all_groups() else: radius = 100 population = group.population or 50000 if population < 60000: radius = 40 elif population < 100000: radius = 60 groups_to_add_to = get_close_groups( group.latitude, group.longitude, radius) num_visited = 0 for group_to_add_to in groups_to_add_to: if group_to_add_to.id == group.id: continue # index this event into the close group if event.deleted is False: event.update_global_events(group=group_to_add_to) else: event.remove_index(group=group_to_add_to) # track the change for the group group_to_add_to.track_meta( 'last_event_change', time() + EVENT_CHANGE_TIME_BUFFER) num_visited += 1 if (num_visited % 25) == 0: lock.extend(30) finally: lock.release()
def __get_group(self): try: group = Group.find(lat=self._lat, lon=self._lon) return 1, self._page, [group] except: return 0, self._page, []
def get(self, group_id): group = Group.find(group_id) groups = get_close_groups(group.latitude, group.longitude) return self.serialize_list(self.model, groups)