class Child(MappedClass): class __mongometa__: name='child' session = self.session _id = FieldProperty(S.ObjectId) parent_id = ForeignIdProperty(Parent) field_with_default_id = ForeignIdProperty( Parent, if_missing=lambda:bson.ObjectId('deadbeefdeadbeefdeadbeef')) field_with_default = RelationProperty('Parent', 'field_with_default_id')
class Parent(MappedClass): class __mongometa__: name='parent' session = self.session _id = FieldProperty(S.ObjectId) children = ForeignIdProperty('Child', uselist=True) field_with_default_id = ForeignIdProperty( 'Child', uselist=True, if_missing=lambda:[bson.ObjectId('deadbeefdeadbeefdeadbeef')]) field_with_default = RelationProperty('Child', 'field_with_default_id')
class TPremium(MappedClass, EnhancingClass): class __mongometa__: session = session name = 'testpremiums' extensions = [ MyExtension ] _id = FieldProperty(schema.ObjectId) premium_id = ForeignIdProperty(TPremiumSize) worker_id = ForeignIdProperty(TWorker) earning_date = FieldProperty(schema.DateTime(required=True)) premium_size = FieldProperty(schema.Int(required=True)) note = FieldProperty(schema.String(if_missing = '')) worker = RelationProperty(TWorker) premium = RelationProperty(TPremiumSize)
class Parent(MappedClass): class __mongometa__: name='parent' session = self.session _id = FieldProperty(int) children = RelationProperty('Child') _children = ForeignIdProperty('Child', uselist=True)
class Child(MappedClass): class __mongometa__: name='child' session = self.session _id = FieldProperty(int) parent_id = ForeignIdProperty('Parent') parent = RelationProperty('Parent')
class GoogleAuth(MappedClass): class __mongometa__: session = DBSession name = 'googleplusauth_info' indexes = [(('_user_id', ), )] _id = FieldProperty(s.ObjectId) registered = FieldProperty(s.Bool, if_missing=False) just_connected = FieldProperty(s.Bool, if_missing=False) profile_picture = FieldProperty(s.String) _user_id = ForeignIdProperty('User') user = RelationProperty('User') google_id = FieldProperty(s.String, required=True) access_token = FieldProperty(s.String, required=True) access_token_expiry = FieldProperty(s.DateTime, required=True) @classmethod def ga_user_by_google_id(cls, google_id): google_auth_user = cls.query.find({'google_id': google_id}).first() return google_auth_user @classmethod def googleplusauth_user(cls, user_id): return cls.query.find({'_user_id': user_id}).first()
class Workspace(MappedClass): class __mongometa__: session = DBSession name = 'workspaces' custom_indexes = [ dict(fields=('name', '_owner'), unique=True, sparse=True) ] _id = FieldProperty(s.ObjectId) name = FieldProperty(s.String, required=True) visible = FieldProperty(s.Bool, if_missing=True, index=True) _owner = ForeignIdProperty('User') owner = RelationProperty('User') @classmethod def per_user(cls, user_id): return cls.query.find({ '_owner': { '$in': [user_id, None] }, 'visible': True }).sort('_id').all() @classmethod def by_id(cls, _id): return cls.query.get(_id=ObjectId(_id))
def setUp(self): self.datastore = create_datastore('mim:///test_db') session = Session(bind=self.datastore) self.session = ODMSession(session) class Parent(object): pass class Child(object): pass parent = collection('parent', session, Field('_id', int)) child = collection('child', session, Field('_id', int), Field('parent_id', int)) mapper(Parent, parent, self.session, properties=dict(children=RelationProperty(Child))) mapper(Child, child, self.session, properties=dict(parent_id=ForeignIdProperty(Parent), parent=RelationProperty(Parent))) self.Parent = Parent self.Child = Child
class Parent(MappedClass): class __mongometa__: name='parent' session = self.session _id = FieldProperty(int) grandparent_id = ForeignIdProperty('GrandParent') grandparent = RelationProperty('GrandParent') children = RelationProperty('Child')
class WikiComment(MappedClass): class __mongometa__: session = session name = 'wiki_comment' _id = FieldProperty(schema.ObjectId) page_id = ForeignIdProperty('WikiPage') text = FieldProperty(schema.String(if_missing=''))
class TestCollection(MappedClass): class __mongometa__: name='test_collection' session = self.session _id = FieldProperty(int) children = RelationProperty('TestCollection') _children = ForeignIdProperty('TestCollection', uselist=True) parents = RelationProperty('TestCollection', via=('_children', False))
class Dataset(MappedClass): class __mongometa__: session = DBSession name = 'datasets' _id = FieldProperty(s.ObjectId) homer_q_id = ForeignIdProperty(HomerQ) homer_q = RelationProperty(HomerQ) metadata_origin = FieldProperty(s.String, index=True)
class TemporaryPhotosBucket(MappedClass): class __mongometa__: session = DBSession name = 'temporary_photos_bucket' unique_indexes = [('created_at',), ] _id = FieldProperty(s.ObjectId) created_at = FieldProperty(s.DateTime, required=True, if_missing=datetime.utcnow) photos = ForeignIdProperty(BucketProductImage, uselist=True) photos_rel = RelationProperty('BucketProductImage')
class Parent(MappedClass): class __mongometa__: name = 'parent' session = session _id = FieldProperty(schema.ObjectId) name = FieldProperty(schema.String(required=True)) _children = ForeignIdProperty('Child', uselist=True) children = RelationProperty('Child')
class WikiPage(MappedClass): class __mongometa__: session = cls.s name = 'wiki_page' _id = FieldProperty(schema.ObjectId) title = FieldProperty(str) text = FieldProperty(str) order = FieldProperty(int) author_id = ForeignIdProperty(Author) author = RelationProperty(Author)
class TemplateTranslation(MappedClass): class __mongometa__: name = 'mailtemplates_template_translations' session = DBSession _id = FieldProperty(s.ObjectId) mail_model_id = ForeignIdProperty('MailModel') mail_model = RelationProperty('MailModel') language = FieldProperty(s.String, required=True) subject = FieldProperty(s.String) body = FieldProperty(s.String)
class Registration(MappedClass): class __mongometa__: session = DBSession name = 'registration_registration' indexes = [(('activated', ), ('code', ))] _id = FieldProperty(s.ObjectId) time = FieldProperty(s.DateTime, if_missing=datetime.now) user_name = FieldProperty(s.String, required=True) email_address = FieldProperty(s.String, required=True, index=True) password = FieldProperty(s.String, required=True) code = FieldProperty(s.String) activated = FieldProperty(s.DateTime) extras = FieldProperty(s.Anything) user_id = ForeignIdProperty(app_model.User) @property def dictified(self): return dict(time=self.time, user_name=self.user_name, email_address=self.email_address, code=self.code, activated=self.activated, user_id=self.user_id, activation_link=self.activation_link) @cached_property def user(self): return app_model.User.get(ObjectId(self.user_id)) @cached_property def activation_link(self): return url(mount_point('registration') + '/activate', params=dict(code=self.code), qualified=True) @classmethod def generate_code(cls, email): code_space = string.ascii_letters + string.digits def _generate_code_impl(): base = ''.join(random.sample(code_space, 8)) base += email base += str(time.time()) return hashlib.sha1(base.encode('utf-8')).hexdigest() code = _generate_code_impl() while cls.query.find({'code': code}).first(): code = _generate_code_impl() return code @classmethod def clear_expired(cls): for expired_reg in cls.query.find({'activated': None, 'time': {'$lte': datetime.now()-timedelta(days=2)}}): expired_reg.delete() @classmethod def get_inactive(cls, code): return cls.query.find(dict(activated=None, code=code)).first()
class FlatPage(MappedClass): class __mongometa__: session = model.DBSession name = 'flatpages_page' unique_indexes = [('slug', )] extensions = [UpdateDate] _id = FieldProperty(s.ObjectId) template = FieldProperty( s.String, if_missing=config['_flatpages'].get('templates')[0][0]) slug = FieldProperty(s.String, required=True) title = FieldProperty(s.String, required=True) content = FieldProperty(s.String, if_missing='') required_permission = FieldProperty(s.String) updated_at = FieldProperty(s.DateTime, if_missing=datetime.utcnow) created_at = FieldProperty(s.DateTime, if_missing=datetime.utcnow) author_id = ForeignIdProperty('User') author = RelationProperty('User') @classmethod def by_id(cls, _id): return cls.query.get(ObjectId(_id)) @classmethod def by_slug(cls, slug): return cls.query.find(dict(slug=slug)).first() @cached_property def url(self): return plug_url('flatpages', '/' + self.slug) @classmethod def all_pages(cls): """Returns a list of tuples with title and url of all the flat pages""" return [(page.title, page.url) for page in cls.query.find()] @cached_property def html_content(self): format = config['_flatpages']['format'] formatter = FORMATTERS[format] content = self.content if content.startswith('file://'): package_path = config['paths']['root'] file_path = os.path.join(package_path, content[7:]) with closing(open(file_path)) as f: content = f.read() return formatter(content)
class CommentVote(MappedClass): class __mongometa__: session = model.DBSession name = 'tgcomments_comments_votes' unique_indexes = [( "_id", "comment_id", "user_id", )] _id = FieldProperty(s.ObjectId) created_at = FieldProperty(s.DateTime, if_missing=datetime.now, required=True) value = FieldProperty(s.Int, if_missing=1) user_id = FieldProperty(s.ObjectId) user = ForeignIdProperty(app_model.User) comment_id = FieldProperty(s.ObjectId) comment = ForeignIdProperty(Comment)
class SkeletonClass(MappedClass): """ Sample class with docstring for content. Collection: <put collection assoc here> Relations: Fields: * _id: Unique Mongodb ID. """ # --------------------------------------------- # Mongometa information. # # You should change only the "name" field to be # the name of the associated database collection. # ============================================= class __mongometa__: session = ModSocDB.Session name = "<Change>" # ------------------------------------------------ # Link information. # # Put any links here following the model of the # dataset link below. # # NOTE:: The dataset link must be preserved and # updated on imports so that everything points # to the correct dataset. # ================================================ # Link to the associated Dataset. Dataset_ID = ForeignIdProperty('Dataset') Dataset = RelationProperty('Dataset') # ------------------------------------------------ # Link information. # # Every object will have an _id field so that need # not be changed. Other lines will be a FieldProperty # for the association information. # ================================================ # Mongodb object ID (unique). _id = FieldProperty(schema.ObjectId) # content: (i.e. text) of the post itself. content = FieldProperty(str) created = FieldProperty(datetime.datetime)
class BucketProductImage(MappedClass): class __mongometa__: session = DBSession name = 'bucket_product_image' indexes = [('bucket_id', )] _id = FieldProperty(s.ObjectId) image = UploadedFileProperty( upload_storage='product_images', upload_type=UploadedImageWithThumb ) bucket_id = ForeignIdProperty('TemporaryPhotosBucket') bucket = RelationProperty('TemporaryPhotosBucket')
class Permission(MappedClass): """ Permission definition. """ class __mongometa__: session = DBSession name = 'tg_permission' unique_indexes = [('permission_name',),] _id = FieldProperty(s.ObjectId) permission_name = FieldProperty(s.String) description = FieldProperty(s.String) _groups = ForeignIdProperty(Group, uselist=True) groups = RelationProperty(Group)
class Rule(MappedClass): class __mongometa__: session = DBSession name = 'rules' indexes = [ ('_user', ), ] _id = FieldProperty(s.ObjectId) name = FieldProperty(s.String) content = FieldProperty(s.String) _user = ForeignIdProperty('User') user = RelationProperty('User')
class UserAddress(MappedClass): class __mongometa__: session = DBSession name = 'user_addresses' indexes = [('user_id',),] _id = FieldProperty(s.ObjectId) user = RelationProperty(app_model.User) user_id = ForeignIdProperty(app_model.User) shipping_address = FieldProperty({ 'receiver': s.String, 'address': s.String, 'city': s.String, 'province': s.String, 'state': s.String, 'country': s.String, 'zip': s.String, 'details': s.Anything })
class FlatFile(MappedClass): class __mongometa__: session = model.DBSession name = 'flatpages_file' unique_indexes = [('name')] extensions = [UpdateDate] _id = FieldProperty(s.ObjectId) name = FieldProperty(s.String, required=True) file = UploadedFileProperty(upload_storage='flatfiles') updated_at = FieldProperty(s.DateTime, if_missing=datetime.utcnow) created_at = FieldProperty(s.DateTime, if_missing=datetime.utcnow) author_id = ForeignIdProperty('User') author = RelationProperty('User') @cached_property def url(self): return plug_url('flatpages', '/flatfiles/' + self.file.file_id)
class DatasetElementCommentDAO(MappedClass): class __mongometa__: session = session name = 'data_element_comment' _id = FieldProperty(schema.ObjectId) author_name = FieldProperty(schema.String) author_link = FieldProperty(schema.String) content = FieldProperty(schema.String) addition_date = FieldProperty(schema.datetime) element_id = ForeignIdProperty('DatasetElementDAO') def __init__(self, author_name, author_link, content, addition_date=now(), element_id=None, element=None): if element_id is None and element is not None: element_id = element._id kwargs = { k: v for k, v in locals().items() if k not in ["self", "__class__", "element"] } super().__init__(**kwargs) @classmethod def from_dict(cls, init_dict): return cls(**init_dict) def update(self): return session.refresh(self) def delete(self): DatasetElementCommentDAO.query.remove({'_id': self._id})
class User(MappedClass): """ The User class provides a central identifier for a user. It is based on the PiazzaUser instance primarily and includes some of the same content including the anonymous ID. It will ultimately be a vehicle for adding in other methods to calculate per-user statistics. Collection: users Relations: * PiazzaUser: Link to the associated Piazza User. * Dataset: Link to the associated Dataset. Fields: * _id: Unique Mongodb ID. * PiazzaUser_ID: Mongo ID of the Piazza User. * Dataset_ID: ID of the associated Dataset. * local_user_id: Local anonymously assigned user ID info. * PiazzaID: Piazza ID for user. * StudentNumber: Student ID number. * name: Full student name. * first_name: Split first name. * middle_name: Split middle name. * last_name: Split last name. * email: Student's email address. * piazza_alt_email: Alternate email address. * username: system username. * Instructor: instructor name. * SectionNumber: integer section number. * role: Role in the course - Student, Instructor, or TA * postCount: Count for number of piazza posts. * PiazzaDays: Days active or logged in. * PiazzaViews: Count of views made. * PiazzaAsks: Count of asks made. * PiazzaAnswers: Count of answers given to question. * FinalGrade: Final grade of the student. """ # --------------------------------------------- # Mongometa information. # # You should change only the "name" field to be # the name of the associated database collection. # ============================================= class __mongometa__: session = ModSocDB.Session name = "users" # ------------------------------------------------ # Link information. # # Put any links here following the model of the # author link below. # ================================================ # Link to the associated Dataset. Dataset_ID = ForeignIdProperty('Dataset') Dataset = RelationProperty('Dataset') # Link to the associated Piazza User. PiazzaUser_ID = ForeignIdProperty('PiazzaUser.PiazzaUser') PiazzaUser = RelationProperty('PiazzaUser.PiazzaUser') # ------------------------------------------------ # Link information. # # Every object will have an _id field so that need # not be changed. Other lines will be a FieldProperty # for the association information. # ================================================ # Mongodb object ID (unique). _id = FieldProperty(schema.ObjectId) # Unique user ID set on introduction by the counter # mechanism. local_user_id = FieldProperty(str) PiazzaID = FieldProperty(str) StudentNumber = FieldProperty(str) # Student real name or anonymized name. name = FieldProperty(str) # Split field for individual names. first_name = FieldProperty(str) middle_name = FieldProperty(str) last_name = FieldProperty(str) # User's email address (is in standard form so can be split.) email = FieldProperty(str) # User's alternate email address (is in standard form so can be split.) piazza_alt_email = FieldProperty(str) # Username username = FieldProperty(str) # Instructor they have and Section number to which they are assigned. # The loading code needs to be modified based on the inputs to update these field. Instructor = FieldProperty(str) SectionNumber = FieldProperty(int) # Class role - Student, Instructor, or TA # The loading code needs to be modified based on the inputs to update this field. role = FieldProperty(str) #Count for piazza posts postCount = FieldProperty(int) # Count of answers given to questions. PiazzaAnswers = FieldProperty(int) # Count of views made. PiazzaViews = FieldProperty(int) # Count of asks made. PiazzaAsks = FieldProperty(int) # Days active or logged in? PiazzaDays = FieldProperty(int) #final letter grade of student # The loading code needs to be modified based on the inputs to update this field. FinalGrade = FieldProperty(str) # ------------------------------------------------- # Static Constructor. # ================================================= @staticmethod def makeNewUser(DatasetID, Name, PiazzaUserID=None, PiazzaId=None, WebAssignUserID=None, MoodleActionID=None, Studentnumber=None, Email=None, Alt_Email=None, FirstName=None, MiddleName=None, LastName=None, Username=None, Instructor=None, SectionNumber=None, isPeerTutor=None, Role=None, FlushSession=True): """ Construct a new user. This will set the email and name fields directly from the argument and will then return the user instance. The *_name fields will be set via the split name function and the local_user_id field will be set based upon the current number of users. This is not reset safe. """ # Calculate the new ID. LocalID = "USER_%d" % (countAllUsers()) if not Username and Email: Username = Email.split('@')[0] # Calculate the Split name field values unless # they are supplied. If however the Name is None # then all will be None. if (Name == None): NewUser = User(Dataset_ID=DatasetID, PiazzaUser_ID=PiazzaUserID, WebAssignUser_ID=WebAssignUserID, MoodleAction_ID=MoodleActionID, local_user_id=LocalID, PiazzaID=PiazzaId, StudentNumber=Studentnumber, name=None, first_name=None, middle_name=None, last_name=None, email=Email, piazza_alt_email=Alt_Email, username=Username, Instructor=Instructor, SectionNumber=SectionNumber) elif (FirstName == None): SplitNameDict = ModSocDB.NLP.makeSplitNameDict(Name) NewUser = User(Dataset_ID=DatasetID, PiazzaUser_ID=PiazzaUserID, WebAssignUser_ID=WebAssignUserID, MoodleAction_ID=MoodleActionID, local_user_id=LocalID, PiazzaID=PiazzaId, StudentNumber=Studentnumber, name=Name, first_name=SplitNameDict["FirstName"], middle_name=SplitNameDict["MiddleName"], last_name=SplitNameDict["LastName"], email=Email, piazza_alt_email=Alt_Email, username=Username, Instructor=Instructor, SectionNumber=SectionNumber) else: NewUser = User(Dataset_ID=DatasetID, PiazzaUser_ID=PiazzaUserID, WebAssignUser_ID=WebAssignUserID, MoodleAction_ID=MoodleActionID, local_user_id=LocalID, PiazzaID=PiazzaId, StudentNumber=Studentnumber, name=Name, first_name=FirstName, middle_name=MiddleName, last_name=LastName, email=Email, piazza_alt_email=Alt_Email, username=Username, Instructor=Instructor, SectionNumber=SectionNumber) # If we are set to flush then do so. if (FlushSession == True): ModSocDB.Session.flush() return NewUser # ------------------------------------------------- # User Data. # ================================================= def getUserDataDict(self): """ Make the user data dictionary. """ Dict = {} Dict["UserID"] = self.ID return Dict # ------------------------------------------------- # Accessors/Settors # ================================================= def getName(self): """ Return the user name. """ return self.name def setName(self, NewName): """ Set the new user name. """ self.name = NewName def getLocalUserID(self): """ Get the local user Id value. """ return self.local_user_id def setLocalUserID(self, NewID): """ Set the local user ID value. """ self.local_user_id = NewID def getFirstName(self): """ Get the first_name value. """ return self.first_name def setFirstName(self, NewName): """ Set the first_name field. """ self.first_name = NewName def getMiddleName(self): """ Get the middle_name value. """ return self.middle_name def setMiddleName(self, NewName): """ Set the middle_name field. """ self.middle_name = NewName def getLastName(self): """ Get the last_name value. """ return self.last_name def setLastName(self, NewName): """ Set the last_name field. """ self.last_name = NewName def getEmail(self): """ Get the email. """ return self.email def setEmail(self, NewEmail): """ Set the new email. """ self.email = NewEmail def getUsername(self): """ Get the username. """ return self.username def setUsername(self, NewUsername): """ Set the new username. """ self.username = NewUsername def getRole(self): """ Get the role. """ return self.role def setRole(self, NewRole): """ Set the new role. """ self.role = NewRole def getPostCount(self): """ Get the role. """ return self.postCount def setPostCount(self, NewCount): """ Set the new role. """ self.postCount = NewCount # --------------------------------------------- # Anonymization Code. # ============================================= def anonymizeNameFields(self): """ This code resets all of the local name fields to contain an anonymous form based upon the salted user ID. This will simply set the specific name fields for the user to the IDs and will purge the group name field. For the sake of clarity this will only anonymize cases where the field is nonempty. In cases where it is none then it will simply be left as is. """ # NewID = self.getUserID() if (self.getFirstName() != None): self.setFirstName(self.makeFirstNameAnonForm()) if (self.getMiddleName() != None): self.setMiddleName(self.makeMiddleNameAnonForm()) if (self.getLastName() != None): self.setLastName(self.makeLastNameAnonForm()) if (self.name != None): self.setName(self.makeNameAnonForm()) if (self.email != None): self.setEmail(self.makeEmailAnonForm()) if (self.username != None): self.setUsername(self.makeUsernameAnonForm()) def makeFirstNameAnonForm(self): """ This is a stock method that returns the anon form of the first name for later use. """ return "%s_FirstName" % (self.getLocalUserID()) def makeMiddleNameAnonForm(self): """ This is a stock method that returns the anon form of the middle name for later use. """ return "%s_MiddleName" % (self.getLocalUserID()) def makeLastNameAnonForm(self): """ This is a stock method that returns the anon form of the last name for later use. """ return "%s_LastName" % (self.getLocalUserID()) def makeNameAnonForm(self): """ This is a stock method that returns the anon form of the name for later use. """ return "%s_Name" % (self.getLocalUserID()) def makeEmailAnonForm(self): """ This is a stock method that returns the anon form of the email for later use. """ return "%s_Email" % (self.getLocalUserID()) def makeUsernameAnonForm(self): """ This is a stock method that returns the anon form of the username for later use. """ return "%s_Username" % (self.getLocalUserID()) # ---------------------------------------------------- # NLP Tasks. # ==================================================== def nameInTextP(self, TextStr): """ Given a text string check to see if the user's first name, last name, or full name appear in it. This depends upon the nltk library. If the Name is None then this will return False. """ if (self.name == None): return False else: return ModSocDB.NLP.findNameInText(self.name, TextStr) def anonymizeNamesInText(self, TextStr): """ Performs a replacement of the names in the text with the anonymized form of the names as generated by the makeAnon methods above. NOTE:: This is not a destructive replacement and only returns an updated form. Or None if no changes are made. NOTE:: If the name is none then this will do nothing. """ return self.replaceNameInText(self.makeFirstNameAnonForm(), self.makeLastNameAnonForm(), TextStr) def replaceNameInText(self, FirstNameReplacement, LastNameReplacement, TextStr): """ Given a text string replace the user's name in it if it is present using the supplied first and last replacement strings. NOTE:: This is not a destructive replacement and only returns an updated form. Or None if no changes are made. NOTE:: If the FirstName and LastName are none then this will do nothing. """ if ((self.getFirstName() == None) and (self.getLastName() == None)): return None return ModSocDB.NLP.replaceNameInText(self.getFirstName(), self.getLastName(), FirstNameReplacement, LastNameReplacement, TextStr)
class User(MappedClass): """ User definition. This is the user definition used by :mod:`repoze.who`, which requires at least the ``user_name`` column. """ class __mongometa__: session = DBSession name = 'tg_user' unique_indexes = [ ('user_name', ), ] class PasswordProperty(FieldProperty): @classmethod def _hash_password(cls, password): salt = sha256() salt.update(os.urandom(60)) salt = salt.hexdigest() hash = sha256() # Make sure password is a str because we cannot hash unicode objects hash.update((password + salt).encode('utf-8')) hash = hash.hexdigest() password = salt + hash # Make sure the hashed password is a unicode object at the end of the # process because SQLAlchemy _wants_ unicode objects for Unicode cols password = password.decode('utf-8') return password def __set__(self, instance, value): value = self._hash_password(value) return FieldProperty.__set__(self, instance, value) _id = FieldProperty(s.ObjectId) user_name = FieldProperty(s.String) email_address = FieldProperty(s.String) display_name = FieldProperty(s.String) _groups = ForeignIdProperty(Group, uselist=True) groups = RelationProperty(Group) password = PasswordProperty(s.String) created = FieldProperty(s.DateTime, if_missing=datetime.now) @property def permissions(self): return Permission.query.find(dict(_groups={'$in': self._groups})).all() @classmethod def by_email_address(cls, email): """Return the user object whose email address is ``email``.""" return cls.query.get(email_address=email) def validate_password(self, password): """ Check the password against existing credentials. :param password: the password that was provided by the user to try and authenticate. This is the clear text version that we will need to match against the hashed one in the database. :type password: unicode object. :return: Whether the password is valid. :rtype: bool """ hash = sha256() hash.update((password + self.password[:64]).encode('utf-8')) return self.password[64:] == hash.hexdigest()
class PiazzaContent_ChangeLog(MappedClass): """ This class solely deals with the good tag of the piazza content. It is used to handle the cases where a user has tagged a post as good. It is in a 1-1 relationship with a PiazzaUser and a one to many relationship with the PiazzaContent class to which it is attached. For the most part these should match the user information in the users but because of Piazza's odd data structure this may or may not vary hence the decision to combine the information here. This class is linked to the "piazza_content_changelog" collection. Relationships: * Dataset: Link to the associated Dataset. * Content: Link to the associated PiazzaContent instance. * Author: Link to the associated PiazzaUser instance. * CentralAuthor: The Central author object for this user. Fields: * Course_ID: Link to the associated Dataset. * Content_ID: MongoID of the parent content. * Author_ID: Mongoid of the author of this change. * CentralAuthor_ID: Mongoid of the Central author of this change. * _id: Mongodb ID. * ParentPostID : (string) which appears to be an index to a target post; * ThreadID: (int) thread ID of the post, similar for content, children and subchildren. * data: (string) of the change which appears to be another key; * uid: (string) the uid of the individual making the change; * anon: (string) whether or not the change is anonymous; * type: (string) the type of the change; and * when: (datetime) when the change was made. """ # --------------------------------------------- # Mongometa information. # ============================================= class __mongometa__: session = ModSocDB.Session name = "piazza_content_changelog" # ------------------------------------------------ # Link information. # ================================================ # Link to the associated Dataset. Course_ID = ForeignIdProperty('Dataset') Course = RelationProperty('Dataset') # Parent Piazza Content. # ------------------------------------------------ Content_ID = ForeignIdProperty('PiazzaContent') Content = RelationProperty('PiazzaContent') # Link to the associated user. Author_ID = ForeignIdProperty('PiazzaUser.PiazzaUser') Author = RelationProperty('PiazzaUser.PiazzaUser') # Direct link to the central author instance. CentralAuthor_ID = ForeignIdProperty('User') CentralAuthor = RelationProperty('User') # ------------------------------------------------ # Fields # ================================================ # Mongodb object ID (unique). _id = FieldProperty(schema.ObjectId) # The ChangeLog is a record of when updates were # made to the relevant content. This stores: # 'to': which appears to be an index to a target post; # data: of the change which appears to be another key; # uid: the uid of the individual making the change; # anon: whether or not the change is anonymous; # type: the type of the change; and # when: it took place (appears to be GMT) # int for post number. ParentPostID = FieldProperty(str) data = FieldProperty(str) uid = FieldProperty(str) anon = FieldProperty(str) type = FieldProperty(str) when = FieldProperty(datetime.datetime) ThreadID = FieldProperty(int) # --------------------------------------------- # Anonymization Code. # ============================================= @staticmethod def overwriteUserData(DatasetID=None, Timeout=False): Changelogs = findAllChangeLogs(DatasetID=DatasetID) for C in Changelogs: C.uid = None ModSocDB.Session.flush()
class PiazzaUser(MappedClass): """ The PiazzaUser class represents a wrapper for the user records that were pulled from the piazza data in the form of the RawPiazzaUser instances. They will be generated by code in the RawPiazzaUser module and would then generate the first and last names and perform other relevant features. Access to the fields will be provided later on as needed and anonymization when it happens will be done on this object. The user contains the following fields: Collection: piazza_users Relations: --------------------------------------------------- * Dataset: Link to the associated Dataset. * PiazzaContent_GoodTags: Relationship with good tags by this user. * PiazzaContent_HistoryEntries: Relationship with edited history elements. * PiazzaContent_ChangeLogs: ChangeLog Entries for the PiazzaContent. * PiazzaContent_Children: Children authored. * PiazzaContent_Child_Endorsements: Endorsements of child posts. * PiazzaContent_Child_HistoryEntries: History of children. * PiazzaContent_Child_Subchildren: Subchildren authored by this user. Fields: ------------------------------------------------------------- * Course_ID: ID of the associated Dataset. * _id: Mongodb object ID (unique). * user_id: (string) Piazza unique user ID. * answers: (int) Count of answers given to questions. * posts: (int) Count of posts made. * views: (int) Count of views made. * asks: (int) Count of asks made. * name: (string) Student real name or anonymized name. * days: (int) Days active or logged in? * email: (string) User's email address (is in standard form so can be split.) * first_name: (string) first name of student extracted from name field. * middle_name: (string) middle name of student extracted from name field. * last_name: (string) last name of student extracted from name field. """ # --------------------------------------------- # Mongometa information. # ============================================= class __mongometa__: session = ModSocDB.Session name = "piazza_users" # --------------------------------------------- # Link Information # ============================================= # Relationship with good tags by this user. PiazzaContent_GoodTags = RelationProperty("PiazzaContent_TagGood") # Relationship with edited history elements. PiazzaContent_HistoryEntries = RelationProperty("PiazzaContent_History") # ChangeLog Entries for the PiazzaContent. PiazzaContent_ChangeLogs = RelationProperty("PiazzaContent_ChangeLog") # Children authored. PiazzaContent_Children = RelationProperty("PiazzaContent_Child") # Endorsements of child posts. PiazzaContent_Child_Endorsements = RelationProperty( "PiazzaContent_Child_Endorsement") # History of children. PiazzaContent_Child_HistoryEntries = RelationProperty( "PiazzaContent_Child_History") # Subchildren authored by this user. PiazzaContent_Child_Subchildren = RelationProperty( "PiazzaContent_Child_Subchild") # ID of the main user to which this is linked (updated on MigrateRawContent). CentralUser_ID = ForeignIdProperty("User") CentralUser = RelationProperty("User") # Link to the associated Dataset (updated on MigrateRawContent) Course_ID = ForeignIdProperty('Dataset') Course = RelationProperty('Dataset') # --------------------------------------------- # Original Fields. # # These fields are part of the original Piazza data. # as downloaded in the fields. # ============================================= # Mongodb object ID (unique). _id = FieldProperty(schema.ObjectId) # Piazza unique user ID. user_id = FieldProperty(str) # Count of answers given to questions. answers = FieldProperty(int) # Count of posts made. posts = FieldProperty(int) # Count of views made. views = FieldProperty(int) # Count of asks made. asks = FieldProperty(int) # Student real name or anonymized name. name = FieldProperty(str) # Days active or logged in? days = FieldProperty(int) # User's email address (is in standard form so can be split.) email = FieldProperty(str) # A note that may indicate specific caveats about this user ID. note = FieldProperty(str, required=False, if_missing=None) # --------------------------------------------- # Added Fields. # # These are split fields or other resolution fields that # will be added to the instance by the migration fields # below. # ============================================= # Split field for individual names. first_name = FieldProperty(str) middle_name = FieldProperty(str) last_name = FieldProperty(str) # ====================================================================== # Duplicate Methods. # # Remove Duplace elements from the database. # ====================================================================== @staticmethod def removeDuplicateUsers(DatasetID): """ Remove duplicate users from the dataset. """ UserIDs = set(collectAllPiazzaIDs()) for ID in UserIDs: Users = collectUsersByPiazzaID(ID) if (len(Users) > 1): # print "%s %d" % (ID, len(Users)) for U in Users[1:]: U.delete() ModSocDB.Session.flush() # ====================================================================== # Migration Function. # # Migrate or update the initial raw format with one that adds in the # split name fields and other data. # ====================================================================== @staticmethod def migrateRawContent(DatasetID): """ This is a static method that will extract and iterate over the raw user content to generate the additional fields that will be used for later evaluation. At the same time this will reevaluate the name fields. If the name field is the empty string then it will be set to None. It will also establish a link with an appropriate User instance in the database or, if none is present, it will create one. """ Results = [] RawUserList = PiazzaUser.query.find() for PUser in RawUserList: # Set the dataset ID. PUser.Course_ID = DatasetID # If the name field is "" then set it to None so # that the absence of a name is clear. If not then # set the split name fields accordingly. We will # then search for a matching user by name for merging. # # If none is found then we will make a user. For the sake # of clarity we will treat users with None as the name as # unique and will not search for them. if (PUser.email == "") or (PUser.email == None): PUser.email = None CUser = None else: CUser = UserMod.findUserByEmail(PUser.email) # print "CUser Found: %s" % (CUser) if (CUser == None): PUser.makeSplitNameFields() first_name = PUser.first_name.title( ) if PUser.first_name else "" last_name = PUser.last_name.title() if PUser.last_name else "" CUser = UserMod.findUserByFirstLast(first_name, last_name) # print "CUser Found: %s" % (CUser) if (CUser == None): CUser = UserMod.User.makeNewUser( DatasetID=DatasetID, Name=PUser.name.title(), Email=PUser.email, FirstName=first_name, MiddleName=PUser.middle_name, LastName=last_name, PiazzaUserID=PUser._id) # print "CUser Made: %s" % (CUser) else: CUser.piazza_alt_email = PUser.email # ModSocDB.Session.flush() # And now set the appropriate link information to the # central user. PUser.CentralUser_ID = CUser._id CUser.PiazzaUser_ID = PUser._id CUser.PiazzaID = PUser.user_id ModSocDB.Session.flush() Results.append(CUser) ModSocDB.Session.flush() return Results # Add pushCentralAuthor(self) which goes over linked items and sets the # CentralAuthor_ID to be the same as the local central author. def makeSplitNameFields(self): """ Split the user name into personal and family names so that those can be used for searching in the text. This uses the name_tools library to segment the name into the standard blocks (Honorific, Personal Name, Family Name, Modifier). It will then split the personal name on whitespace to get the first name. """ # print self.name Dict = NLP.makeSplitNameDict(self.name) self.first_name = Dict["FirstName"] self.middle_name = Dict["MiddleName"] self.last_name = Dict["LastName"] # print self return Dict # --------------------------------------------- # Simple Accessors/Settors # ============================================= def getUserID(self): """ Get the Piazza User ID set in the system. """ return self.user_id def setUserID(self, NewUID): """ Set the user_id field. """ self.user_id = NewUID def getName(self): """ Get the User name. """ return self.name def setName(self, NewName): """ Set the user name. """ self.name = NewName def getFirstName(self): """ Get the first_name value. """ return self.first_name def setFirstName(self, NewName): """ Set the first_name field. """ self.first_name = NewName def getMiddleName(self): """ Get the middle_name value. """ return self.middle_name def setMiddleName(self, NewName): """ Set the middle_name field. """ self.middle_name = NewName def getLastName(self): """ Get the last_name value. """ return self.last_name def setLastName(self, NewName): """ Set the last_name field. """ self.last_name = NewName def getEmail(self): """ Get the email. """ return self.email def setEmail(self, NewEmail): """ Set the new email. """ self.email = NewEmail # --------------------------------------------- # Complex Accessors/Settors # ============================================= def getSplitEmail(self): """ If the e-mail is not null split it around the @ and return the pairs as a 2-tuple. """ Index = self.email.find('@') if (Index == -1): return (None, None) else: return (self.email[:Index], self.email[Index + 1:]) def getSplitName(self): """ This accessor uses the name_tools library to get the split name contents as a tuple which will then be returned. It is also used to set the standard first-name, middleName, and LastName fields above. """ return name_tools.split(self.name) @staticmethod def overwriteUserData(DatasetID=None, Timeout=False): """ This static method will query for all defined users and will overwrite their ID information based upon the information in the associated User class instance. It is defined here as a static method but will be called primarily from the anonymization code after the upstream users have been updated. Most of the actual work is done by the syncPiazzaAuthorIDs method which does the scut work. """ Users = findAllUsers(DatasetID=DatasetID, Timeout=Timeout) for U in Users: U.user_id = None U.syncPiazzaAuthorIDs() ModSocDB.Session.flush() def syncPiazzaAuthorIDs(self): """ As part of the anonymization process it is necessary to reset the anonymous user ID. This code will first pull the name and ID information from the user instance and will then set the values locally. This code will then iterate over all items that are linked to this user and that have a set author or uid field associated with it. These will then be reset to the current user ID. This assumes that the links are already set correctly and that the sole purpose is a brute-force reset of the link code. This will also foceably sync user names. Facebook IDs and photos will simply be purged if present. NOTE:: This makes no attempt to retain separate facebook IDs emails or photos from the good tags or endorsements for later use. """ NewID = self.CentralUser.getLocalUserID() NewName = self.CentralUser.getName() NewFirstName = self.CentralUser.getFirstName() NewMiddleName = self.CentralUser.getMiddleName() NewLastName = self.CentralUser.getLastName() NewEmail = self.CentralUser.getEmail() # Set the local information. self.setName(NewName) self.setFirstName(NewFirstName) self.setMiddleName(NewMiddleName) self.setLastName(NewLastName) self.setEmail(NewEmail) # The user ID information is on the id field of the tags. for Tag in self.PiazzaContent_GoodTags: Tag.id = NewID Tag.name = NewName Tag.email = None Tag.photo = None Tag.facebook_id = None # The UID author info is on the History entries. for Hist in self.PiazzaContent_HistoryEntries: Hist.uid = NewID # ChangeLog Entries for the PiazzaContent. for Change in self.PiazzaContent_ChangeLogs: Change.uid = NewID # Children authored. for Child in self.PiazzaContent_Children: Child.uid = NewID Child.id = None Child.overwriteUserData() # Endorsements of child posts. for Endorse in self.PiazzaContent_Child_Endorsements: Endorse.id = NewID Endorse.name = NewName Endorse.email = None Endorse.photo = None Endorse.facebook_id = None # History of children. for ChildHist in self.PiazzaContent_Child_HistoryEntries: ChildHist.id = NewID # Subchildren authored by this user. for Subchild in self.PiazzaContent_Child_Subchildren: Subchild.uid = NewID