class Organization(mongo.Document): id = mongo.UUIDField(required=False) name = mongo.StringField(max_length=255, required=True, unique=True) address = mongo.StringField(max_length=255, required=False) city = mongo.StringField(max_length=255, required=False) state = mongo.StringField(max_length=255, required=False) zip = mongo.StringField(max_length=50, required=False) point = mongo.PointField(required=False) phone = mongo.StringField(max_length=20, required=False) website = mongo.StringField(required=False) description = mongo.StringField(required=False) privacy = mongo.StringField(required=True, default='') last_updated = mongo.DateTimeField(required=True, default=datetime.datetime.now()) created_by = mongo.StringField(required=False) def save(self, *args, **kwargs): self.last_updated = datetime.datetime.now() address = (self.address + ", " + self.zip, self.city, self.state) geocoded_addr = verify_address(address) if geocoded_addr != 'Error': self.point = geocoded_addr super(Organization, self).save(*args, **kwargs) def location_count(self): return Location.objects.filter(org=str(self.id)).count() def location_set(self): return Location.objects.filter(org=str(self.id))
class DataSourceMongoDocument(MongoDocument): meta = {'allow_inheritance': True, 'abstract': True, 'indexes': ['datetime', 'user']} user = mongo.ReferenceField(User, reverse_delete_rule=mongo.CASCADE) datetime: datetime = mongo.DateTimeField(default=datetime.utcnow) coordinates = mongo.PointField() geohash = mongo.StringField() @abstractmethod def compute_happiness_level(self): pass @property def data_source(self): raise NotImplementedError def save(self, **kwargs): super().save(**kwargs) start_date = self.datetime.date() end_date = start_date + timedelta(days=1) happiness_levels = [emotions_result.compute_happiness_level() for emotions_result in self.__class__.objects(user=self.user, datetime__gte=start_date, datetime__lt=end_date)] average = np.mean(happiness_levels) try: happiness = HappinessLevel.objects.get(user=self.user, date__gte=start_date, date__lt=end_date) except mongo.DoesNotExist: happiness = HappinessLevel(user=self.user, date=start_date) setattr(happiness, f'{self.data_source}_happiness_level', average) happiness.compute_mean()
class Event(Base): name = mongoengine.StringField() description = mongoengine.StringField() location = mongoengine.PointField() date_time = mongoengine.DateTimeField() categories = mongoengine.ListField(mongoengine.ReferenceField(Category)) def __init__(self, name='', description='', location=(0, 0), date_time=0, categories=None, *args, **values): super().__init__(*args, **values) self.name = name self.description = description self.location = location self.date_time = date_time self.categories = categories def to_json(self, *args, **kwargs): cats = [str(cat_ref.id) for cat_ref in self.categories] return { "id": str(self.id), "name": self.name, "description": self.description, "location": self.location, "date_time": self.date_time, "category_ids": cats, }
class Fan(mclient.DynamicDocument): _id = mclient.ObjectIdField() user = mclient.StringField(max_length=255) currentPosition = mclient.PointField() friends = mclient.ListField(mclient.StringField(max_length=100)) posts = mclient.EmbeddedDocumentListField(document_type=Post) meta = {'collection': 'Fans'}
class Locations(mongoengine.Document): location = mongoengine.PointField() moisture_data = mongoengine.ListField( mongoengine.ReferenceField(SoilMoisture, required=True)) time = mongoengine.DateTimeField(default=datetime.datetime.now) def __unicode__(self): return "{} {} {}".format(self.location, self.moisture_data, self.time)
def findServiceArea(request, x, y, format=None): point = mongoengine.PointField([x, y]) for loc in GeoJsonPolygon.objects.all(): locations = loc.objects(point__geo_within=loc.geometry) return Response({"locations": locations})
class User(me.Document): DEFAULT_CONFIG = { # These match up with keys in praytimes.py "method": "ISNA", "asr": "Standard", # These don't "prayer_names": "standard" } user_token = me.StringField() timeline_token = me.StringField() location = me.PointField() location_geoname = me.StringField() tz_offset = me.IntField() created_at = me.DateTimeField() subscribed_at = me.DateTimeField() # It melted down when I tried name the db field "config" # Not sure what was up _sparse_config = me.DictField(db_field="sparse_config") def geocode(self): import requests res = requests.get('http://api.geonames.org/findNearbyPlaceNameJSON', params={ 'lat': self.location[1], 'lng': self.location[0], 'cities': 'cities1000', 'maxRows': 1, 'username': os.environ.get('GEONAMES_USERNAME', 'demo') }) for place in res.json()["geonames"]: self.location_geoname = place["name"] @property def config(self): if not hasattr(self, "_config_inst"): self._config_inst = dict(self.DEFAULT_CONFIG) self._config_inst.update(self._sparse_config) return self._config_inst def save(self): # Paste _config_inst back into _sparse_config if reqd. if hasattr(self, "_config_inst"): # Transfer updated keys if not default for k, v in self._config_inst.items(): if self.DEFAULT_CONFIG[k] != v: self._sparse_config[k] = v elif k in self._sparse_config: del self._sparse_config[k] # Remove deleted keys for k, v in self.DEFAULT_CONFIG.items(): if k not in self._config_inst and k in self._sparse_config: del self._sparse_config[k] super(User, self).save()
class Pano(me.Document): location = me.StringField() longlat = me.PointField(auto_index=True) # note that the coordinates are (long, lat) pairs heading = me.FloatField(default=None) fov = me.FloatField(default=90) pitch = me.FloatField(default=0) pano_id = me.StringField() image = me.ImageField() meta = { 'indexes': ['pano_id'] } @property def size(self): return self.image.size @property def url(self): loc = self.location or self.longlat return generate_pano_url(loc, self.heading, self.fov, self.pitch, self.size, self.pano_id) @property def image_md5(self): '''return the md5 hash of the stored image''' if self.image: return self.image.md5 return None @property def PIL_image(self): if hasattr(self, '_PIL_image'): return self._PIL_image self._PIL_image = Image.open(cStringIO.StringIO(self.image.read())) return self._PIL_image @property def has_image(self): '''return False if the image is a null image''' return self.image_md5 != no_image_md5 def show_image(self): return self.PIL_image.show() def __unicode__(self): return 'location=%r, longlat=%r, image_md5=%r' % (self.location, self.longlat['coordinates'], self.image_md5) @staticmethod def new_pano(location=None, heading=None, fov=90, pitch=0, size=(640, 640), pano_id=None, key=None): image = cStringIO.StringIO(get_pano(location, heading, fov, pitch, size, pano_id, key)) params = dict(heading=heading, fov=fov, pitch=pitch, size=size, pano_id=pano_id, image=image) if isinstance(location, str): pano = Pano(location=location, **params) else: # location is provided as a (long, lat) pair pano = Pano(longlat=location, **params) return pano
class SampleSpecies(mongoengine.Document): original_id = mongoengine.StringField(required=True) smarter_id = mongoengine.StringField(required=True, unique=True) country = mongoengine.StringField(required=True) species = mongoengine.StringField(required=True) breed = mongoengine.StringField(required=True) breed_code = mongoengine.StringField(min_length=3) # required to search a sample relying only on original ID dataset = mongoengine.ReferenceField(Dataset, db_field="dataset_id", reverse_delete_rule=mongoengine.DENY) # track the original chip_name with sample chip_name = mongoengine.StringField() # define enum types for sex sex = mongoengine.EnumField(SEX) # GPS location # NOTE: X, Y where X is longitude, Y latitude location = mongoengine.PointField() # additional (not modelled) metadata metadata = mongoengine.DictField(default=None) # for phenotypes phenotype = mongoengine.EmbeddedDocumentField(Phenotype, default=None) meta = { 'abstract': True, } def save(self, *args, **kwargs): """Custom save method. Deal with smarter_id before save""" if not self.smarter_id: logger.debug(f"Determining smarter id for {self.original_id}") # get the pymongo connection object conn = mongoengine.connection.get_db(alias=DB_ALIAS) # even is species, country and breed are required fields for # SampleSpecies document, their value will not be evaluated until # super().save() is called. I can't call it before determining # a smarter_id self.smarter_id = getSmarterId(self.species, self.country, self.breed, conn) # default save method super(SampleSpecies, self).save(*args, **kwargs) def __str__(self): return f"{self.smarter_id} ({self.breed})"
class Map(mongoengine.Document): robot_name = mongoengine.StringField() resolution = mongoengine.DecimalField(min_value=0) height = mongoengine.IntField(min_value=0) width = mongoengine.IntField(min_value=0) b64_image = mongoengine.StringField() raw_map = mongoengine.StringField() origin = mongoengine.PointField() origin_angle_shift = mongoengine.FloatField() shift_x = mongoengine.FloatField() shift_y = mongoengine.FloatField()
class POI(me.Document): """ Implements a point of interest. """ name = me.StringField(required=True) point = me.PointField(required=True) def __str__(self): return ("'" + self.name + "' (x=" + str(self.point['coordinates'][0]) + ", y=" + str(self.point['coordinates'][1]) + ")")
class User(gj.Document): meta = {'queryset_class': fm.BaseQuerySet} uid = db.StringField() nick_name = db.StringField() sex = db.StringField() birthed_at = db.LongField() height = db.IntField() body_id = db.IntField() occupation = db.StringField() education = db.StringField() religion_id = db.IntField() drink_id = db.IntField() smoking_id = db.IntField() blood_id = db.IntField() r_token = db.StringField() location = db.PointField() introduction = db.StringField() joined_at = db.LongField() last_login_at = db.LongField() job = db.StringField() area = db.StringField() phone = db.StringField() user_images = db.SortedListField(db.EmbeddedDocumentField(UserImage), ordering="index") user_images_temp = db.SortedListField(db.EmbeddedDocumentField(UserImage), ordering="index") charm_ids = db.ListField(db.IntField()) ideal_type_ids = db.ListField(db.IntField()) interest_ids = db.ListField(db.IntField()) available = db.BooleanField(default=False) status = db.IntField(default=0) # -10, 0, 10 @property def posts(self): posts = Post.objects(author=self).order_by("-created_at").all() for post in posts: post.favorite_users = [] post.comments.sort(key=lambda x: x.created_at, reverse=True) return posts def list_requests_like_me(self, response=None): return Request.objects(user_to=self, response=response).all() def list_requests_i_like(self, response=None): return Request.objects(user_from=self, response=response).all() def list_chat_rooms(self): return ChatRoom.objects(members=self).all() def list_users_rated_me_high(self): star_ratings = StarRating.objects(user_to=self, score__gte=4).all() return [star_rating.user_from for star_rating in star_ratings]
class ParkingSpot(mongoengine.Document): meta = {'collection': 'parking_spots'} # schema address = mongoengine.StringField() location = mongoengine.PointField() def fetch(self): """ Fetches doc and parses id :return: doc """ doc = self.to_mongo() doc['_id'] = str(doc['_id']) return doc
class Segment(me.Document): segment_id = me.IntField(unique=True) label = me.StringField() length = me.FloatField() segment = me.LineStringField() # GeoJSON LineString. midpoint = me.PointField() # GeoJSON PointField. vehicles = me.ListField(me.ReferenceField('VehicleSegmentData')) running_average_speed = me.FloatField() running_average_travel_time = me.FloatField() last_updated = me.ComplexDateTimeField() meta = { 'collection': config['MONGODB_SEGMENT_COLLECTION'], 'indexes': ['segment_id', 'last_updated'] }
class Event(Base): name = mongoengine.StringField() description = mongoengine.StringField() location = mongoengine.PointField() date_time = mongoengine.DateTimeField() categories = mongoengine.ListField(mongoengine.ReferenceField(Category)) def __init__(self, name='', description='', location=(0, 0), date_time=0, categories=None, *args, **values): super().__init__(*args, **values) self.name = name self.description = description self.location = location self.date_time = date_time self.categories = categories
class VehicleSegmentData(me.Document): # vehicle data is tied to segment vehicle_id = me.IntField(null=True) vehicle_datetime = me.ComplexDateTimeField() vehicle_location = me.PointField() vehicle_segment_speed = me.FloatField() vehicle_segment_travel_time = me.FloatField() segment = me.ReferenceField('Segment') meta = { 'collection': config['MONGODB_VEHICLE_COLLECTION'], 'indexes': [ 'vehicle_datetime', { "fields": ['vehicle_id'], "unique": True, "partialFilterExpression": { "vehicle_id": { "$type": "int" } } } ] }
class Coordinate(mongoengine.EmbeddedDocument): epsg = mongoengine.StringField(required=True, default='EPSG:2278') coordinate = mongoengine.PointField(required=True)
def test_should_point_convert_field(): graphene_type = convert_mongoengine_field(mongoengine.PointField()) assert isinstance(graphene_type, graphene.Field) assert isinstance(graphene_type.type.type, graphene.String) assert isinstance(graphene_type.type.coordinates, graphene.List)
class Child(Parent): meta = {'collection': 'test_child'} baz = mongoengine.StringField() loc = mongoengine.PointField()
class AnotherChild(Parent): meta = {"collection": "test_parent"} qux = mongoengine.StringField() loc = mongoengine.PointField()
class Jail(me.Document): name = me.StringField(required=True) location = me.PointField(required=True) state = me.StringField()
class Child(Parent): meta = {"collection": "test_child"} baz = mongoengine.StringField() loc = mongoengine.PointField()
class Ride(me.Document): pickup_datetime = me.DateTimeField() dropoff_datetime = me.DateTimeField() pickup_zipcode = me.IntField() pickup_borough = me.StringField() pickup_county = me.StringField() pickup_long_lat = me.PointField() dropoff_zipcode = me.IntField() dropoff_borough = me.StringField() dropoff_county = me.StringField() dropoff_long_lat = me.PointField() total_amount = me.FloatField() fare_amount = me.FloatField() tip_amount = me.FloatField() passenger_count = me.IntField() trip_distance = me.FloatField() @me.queryset_manager def pickups_nearby(doc_cls, queryset, long, lat, distance): return queryset.filter( pickup_long_lat__near=[long, lat], pickup_long_lat__max_distance=distance).order_by( '-pickup_datetime') @me.queryset_manager def dropoffs_nearby(doc_cls, queryset, long, lat, distance): return queryset.filter( dropoff_long_lat__near=[long, lat], dropoff_long_lat__max_distance=distance).order_by( '-dropoff_datetime') def to_json(self): response = { "pickup_datetime": self.pickup_datetime, "dropoff_datetime": self.dropoff_datetime, "pickup_zipcode": self.pickup_zipcode, "pickup_borough": self.pickup_borough, "pickup_county": self.pickup_county, "pickup_long_lat": self.pickup_long_lat['coordinates'], "dropoff_zipcode": self.dropoff_zipcode, "dropoff_borough": self.dropoff_borough, "dropoff_county": self.dropoff_county, "dropoff_long_lat": self.dropoff_long_lat['coordinates'], "total_amount": self.total_amount, "fare_amount": self.fare_amount, "tip_amount": self.tip_amount, "passenger_count": self.passenger_count, "trip_distance": self.trip_distance } return response def to_series(self): response = pd.Series({ "pickup_datetime": self.pickup_datetime, "dropoff_datetime": self.dropoff_datetime, "pickup_zipcode": self.pickup_zipcode, "pickup_borough": self.pickup_borough, "pickup_county": self.pickup_county, "pickup_long_lat": self.pickup_long_lat['coordinates'], "dropoff_zipcode": self.dropoff_zipcode, "dropoff_borough": self.dropoff_borough, "dropoff_county": self.dropoff_county, "dropoff_long_lat": self.dropoff_long_lat['coordinates'], "total_amount": self.total_amount, "fare_amount": self.fare_amount, "tip_amount": self.tip_amount, "passenger_count": self.passenger_count, "trip_distance": self.trip_distance }) return response meta = { 'indexes': [[("pickup_long_lat", "2dsphere"), ("pickup_datetime", 1)], [("dropoff_long_lat", "2dsphere"), ("dropoff_datetime", 1)], [("pickup_datetime", 1), ("pickup_borough", 1), ("pickup_zipcode", 1)], [("dropoff_datetime", 1), ("dropoff_borough", 1), ("dropoff_zipcode", 1)], [("pickup_datetime", 1), ("pickup_long_lat", "2dsphere")], [("dropoff_datetime", 1), ("dropoff_long_lat", "2dsphere")]], 'collection': 'rides_15' }
class MongoDescriptor(me.Document): photo_id = me.LongField(primary_key=True, required=True) coords = me.PointField(db_field="coordinates", required=True) dataset = me.EnumField(DatasetEnum, required=True) binary_descriptor = me.BinaryField(required=True, db_field="descriptor") meta = {'collection': 'flickr.descriptors_512'} @property def descriptor(self): if self.binary_descriptor is not None: return pickle.loads(self.binary_descriptor) else: return None @descriptor.setter def descriptor(self, descriptor_array: np.ndarray): if not isinstance(descriptor_array, np.ndarray): raise ValueError("Descriptor array should be of type np.ndarray") self.binary_descriptor = pickle.dumps(descriptor_array, protocol=2) @property def coordinates(self): return self.coords['coordinates'] @coordinates.setter def coordinates(self, coord_dict): if 'lat' not in coord_dict or 'lng' not in coord_dict: raise ValueError( "error setting coordinates, lat or lng is not in dict") self.coords = [coord_dict['lng'], coord_dict['lat']] def set_coordinates(self, lng, lat): self.coords = [lng, lat] @classmethod def get_ids_and_coords(cls, dataset: DatasetEnum) -> dict: if isinstance(dataset, DatasetEnum): objects = cls.objects(dataset=dataset) else: raise ValueError( f"dataset should be one of DatasetEnum, was {dataset}") total = objects.count() log.debug( f"Getting {dataset.value} dataset from db. Total number of documents in db {total}" ) ids = np.zeros(shape=total, dtype=int) coordinates = np.zeros(shape=(total, 2)) batch_size = 100000 for i, descriptor in enumerate( objects.batch_size(batch_size).only('photo_id', 'coords')): ids[i] = descriptor.photo_id coordinates[i, :] = descriptor.coordinates if (i + 1) % batch_size == 0: log.debug(f"Processed {i + 1} documents") return ids, coordinates @classmethod def get_data_as_arrays(cls, dataset: DatasetEnum = None) -> dict: if isinstance(dataset, DatasetEnum): objects = cls.objects(dataset=dataset) else: raise ValueError( f"dataset should be one of DatasetEnum, was {dataset}") total = objects.count() dim = cls.objects.first().descriptor.shape[0] log.debug( f"Getting {dataset.value} dataset from db. Total number of documents in db {total}" ) descriptors = np.zeros(shape=(total, dim)) ids = np.zeros(shape=total, dtype=int) coordinates = np.zeros(shape=(total, 2)) batch_size = 50000 for i, descriptor in enumerate(objects.batch_size(batch_size)): descriptors[i, :] = descriptor.descriptor ids[i] = descriptor.photo_id coordinates[i, :] = descriptor.coordinates if (i + 1) % batch_size == 0: log.debug(f"Processed {i + 1} documents") return ids, coordinates, descriptors @classmethod def get_random_docs(cls, dataset, sample_size): # Sample is quite slow, don't suggest using it pipeline = [{ "$sample": { "size": sample_size } }, { "$project": { '_id': 1 } }] pipeline_cursor = cls.objects(dataset=dataset).aggregate(pipeline) ids = [doc['_id'] for doc in pipeline_cursor] return cls.objects(photo_id__in=ids)
class Doc(me.Document): point = me.PointField()
class Restaurant(mongo.DynamicDocument): name = mongo.StringField(required=True) address = mongo.StringField(required=True) email = mongo.EmailField() geolocation = mongo.PointField() description = mongo.StringField(max_length=TWEET_CHAR_LENGTH)
class User(mongo.Document): meta = {'queryset_class': CustomUserQuerySet, 'allow_inheritance': True} full_name = mongo.StringField(verbose_name=_('full name'), max_length=30) email = mongo.EmailField(verbose_name=_('email address'), required=True, unique=True) password = mongo.StringField(max_length=128, verbose_name=_('password')) last_login = mongo.DateTimeField(verbose_name=_('last login'), default=timezone.now) is_staff = mongo.BooleanField(default=False) is_active = mongo.BooleanField(default=True) is_superuser = mongo.BooleanField(default=False) site_id = mongo.IntField(required=True, default=settings.SITE_ID) current_role = mongo.StringField(required=False) image = mongo.StringField(required=False) user_types = mongo.ListField(mongo.ReferenceField(UserTypes), required=True, verbose_name=_('User Types')) primary_user_type = mongo.ReferenceField( UserTypes, required=False, verbose_name=_('Primary User Type')) organization = mongo.ReferenceField(Organization, required=False, verbose_name=_('Organization')) custom_field_form = mongo.ReferenceField("FormSchema", required=False) activation_key = mongo.StringField(required=False) key_expires = mongo.DateTimeField(required=False) ban = mongo.BooleanField(default=False) ban_reason = mongo.StringField(max_length=255, required=False) address = mongo.StringField(max_length=255, required=False) city = mongo.StringField(max_length=255, required=False) state = mongo.StringField(max_length=255, required=False) zip = mongo.StringField(max_length=50, required=False) phone = mongo.StringField(max_length=20, required=False) description = mongo.StringField(required=False) point = mongo.PointField(required=False) #used for dashboard resource reference dashboard_resource_id = mongo.StringField(max_length=24, required=False) # TODO disabled for now, either needs to become a custom form, or hard coded like the address above # billing_phone = mongo.StringField(max_length=20, required=False, verbose_name=_('Billing Phone')) # shipping_phone = mongo.StringField(max_length=20, required=False, verbose_name=_('Shipping Phone')) # same_address = mongo.BooleanField(default=False, verbose_name=_('Is shipping adddress same as billing address')) # address_verified = mongo.BooleanField(default=False, verbose_name=_('Address verified by TaxCloud')) created = mongo.StringField(required=True, default=str(datetime.now())) updated = mongo.StringField(required=True, default=str(datetime.now())) USERNAME_FIELD = 'email' REQUIRED_FIELDS = ['full_name'] @mongo.queryset.queryset_manager def objects(doc_cls, queryset): # This may actually also be done by defining a default ordering for # the document, but this illustrates the use of manager methods return queryset.filter(site_id=settings.SITE_ID) @mongo.queryset.queryset_manager def all(doc_cls, queryset): # This may actually also be done by defining a default ordering for # the document, but this illustrates the use of manager methods return queryset.all() def save(self, *args, **kwargs): self.updated = datetime.now().strftime("%Y-%m-%d %H:%M:%S") if self.address and self.zip and self.city and self.state: address = (self.address + ", " + self.zip, self.city, self.state) geocoded_addr = verify_address(address) if geocoded_addr != 'Error': self.point = geocoded_addr super(User, self).save(*args, **kwargs) if not self.primary_user_type: self.set_primary_user_type() def set_current_role(self, role): self.current_role = role self.save() def set_primary_user_type(self, typ=None): admin = UserTypes.objects.get(name='Admin') if typ: typ = UserTypes.objects.get(name=typ) elif admin in self.user_types: typ = admin else: typ = self.user_types[0] self.primary_user_type = typ if self.site_id == 1: self.current_role = 'maps-admin' else: self.current_role = typ.name self.save() # def is_provider(self): # return len(self.user_types.filter(type='provider')) > 0 def get_username(self): "Return the identifying username for this User" return getattr(self, self.USERNAME_FIELD) def __unicode__(self): return self.get_username() def natural_key(self): return (self.get_username(), ) def is_anonymous(self): """ Always returns False. This is a way of comparing User objects to anonymous users. """ return False def is_authenticated(self): """ Always return True. This is a way to tell if the user has been authenticated in templates. """ return True def set_password(self, raw_password): self.password = make_password(raw_password) def check_password(self, raw_password): """ Returns a boolean of whether the raw_password was correct. Handles hashing formats behind the scenes. """ def setter(raw_password): self.set_password(raw_password) self.save(update_fields=["password"]) return check_password(raw_password, self.password, setter) def set_unusable_password(self): # Sets a value that will never be a valid hash self.password = make_password(None) def has_usable_password(self): return is_password_usable(self.password) def get_session_auth_hash(self): """ Returns an HMAC of the password field. """ key_salt = "maps.users.models.AbstractBaseUser.get_session_auth_hash" return salted_hmac(key_salt, self.password).hexdigest() def email_user(self, subject, message, from_email=None, **kwargs): """ Sends an email to this User. """ send_mail(subject, message, from_email, [self.email], **kwargs) @property def location_count(self): try: return Location.objects(created_by=str(self.id)).count() except mongo.ValidationError: return 0 @property def image_url(self): url = None if self.image: url = settings.MEDIA_URL + self.image return url def custom_form(self): output = [] try: if self.custom_field_form: serializer = FormSchemaIdSerializer(self.custom_field_form) output.append(serializer.data) except: pass return output def has_permission(self, obj_name, action): for ut in self.user_types: if ut.has_permission(obj_name, action): return True return False def has_edit_other_permission(self, obj_name): return self.has_permission(obj_name, '4') def has_delete_other_permission(self, obj_name): return self.has_permission(obj_name, '5') def has_edit_own_permission(self, obj_name): return self.has_permission(obj_name, '1') def has_delete_own_permission(self, obj_name): return self.has_permission(obj_name, '2') def can_delete(self, obj, perm_type=None): if not perm_type: perm_type = obj.permission_type if obj.created_by == self.id: if self.has_delete_own_permission(perm_type): return True else: if self.has_delete_other_permission(perm_type): return True return False def can_edit(self, obj, perm_type=None): if not perm_type: perm_type = obj.permission_type if obj.created_by == self.id: if self.has_edit_own_permission(perm_type): return True else: if self.has_edit_other_permission(perm_type): return True return False @property def permission_type(self): return "users"
class User(db.Document): meta = { 'strict': False, 'queryset_class': fm.BaseQuerySet, 'index_opts': INDEX_OPTS, 'index_background': INDEX_BACKGROUND, 'index_cls': INDEX_CLS, 'auto_create_index': AUTO_CREATE_INDEX, 'indexes': [ # 'uid', # 'phone', # '-last_login_at', # (("location", "2dsphere"), '-birthed_at'), # (("location", "2dsphere"), '-birthed_at', '-star_rating_avg'), ] } class Status(object): OPENED = "OPENED" PENDING = "PENDING" APPROVED = "APPROVED" REJECTED = "REJECTED" BLOCKED = "BLOCKED" UNREGISTERED = "UNREGISTERED" uid = db.StringField() nickname = db.StringField() sex = db.StringField(choices=["M", "F"]) birthed_at = db.LongField() height = db.IntField() body_id = db.IntField() occupation = db.StringField() education = db.StringField() religion_id = db.IntField() drink_id = db.IntField() smoking_id = db.IntField() blood_id = db.IntField() device_token = db.StringField() location = db.PointField() introduction = db.StringField() joined_at = db.LongField() last_login_at = db.LongField() job = db.StringField() area = db.StringField() phone = db.StringField(unique=True) user_images = db.SortedListField(db.EmbeddedDocumentField(UserImage), ordering="index") user_images_temp = db.SortedListField(db.EmbeddedDocumentField(UserImage), ordering="index") charm_ids = db.ListField(db.IntField()) ideal_type_ids = db.ListField(db.IntField()) interest_ids = db.ListField(db.IntField()) available = db.BooleanField(default=False) status = db.StringField(choices=[ Status.OPENED, Status.PENDING, Status.APPROVED, Status.REJECTED, Status.BLOCKED, Status.UNREGISTERED ]) star_rating_avg = db.FloatField(default=0) free_pass_tokens = db.ListField(db.LongField(), default=[0, 0]) free_open_tokens = db.ListField(db.LongField(), default=[0]) is_phones_cached = False phones_cache = [] def identify(self, request): uid = request.headers.get("uid", None) if self.uid != uid: raise abort(401) def list_posts(self) -> list: result = Post.list_posts(author=self.id, is_deleted=False) return result def list_requests_to_me(self, response=None): return Request.list_requests(user_to=self, response=response) def list_requests_from_me(self, response=None): return Request.list_requests(user_from=self, response=response) def list_conversations(self): return Conversation.objects(participants=self).all() def list_users_rated_me(self): star_ratings = StarRating.objects(user_to=self).order_by("-rated_at").as_pymongo() score_repository = { str(s.get("user_from")): dict(score=s.get("score", 0), rated_at=s.get("rated_at")) for s in star_ratings } raters = User.list(id__in=score_repository.keys()).as_pymongo() result = [] for rater in raters: user_id = str(rater.get("_id")) o = score_repository.get(user_id) o["user"] = rater result.append(o) return result def list_users_rated_me_high(self): star_ratings = StarRating.objects(user_to=self, score__gte=4).order_by("-rated_at").as_pymongo() user_ids = [star_rating.get("user_from", None) for star_rating in star_ratings] users_dict = User.get_users_dict(set(user_ids)) result = [users_dict.get(str(user_id)) for user_id in user_ids] return filter(lambda x: x, result) def list_users_i_rated_high(self): star_ratings = StarRating.objects(user_from=self, score__gte=4).order_by("-rated_at").as_pymongo() user_ids = [star_rating.get("user_to", None) for star_rating in star_ratings] users_dict = User.get_users_dict(set(user_ids)) result = [users_dict.get(str(user_id)) for user_id in user_ids] return filter(lambda x: x, result) def get_recommendation(self): recommendation = Recommendation.objects(owner=self).first() if not recommendation: recommendation = Recommendation( owner=self, user_ids=[], last_recommended_at=pendulum.yesterday().int_timestamp ) recommendation.save() recommendation.reload() return recommendation def get_first_image(self): return self.user_images[0].url if self.user_images else "" def remove_user_from_recommendation(self, user_to_remove): if not user_to_remove: raise ValueError("owner and target must be required value.") recommendation = Recommendation.objects(owner=self).first() if not recommendation: return for index, rec_user_id in enumerate(recommendation.user_ids): if rec_user_id == user_to_remove.id: del recommendation.user_ids[index] break recommendation.save() def get_current_amount_of_point(self): payments = Payment.objects(owner=self).all() amount_sum = 0 for payment in payments: amount_sum += payment.amount return amount_sum def is_available_for_free_pass_token(self): token_min = min(self.free_pass_tokens) current_time = pendulum.now().int_timestamp delta = current_time - token_min return delta >= 0 def consume_free_pass_token(self): tokens = list(self.free_pass_tokens) index = tokens.index(min(tokens)) tokens[index] = pendulum.now().int_timestamp + FREE_PASS_NEXT * 60 * 60 if len(tokens) != 2: tokens = [0, 0] self.update(free_pass_tokens=tokens) def is_available_for_free_open_token(self): token_min = min(self.free_open_tokens) current_time = pendulum.now().int_timestamp delta = current_time - token_min return delta >= 0 def consume_free_open_token(self): tokens = list(self.free_open_tokens) index = tokens.index(min(tokens)) tokens[index] = pendulum.now().int_timestamp + FREE_OPEN_NEXT * 60 * 60 if len(tokens) != 1: tokens = [0] self.update(free_open_tokens=tokens) def consume(self, amount): if amount <= 0: raise ValueError("Illegal amount found. The amount must be bigger than 0.") total_amount = self.get_current_amount_of_point() if total_amount < amount: raise Exception( "Not enough balance..\n" "remaining amount: {total_amount}, amount to consume: {amount}".format( total_amount=total_amount, amount=amount)) Payment( owner=self, type="CONSUME", amount=(amount * -1), created_at=pendulum.now().int_timestamp ).save() def purchase(self, platform=None, order_id=None, product_id=None, amount=None, created_at=None, purchase_time_ms=None): if not product_id or amount <= 0 or not order_id or not created_at or not purchase_time_ms or not platform: raise ValueError("Illegal amount found. The amount must be bigger than 0.") existing = Payment.objects(order_id=order_id, purchase_time_ms=purchase_time_ms).first() if existing: return Payment.Result.DUPLICATE payment = Payment( owner=self, type="PURCHASE", platform=platform, order_id=order_id, product_id=product_id, amount=amount, created_at=int(created_at), purchase_time_ms=int(purchase_time_ms) ) payment.save() return Payment.Result.PURCHASED def list_payments(self, to_json=False): if not to_json: return Payment.objects(owner=self).all() else: payments = Payment.objects(owner=self).as_pymongo() return list(payments) def set_contact(self, phones): contact = self.get_contact() contact.phones = phones contact.last_updated = pendulum.now().int_timestamp contact.save() contact.reload() return contact def add_user_knows_me(self, user): contact = self.get_contact() user_ids_know_me = contact.user_ids_know_me if user.id not in user_ids_know_me: contact.user_ids_know_me.append(user.id) contact.save() def get_contact(self): contact = Contact.objects(owner=self).first() if not contact: contact = Contact( owner=self, phones=[], last_updated_at=pendulum.now().int_timestamp) contact.save() contact.reload() return contact def get_phones(self): if self.is_phones_cached: return self.phones_cache contact = self.get_contact() self.phones_cache = contact.phones self.is_phones_cached = True return self.phones_cache def has_in_contacts(self, phone) -> bool: if not phone: return False phones = self.get_phones() return phone in phones def does_know_each_other(self, user): if self.has_in_contacts(user.phone): # checks whether I know him return True if user.has_in_contacts(self.phone): # checks whether he knows me return True return False def _get_nin_ids(self) -> set: app.logger.debug("collecting nin ids..") nin_ids = set() # nin with requests requests_to_me = Request.objects(user_to=self).as_pymongo() requests_from_me = Request.objects(user_from=self).as_pymongo() to_me_ids = [r["user_from"] for r in requests_to_me] from_me_ids = [r["user_to"] for r in requests_from_me] nin_ids.update(to_me_ids) nin_ids.update(from_me_ids) app.logger.debug("collected nin ids with requests..") # nin with recommendation recommendation = self.get_recommendation() recommendation = recommendation.to_mongo() rec_ids = [user_id for user_id in recommendation["user_ids"]] nin_ids.update(rec_ids) app.logger.debug("collected nin ids with recommendation..") # nin with contact contact = self.get_contact() know_ids = [user_id for user_id in contact.user_ids_know_me] nin_ids.update(know_ids) phones = contact.phones ids_in_contacts = User.list_only_user_ids(phone__in=phones) nin_ids.update(ids_in_contacts) app.logger.debug("collected nin ids with contacts phone numbers..") return nin_ids def list_recommended_user_ids(self, nin_ids: set = None, result=None, min_diameter=0, max_diameter=30, star_rating_avg=3.5): """Generates recommended users. If not found, recursively try it increasing diameter up to 300 km.""" sex = next((s for s in ['M', 'F'] if s != self.sex)) result = result or [] nin_ids = nin_ids or self._get_nin_ids() location = self.location["coordinates"] if self.location else [127.0977517240413, 37.49880740259655] params = dict( location__near=location, location__min_distance=min_diameter * 1000, location__max_distance=max_diameter * 1000, birthed_at__gte=self.birthed_at - (12 * ONE_YEAR_TO_SECONDS), birthed_at__lte=self.birthed_at + (12 * ONE_YEAR_TO_SECONDS), star_rating_avg__gte=star_rating_avg, id__nin=nin_ids, available=True, sex=sex ) if max_diameter > 300: return result app.logger.debug("collecting users with params: {0}..".format(str(params))) cursor = User.list(**params) index = 0 while True: app.logger.debug("cursor is moving..") user = _get_index_at(cursor, index) if user is None: break if self.does_know_each_other(user): self.add_user_knows_me(user) user.add_user_knows_me(self) nin_ids.add(user.id) continue result.append(user.id) index += 1 if len(result) >= 2: break if len(result) < 2: app.logger.debug("Not enough recommended user found.. collecting more") return self.list_recommended_user_ids( nin_ids, result=result, star_rating_avg=star_rating_avg * 0.9, min_diameter=max_diameter, max_diameter=max_diameter * 2) return result # 126.98265075683594, 37.56100082397461 def list_realtime_user_ids(self): nin_ids = self._get_nin_ids() sex = 'M' if self.sex == 'F' else 'F' last_login_at_gte = pendulum.now().int_timestamp - (60 * 30) params = dict( last_login_at__gte=last_login_at_gte, id__nin=nin_ids, sex=sex, available=True ) cursor = User.objects(**params).order_by("-last_login_at") size = cursor.count() result = [] for index in range(0, size): user = cursor[index] if self.does_know_each_other(user): self.add_user_knows_me(user) user.add_user_knows_me(self) continue result.append(user.id) if len(result) >= 10: break return result @time_lapse def list_user_ids_within_distance(self, distance=5): nin_ids = self._get_nin_ids() sex = 'M' if self.sex == 'F' else 'F' location = self.location["coordinates"] if self.location else [127.0936859, 37.505808] params = dict( location__near=location, location__max_distance=distance * 1000, # 1000 = 1 km location__min_distance=0 * 1000, birthed_at__gte=self.birthed_at - (12 * ONE_YEAR_TO_SECONDS), birthed_at__lte=self.birthed_at + (12 * ONE_YEAR_TO_SECONDS), id__nin=nin_ids, sex=sex, available=True ) user_ids = User.list_only_user_ids(**params) random.seed(_get_hash(str(self.id)) + 1) random.shuffle(user_ids) result = [] for user_id in user_ids: user = User.get(id=user_id) if self.does_know_each_other(user): self.add_user_knows_me(user) user.add_user_knows_me(self) continue result.append(user.id) if len(result) >= 10: break return result @classmethod def excludes(cls): return [ "uid", "joined_at", "user_images_temp", "free_pass_tokens", "free_open_tokens", "phone" ] @classmethod def get(cls, **kwargs): excludes = kwargs.get("excludes", User.excludes()) return User.objects.exclude(*excludes).get_or_404(**kwargs) @classmethod def list(cls, **kwargs): users = User.objects(**kwargs).exclude(*User.excludes()) return users @classmethod def get_users_dict(cls, user_ids: set): users = User.list(id__in=list(user_ids)).as_pymongo() return {str(user.get("_id")): user for user in users} @classmethod def get_verified_user(cls, user_id, request): uid = request.headers.get("uid", None) user = User.get(uid=uid) if user_id != str(user.id): abort(401) return user @classmethod @time_lapse def list_only_user_ids(cls, **kwargs): query = User.objects(**kwargs).only("id").as_pymongo() return [o["_id"] for o in query] def is_admin(self): admin = Admin.objects(user=self).first() return admin is not None def withdraw(self): unregister = Unregister(nickname=self.nickname, uid=self.uid, phone=self.phone, user=self) unregister.save() self.uid = None self.phone = None self.device_token = None self.user_images = [] self.user_images_temp = [] self.available = False self.status = User.Status.REJECTED self.nickname = "탈퇴 한 회원" self.save() Post.objects(author=self).delete() # Comment.objects(owner=user).delete() # Request.objects(user_from=user).delete() # Request.objects(user_to=user).delete() # StarRating.objects(user_from=user).delete() # StarRating.objects(user_to=user).delete() # Contact.objects(owner=user).delete() # Alarm.objects(owner=user).delete() def get_setting(self): setting = Setting.objects(owner=self).first() if not setting: setting = Setting(owner=self) setting.save() setting.reload() return setting