class Program(linkable_model.Linkable): """The Program model, representing a Program ran by a Sponsor.""" _messages_model = ProgramMessages #: string used as a prefix of various key names for other models #TODO(daniel): eliminate this prefix = 'program' #: Identifier of the program which is be the last part of its unique key name program_id = db.StringProperty(required=True, verbose_name=translation.ugettext('Program ID')) program_id.help_text = translation.ugettext( 'Used as part of various URL links throughout the site.') #: Reference to the sponsor of the program. Its key_name is used as the first #: part of program's unique key name sponsor = db.ReferenceProperty(required=True, reference_class=sponsor_model.Sponsor, collection_name='programs', verbose_name=translation.ugettext('Sponsor')) #: Required field storing name of the group. name = db.StringProperty(required=True, verbose_name=translation.ugettext('Name')) name.group = GENERAL_INFO_GROUP name.help_text = translation.ugettext( 'Complete, formal name of the program.') #: Required field storing short name of the group. #: It can be used for displaying group as sidebar menu item. short_name = db.StringProperty(required=True, verbose_name=translation.ugettext('Short name')) short_name.group = GENERAL_INFO_GROUP short_name.help_text = translation.ugettext( 'Short name used for sidebar menu') #: Required field storing description of the group. description = db.TextProperty(required=True, verbose_name=translation.ugettext('Description')) description.group = GENERAL_INFO_GROUP description.help_text = translation.ugettext( '<small><i>for example:</i></small><br>' '<tt><b>GSoC 2009</b> is the <i>Google Summer of Code</i>,' ' but in <u>2009</u>!</tt><br><br>' '<small><i>(rich text formatting is supported)</i></small>') #: Number of accepted organizations nr_accepted_orgs = db.IntegerProperty( required=False, verbose_name=translation.ugettext('#accepted orgs')) nr_accepted_orgs.group = GENERAL_INFO_GROUP nr_accepted_orgs.help_text = translation.ugettext( 'The number of accepted organizations.') #: Property that contains the minimum age of a student allowed to #: participate student_min_age = db.IntegerProperty( required=False, verbose_name=translation.ugettext('Student minimum age')) student_min_age.group = AGE_REQUIREMENTS_GROUP student_min_age.help_text = translation.ugettext( 'Minimum age of students.') #: Property that contains the maximum age of a student allowed to #: participate student_max_age = db.IntegerProperty(default=200, required=False, verbose_name=translation.ugettext('Student maximum age')) student_max_age.group = AGE_REQUIREMENTS_GROUP student_max_age.help_text = translation.ugettext( 'Maximum whole-year age of students.') #: Property that contains the date as of which above student #: minimum/maximum age requirement holds. student_min_age_as_of = db.DateProperty( required=False, verbose_name=translation.ugettext('Age as of')) student_min_age_as_of.group = AGE_REQUIREMENTS_GROUP student_min_age_as_of.help_text = translation.ugettext( 'Date on which students must satisfy age requirements.') #: Required 1:1 relationship indicating the Program the Timeline #: belongs to. timeline = db.ReferenceProperty( reference_class=timeline_model.Timeline, required=True, collection_name="program", verbose_name=translation.ugettext('Timeline')) #: Document reference property used for the Org Admin Agreement org_admin_agreement = db.ReferenceProperty( reference_class=document_model.Document, verbose_name=translation.ugettext('Organization Admin Agreement'), collection_name='org_admin_agreement') org_admin_agreement.group = PROGRAM_DOCUMENTS_GROUP org_admin_agreement.help_text = translation.ugettext( 'Document containing optional Mentor Agreement for participating as a ' 'Organization admin.') #: Document reference property used for the Mentor Agreement mentor_agreement = db.ReferenceProperty( reference_class=document_model.Document, verbose_name=translation.ugettext('Mentor Agreement'), collection_name='mentor_agreement') mentor_agreement.group = PROGRAM_DOCUMENTS_GROUP mentor_agreement.help_text = translation.ugettext( 'Document containing optional Mentor Agreement for participating as a ' 'Mentor.') #: Document reference property used for the Student Agreement student_agreement = db.ReferenceProperty( reference_class=document_model.Document, verbose_name=translation.ugettext('Student Agreement'), collection_name='student_agreement') student_agreement.group = PROGRAM_DOCUMENTS_GROUP student_agreement.help_text = translation.ugettext( 'Document containing optional Student Agreement for participating as a ' 'Student.') #: Status of the program status = db.StringProperty(required=True, default=STATUS_INVISIBLE, verbose_name=translation.ugettext('Program Status'), choices=[STATUS_INVISIBLE, STATUS_VISIBLE, STATUS_INVALID]) status.group = GENERAL_INFO_GROUP status.help_text = translation.ugettext( # TODO(nathaniel): Someone got their HTML in this Python. '<tt>%s: Program Stealth-Mode Visible to Hosts and Devs only.<br/>' '%s: Visible to everyone.<br/>' '%s: Not visible or editable by anyone.</tt>' % ( STATUS_INVISIBLE, STATUS_VISIBLE, STATUS_INVALID)) #: The document entity which contains the "About" page for the program about_page = db.ReferenceProperty( reference_class=document_model.Document, verbose_name=translation.ugettext('About page document'), collection_name='about_page') about_page.group = PROGRAM_DOCUMENTS_GROUP about_page.help_text = translation.ugettext('The document with <b>About</b>') #: The document entity which contains "Events & Timeline" page #: for the program events_page = db.ReferenceProperty( reference_class=document_model.Document, verbose_name=translation.ugettext('Events page document'), collection_name='events_page') events_page.group = PROGRAM_DOCUMENTS_GROUP events_page.help_text = translation.ugettext( 'The document for the <b>Events & Timeline</b> page') #: The url which contains the "Events & Timeline" frame events_frame_url = db.LinkProperty( verbose_name=translation.ugettext('Events page iframe url')) events_frame_url.group = GENERAL_INFO_GROUP events_frame_url.help_text = translation.ugettext( 'The iframe url for the <b>Events & Timeline</b> page') #: The document entity which contains the "Connect With Us" page #: for the program connect_with_us_page = db.ReferenceProperty( reference_class=document_model.Document, verbose_name=translation.ugettext('Connect with us document'), collection_name='connect_with_us_page') connect_with_us_page.group = PROGRAM_DOCUMENTS_GROUP connect_with_us_page.help_text = translation.ugettext( 'The document for the <b>Connect With Us</b> page') #: The document entity which contains the "Help" page #: for the program help_page = db.ReferenceProperty( reference_class=document_model.Document, verbose_name=translation.ugettext('Help document'), collection_name='help_page') help_page.group = PROGRAM_DOCUMENTS_GROUP help_page.help_text = translation.ugettext( 'The document for the <b>Help</b> page') privacy_policy_url = db.LinkProperty( verbose_name=translation.ugettext("Privacy Policy")) privacy_policy_url.group = GENERAL_INFO_GROUP privacy_policy_url.help_text = translation.ugettext( "The url for the <b>Privacy Policy</b>") #: ATOM or RSS feed URL. Feed entries are shown on the site #: page using Google's JavaScript blog widget feed_url = db.LinkProperty(verbose_name=translation.ugettext('Feed URL')) feed_url.group = CONTACT_INFO_GROUP feed_url.help_text = translation.ugettext( 'The URL should be a valid ATOM or RSS feed. ' 'Feed entries are shown on the program home page.') blogger = db.LinkProperty( required=False, verbose_name=translation.ugettext("Blogger URL")) blogger.group = CONTACT_INFO_GROUP blogger.help_text = translation.ugettext( "URL of the Blogger home page for the program") gplus = db.LinkProperty( required=False, verbose_name=translation.ugettext("Google+ URL")) gplus.group = CONTACT_INFO_GROUP gplus.help_text = translation.ugettext( "URL of the Google+ home page for the program") email = db.EmailProperty( required=False, verbose_name=translation.ugettext("Program email")) email.group = CONTACT_INFO_GROUP email.help_text = translation.ugettext( "Contact email address for the program") irc = db.EmailProperty( required=False, verbose_name=translation.ugettext("IRC URL")) irc.group = CONTACT_INFO_GROUP irc.help_text = translation.ugettext( "URL of the irc channel for the program in " "the format irc://<channel>@server") #: Whether the messaging system is enabled messaging_enabled = db.BooleanProperty(default=False, verbose_name=translation.ugettext('Messaging Enabled')) messaging_enabled.group = GENERAL_INFO_GROUP messaging_enabled.help_text = translation.ugettext( 'Indicates if the messaging system should be enabled.') #: Property pointing to the file with predefined schools. schools = blobstore.BlobReferenceProperty() def getProgramMessages(self): def get_or_create_txn(): entity = type(self)._messages_model.all().ancestor(self).get() if not entity: entity = self._messages_model(parent=self) entity.put() return entity return db.run_in_transaction(get_or_create_txn)
class CrawlSkipUrl(db.Model): url = db.LinkProperty() lastseen = db.DateTimeProperty(auto_now_add=True) added_on = db.DateTimeProperty(auto_now_add=True)
class Client(db.Model): id = db.StringProperty(required=True) name = db.StringProperty(required=True) created = db.DateTimeProperty(required=True, auto_now_add=True) last_modified = db.DateTimeProperty(required=True, auto_now=True) domain = db.LinkProperty(required=True)
class Profile(soc.models.linkable.Linkable): """Per-program user information. Parent: soc.models.user.User """ #: A required many:1 relationship that ties (possibly multiple #: entities of) Role details to a unique User. A Role cannot #: exist unassociated from a login identity and credentials. The #: back-reference in the User model is a Query named 'roles'. user = db.ReferenceProperty(reference_class=soc.models.user.User, required=True, collection_name='roles') #: A reference to program entity to which the profile corresponds. #: Each profile is created for exactly one program. If the same #: user participates in more of them, a separate profile must be created #: for each. # TODO(daniel): make this field required when it is updated for # all existing entities program = db.ReferenceProperty( reference_class=soc.models.program.Program, required=False, collection_name='profiles') #: Required field storing publicly-displayed name. Can be a real name #: (though this is not recommended), or a nick name or some other public #: alias. Public names can be any valid UTF-8 text. public_name = db.StringProperty( required=True, verbose_name=ugettext('Public name')) public_name.help_text = ugettext( 'Human-readable name (UTF-8) that will be displayed publicly on the' ' site.') public_name.group = PUBLIC_INFO_GROUP #==================================================================== # (public) name information #==================================================================== #: Required field storing the parts of the Role's name #: corresponding to the field names; displayed publicly. #: given_name can only be ASCII, not UTF-8 text, because it is #: used, for example, as part of the shipping (mailing) address. given_name = db.StringProperty(required=True, verbose_name=ugettext('First (given) name')) given_name.help_text = ugettext('only A-z, 0-9 and whitespace characters') given_name.group = PUBLIC_INFO_GROUP #: Required field storing the parts of the Role's name #: corresponding to the field names; displayed publicly. #: Surname can only be ASCII, not UTF-8 text, because it is #: used, for example, as part of the shipping (mailing) address. surname = db.StringProperty( required=True, verbose_name=ugettext('Last (family) name')) surname.help_text = ugettext('only A-z, 0-9 and whitespace characters') surname.group = PUBLIC_INFO_GROUP #: Optional field used as a display name, such as for awards #: certificates. Should be the entire name in the format #: the Role would like it displayed (could be surname followed by #: given name in some cultures, for example). Display names can be #: any valid UTF-8 text. name_on_documents = db.StringProperty( verbose_name=ugettext('Legal name')) name_on_documents.help_text = ugettext( 'Optional field used as a display name, such as for documents like ' 'awards certificates. Should be the entire name in the format ' 'the person would like it displayed (could be family name followed ' 'by given name in some cultures, for example). Legal name can be ' 'any valid UTF-8 text.') name_on_documents.group = PUBLIC_INFO_GROUP #==================================================================== # (public) contact information #==================================================================== #: Optional field storing Instant Messaging network; displayed publicly. im_network = db.StringProperty( verbose_name=ugettext('IM Network')) im_network.help_text = ugettext( 'examples: irc:irc.freenode.net xmpp:gmail.com/Home') im_network.group = PUBLIC_INFO_GROUP #: Optional field storing Instant Messaging handle; displayed publicly. im_handle = db.StringProperty( verbose_name=ugettext('IM Handle')) im_handle.help_text = ugettext( 'personal identifier, such as: screen name, IRC nick, user name') im_handle.group = PUBLIC_INFO_GROUP #: Optional field storing a home page URL; displayed publicly. home_page = db.LinkProperty( verbose_name=ugettext('Home Page URL')) home_page.group = PUBLIC_INFO_GROUP #: Optional field storing a blog URL; displayed publicly. blog = db.LinkProperty( verbose_name=ugettext('Blog URL')) blog.group = PUBLIC_INFO_GROUP #: Optional field storing a URL to an image, expected to be a #: personal photo (or cartoon avatar, perhaps); displayed publicly. photo_url = db.LinkProperty( verbose_name=ugettext('Thumbnail Photo URL')) photo_url.help_text = ugettext( 'URL of 64x64 pixel thumbnail image') photo_url.group = PUBLIC_INFO_GROUP #==================================================================== # (private) contact information #==================================================================== #: Required field used as the contact mechanism for the program #: Role (for example the address the system sends emails to). email = db.EmailProperty( required=True, verbose_name=ugettext('Email Address')) email.group = CONTACT_INFO_GROUP email.help_text = ugettext("This is the address we send all notifications to.") #: Required field containing residence street address; kept private. #: Residence street address can only be ASCII, not UTF-8 text, because #: it may be used as a shipping address. res_street = db.StringProperty(required=True, verbose_name=ugettext('Street Address 1')) res_street.help_text = ugettext( 'street number and name, ' 'only A-z, 0-9 and whitespace characters') res_street.group = CONTACT_INFO_GROUP #: Optional field containing the 2nd line for the residence street address; #: kept private. #: Can only be ASCII, not UTF-8 text, because #: it may be used as a shipping address. res_street_extra = db.StringProperty(required=False, verbose_name=ugettext('Street Address 2')) res_street_extra.help_text = ugettext( '2nd address line usually for apartment numbers. ' 'only A-z, 0-9 and whitespace characters') res_street_extra.group = CONTACT_INFO_GROUP #: Required field containing residence address city; kept private. #: Residence city can only be ASCII, not UTF-8 text, because it #: may be used as a shipping address. res_city = db.StringProperty(required=True, verbose_name=ugettext('City')) res_city.help_text = ugettext( 'only A-z, 0-9 and whitespace characters') res_city.group = CONTACT_INFO_GROUP #: Optional field containing residence address state or province; kept #: private. Residence state/province can only be ASCII, not UTF-8 #: text, because it may be used as a shipping address. res_state = db.StringProperty( verbose_name=ugettext('State/Province')) res_state.help_text = ugettext( 'optional if country/territory does not have states or provinces, ' 'only A-z, 0-9 and whitespace characters') res_state.group = CONTACT_INFO_GROUP #: Required field containing residence address country or territory; kept #: private. res_country = db.StringProperty(required=True, verbose_name=ugettext('Country/Territory'), choices=countries.COUNTRIES_AND_TERRITORIES) res_country.group = CONTACT_INFO_GROUP #: Required field containing residence address postal code (ZIP code in #: the United States); kept private. Residence postal code can only be #: ASCII, not UTF-8 text, because it may be used as a shipping address. res_postalcode = db.StringProperty(required=True, verbose_name=ugettext('ZIP/Postal Code')) res_postalcode.help_text = ugettext( 'only A-z, 0-9 and whitespace characters') res_postalcode.group = CONTACT_INFO_GROUP #: Required field containing a phone number that will be used to #: contact the user, also supplied to shippers; kept private. phone = db.PhoneNumberProperty( required=True, verbose_name=ugettext('Phone Number')) phone.help_text = ugettext( 'include complete international calling number with country code, ' 'use numbers only') phone.group = CONTACT_INFO_GROUP #: Optional field containing a separate recipient name; kept #: private. Recipient name can only be ASCII, not UTF-8 text ship_name = db.StringProperty( verbose_name=ugettext('Full Recipient Name')) ship_name.help_text = ugettext( 'Fill in the name of the person who should be receiving your packages.') ship_name.group = SHIPPING_INFO_GROUP #: Optional field containing a separate shipping street address; kept #: private. If shipping address is not present in its entirety, the #: residence address will be used instead. Shipping street address can only #: be ASCII, not UTF-8 text, because, if supplied, it is used as a #: shipping address. ship_street = db.StringProperty( verbose_name=ugettext('Shipping Street Address 1')) ship_street.help_text = ugettext( 'Street number and name, only A-z, 0-9 and whitespace characters.') ship_street.group = SHIPPING_INFO_GROUP #: Optional field containing a 2nd line for the shipping street address; kept #: private. If shipping address is not present in its entirety, the #: residence address will be used instead. Shipping street address can only #: be ASCII, not UTF-8 text, because, if supplied, it is used as a #: shipping address. ship_street_extra = db.StringProperty( verbose_name=ugettext('Shipping Street Address 2')) ship_street_extra.help_text = ugettext( '2nd address line usually used for apartment numbers, ' 'only A-z, 0-9 and whitespace characters.') ship_street_extra.group = SHIPPING_INFO_GROUP #: Optional field containing shipping address city; kept private. #: Shipping city can only be ASCII, not UTF-8 text, because, if #: supplied, it is used as a shipping address. ship_city = db.StringProperty( verbose_name=ugettext('Shipping City')) ship_city.help_text = ugettext( 'Only A-z, 0-9 and whitespace characters.') ship_city.group = SHIPPING_INFO_GROUP #: Optional field containing shipping address state or province; kept #: private. Shipping state/province can only be ASCII, not UTF-8 #: text, because, if supplied, it is used as a shipping address. ship_state = db.StringProperty( verbose_name=ugettext('Shipping State/Province')) ship_state.help_text = ugettext( 'Optional if country/territory does not have states or provinces, ' 'Only A-z, 0-9 and whitespace characters.') ship_state.group = SHIPPING_INFO_GROUP #: Optional field containing shipping address country or territory; kept #: private. ship_country = db.StringProperty( verbose_name=ugettext('Shipping Country/Territory'), choices=countries.COUNTRIES_AND_TERRITORIES) ship_country.help_text = ugettext( 'Only A-z, 0-9 and whitespace characters.') ship_country.group = SHIPPING_INFO_GROUP #: Optional field containing shipping address postal code (ZIP code in #: the United States); kept private. Shipping postal code can only be #: ASCII, not UTF-8 text, because, if supplied, it is used as a #: shipping address. ship_postalcode = db.StringProperty( verbose_name=ugettext('Shipping ZIP/Postal Code')) ship_postalcode.help_text = ugettext( 'Only A-z, 0-9 and whitespace characters') ship_postalcode.group = SHIPPING_INFO_GROUP #==================================================================== # (private) personal information #==================================================================== #: Required field containing the Role's birthdate (for #: determining Program participation eligibility); kept private. birth_date = db.DateProperty( required=True, verbose_name=ugettext('Birth Date')) birth_date.help_text = ugettext( 'format YYYY-MM-DD, required for determining program eligibility') birth_date.group = PRIVATE_INFO_GROUP #: Optional field indicating choice of t-shirt fit; kept private. tshirt_style = db.StringProperty( verbose_name=ugettext('T-shirt Style'), choices=('male', 'female')) tshirt_style.group = PRIVATE_INFO_GROUP #: Optional field indicating choice of t-shirt, from XXS to XXXL; #: kept private. tshirt_size = db.StringProperty( verbose_name=ugettext('T-shirt Size'), choices=('XXS', 'XS', 'S', 'M', 'L', 'XL', 'XXL', 'XXXL')) tshirt_size.group = PRIVATE_INFO_GROUP tshirt_size.help_text = ugettext('See also ' '<a href="http://bit.ly/ayGxJk" target="_blank"> for women</a> and ' '<a href="http://bit.ly/8ZrywF" target="_blank">for men</a>.') #: Optional field indicating gender; #: kept private. gender = db.StringProperty( verbose_name=ugettext('Gender'), choices=('male', 'female', 'other')) gender.group = PRIVATE_INFO_GROUP #: Property to gain insight into where students heard about this program program_knowledge = db.TextProperty(required=False, verbose_name=ugettext( "How did you hear about this program?")) program_knowledge.help_text = ugettext("Please be as " "specific as possible, e.g. blog post (include URL if possible), mailing " "list (please include list address), information session (please include " "location and speakers if you can), etc.") program_knowledge.group = PRIVATE_INFO_GROUP #: field storing wheter the User has agreed to the site-wide Terms of Service. #: (Not a required field because the Terms of Service might not be present #: when the first User profile is created when bootstrapping the site.) agreed_to_tos = db.BooleanProperty(required=False, default=False, verbose_name=ugettext('I Agree to the Terms of Service')) agreed_to_tos.help_text = ugettext( 'Indicates whether the user agreed to this role Terms of Service.') agreed_to_tos.group = TERMS_OF_SERVICE_GROUP #: field storing when the User has agreed to the site-wide Terms of Service. #: (Not a required field because the Terms of Service might not be present #: when the first User profile is created when bootstrapping the site.) agreed_to_tos_on = db.DateTimeProperty(required=False, default=None, verbose_name=ugettext('Has agreed to the Terms of Service on')) agreed_to_tos_on.help_text = ugettext( 'Indicates when the user agreed to this role Terms of Service.') agreed_to_tos.group = TERMS_OF_SERVICE_GROUP #: field storing the status of this role #: Active means that this role can exercise all it's privileges. #: Invalid means that a role should not be able to excercise any #: priviliges. status = db.StringProperty(default='active', choices=['active', 'invalid'], verbose_name=ugettext('Status of this Role')) status.help_text = ugettext('Indicates the status of the role ' 'concerning which privileges may be used.') #==================================================================== #notification settings #==================================================================== notify_new_requests = db.BooleanProperty(required=False, default=True, verbose_name=ugettext('Notify of new requests')) notify_new_requests.help_text = ugettext( 'Whether to send an email notification when new requests are submitted.') notify_new_requests.group = NOTIFICATION_SETTINGS_GROUP notify_request_handled = db.BooleanProperty(required=False, default=True, verbose_name=ugettext('Notify of handled requests')) notify_request_handled.help_text = ugettext( 'Whether to send an email notification when your request is handled.') notify_request_handled.group = NOTIFICATION_SETTINGS_GROUP #==================================================================== #specific roles information #==================================================================== #: field storing whether the User is a student is_student = db.BooleanProperty(required=False, default=False, verbose_name=ugettext('Is student')) #: field storing whether the User is a mentor is_mentor = db.BooleanProperty(required=False, default=False, verbose_name=ugettext('Is Mentor')) #: field storing whether the User is an org amdin is_org_admin = db.BooleanProperty(required=False, default=False, verbose_name=ugettext('Is Organization Administrator')) #: List of organizations that the user with the role is a mentor for mentor_for = db.ListProperty(item_type=db.Key, default=[]) mentor_for.help_text = ugettext('List of organizations for which the user ' 'is a mentor.') #: List of organizations that the user with the role is an org admin for org_admin_for = db.ListProperty(item_type=db.Key, default=[]) org_admin_for.help_text = ugettext('List of organizations for which ' 'the user is an organization admin.') #: Points to student specific information if the user has a student role student_info = db.ReferenceProperty(required=False, default=None, reference_class=StudentInfo) created_on = db.DateTimeProperty(auto_now_add=True) modified_on = db.DateTimeProperty(auto_now=True) def _fix_name(self, commit=True): """Retrieves the name property from the parent user. """ pass def name(self): """Property as 'name' for use in common templates. """ return self.public_name def document_name(self): """Property as 'document_name' used on for example award certificates. """ if self.name_on_documents: return self.name_on_documents else: return self.name() def full_name(self): """Property which returns given name followed by surname and separated by a single space character. """ return '%s %s' % (self.given_name, self.surname) def shipping_name(self): """Property recipient_name that returns shipping name if shipping address is set else the given name and surname. """ return self.ship_name if self.hasShippingAddress() else self.given_name + " " + self.surname def shipping_street(self): """Property shipping_street that returns shipping street if shipping address is set else the residential street. """ return self.ship_street if self.hasShippingAddress() else self.res_street def shipping_street_extra(self): """Property shipping_street_extra that returns the 2nd shipping address line if shipping address is set else the residential 2nd address line. """ return self.ship_street_extra if self.hasShippingAddress() else \ self.res_street_extra def shipping_city(self): """Property shipping_city that returns shipping city if shipping address is set else the residential city. """ return self.ship_city if self.hasShippingAddress() else self.res_city def shipping_state(self): """Property shipping_state that returns shipping state if shipping address is set else the residential state. """ return self.ship_state if self.hasShippingAddress() else self.res_state def shipping_country(self): """Property shipping_country that returns shipping country if shipping address is set else the residential country. """ return self.ship_country if self.hasShippingAddress() else self.res_country def shipping_postalcode(self): """Property shipping_postalcode that returns the shipping postal code if shipping address set else the residential postal code. """ return self.ship_postalcode if self.hasShippingAddress() else \ self.res_postalcode def hasShippingAddress(self): """Checks if the required fields for the shipping address are set. """ return self.ship_city and self.ship_country and self.ship_postalcode and \ self.ship_street def ccTld(self): """Property as 'ccTld' for use in Maps. """ return countries.COUNTRIES_TO_CCTLD[self.res_country]
class Feedback(db.Model): name = db.LinkProperty()
class Post(db.Model): title = db.StringProperty(required=True) url = db.LinkProperty(required=False) message = db.TextProperty() user = db.ReferenceProperty(User, collection_name='posts') created = db.DateTimeProperty(auto_now_add=True) karma = db.FloatProperty() edited = db.BooleanProperty(default=False) twittered = db.BooleanProperty(default=False) def to_json(self): return { 'id': str(self.key()), 'title': self.title, 'message': self.message, 'created': self.created.strftime("%s"), 'user': self.user.nickname, 'comment_count': self.cached_comment_count, 'url': self.url, 'votes': self.prefetched_sum_votes } def url_netloc(self): return urlparse(self.url).netloc def can_edit(self): session = get_current_session() if session.has_key('user'): user = session['user'] if self.user.key() == user.key() or user.admin: return True return False # This is duplicated code from the pre_fetcher # Do not edit if you don't update those functions too def sum_votes(self): val = memcache.get("p_" + str(self.key())) if val is not None: return val else: val = self.votes.count() memcache.add("p_" + str(self.key()), val, 3600) return val # This is duplicated code from the pre_fetcher # Do not edit if you don't update those functions too def already_voted(self): session = get_current_session() if session.has_key('user'): user = session['user'] # hit memcache for this memValue = memcache.get("vp_" + str(self.key()) + "_" + str(user.key())) if memValue is not None: return memValue == 1 else: vote = Vote.all().filter("user ="******"post =", self).fetch(1) memcache.add("vp_" + str(self.key()) + "_" + str(user.key()), len(vote), 3600) return len(vote) == 1 else: return False def remove_from_memcache(self): memcache.delete("pc_" + str(self.key())) memcache.delete("p_" + str(self.key())) session = get_current_session() if session.has_key('user'): user = session['user'] user.remove_from_memcache() memcache.delete("vp_" + str(self.key()) + "_" + str(user.key())) self.calculate_karma() def calculate_karma(self): delta = (datetime.now() - self.created) seconds = delta.seconds + delta.days * 86400 hours = seconds / 3600 + 1 votes = self.sum_votes() gravity = 1.8 karma = (votes - 1) / pow((hours + 2), gravity) self.karma = karma self.put() @staticmethod def remove_cached_count_from_memcache(): memcache.delete("Post_count") @staticmethod def get_cached_count(): memValue = memcache.get("Post_count") if memValue is not None: return memValue else: count = Post.all().count() memcache.add("Post_count", count, 3600) return count
class Website(db.Model): #person = db.ReferenceProperty(required = True, collection_name = 'persons') address = db.LinkProperty(required=True) group = db.ReferenceProperty(required=True, collection_name='research_groups') authors = db.ListProperty(db.Key, required=True)
class Menu(db.Model): user_id = db.StringProperty(required=True) dish_name = db.StringProperty() price = db.FloatProperty() photo_link = db.LinkProperty() restriction_list = db.StringListProperty()
class Agency(GeoModel): # properties straight out of the NTD import ntd_id = db.StringProperty() gtfs_data_exchange_id = db.ListProperty(unicode) name = db.StringProperty(required=True) short_name = db.StringProperty() city = db.StringProperty(required=True) state = db.StringProperty(required=True) country = db.StringProperty(default="us") postal_code = db.IntegerProperty() address = db.StringProperty() agency_url = db.LinkProperty() executive = db.StringProperty() executive_email = db.EmailProperty() twitter = db.StringProperty() contact_email = db.EmailProperty() phone = db.StringProperty() service_area_population = db.IntegerProperty() passenger_miles = db.IntegerProperty() # bookkeeping updated = db.DateTimeProperty() date_opened = db.DateTimeProperty() # developer amenities dev_site = db.LinkProperty() #None if no developer site arrival_data = db.LinkProperty() #Link to arrival data source; None if no arrival data position_data = db.LinkProperty() #Link to position data source; None if no position data standard_license = db.StringProperty() #String of standard license like "GPL"; None if no standard license # slugs nameslug = db.StringProperty() cityslug = db.StringProperty() stateslug = db.StringProperty() countryslug = db.StringProperty() urlslug = db.StringProperty() # whether or not the agency is a non-government organization private = db.BooleanProperty(default=False) def __init__(self, *args, **kwargs): # this loads everything to self that's passed as a kwarg, making required and default attribs safe to use GeoModel.__init__(self, *args, **kwargs) if "location" in kwargs: self.update_location() self.update_slugs() def __str__(self): return "%s in %s, %s (%s)" % (self.name, self.city, self.state, self.country) def update_slugs(self): self.nameslug = slugify(self.name) self.cityslug = slugify(self.city) self.stateslug = slugify(self.state) self.countryslug = slugify(self.country) self.urlslug = "%s/%s/%s/%s"%(self.countryslug,self.stateslug,self.cityslug,self.nameslug) def to_jsonable(self): return { 'ntd_id':self.ntd_id, 'gtfs_data_exchange_id':self.gtfs_data_exchange_id, 'date_opened':self.date_opened.isoformat(" ") if self.date_opened else None, 'passenger_miles':self.passenger_miles, 'is_public':self.is_public, 'name': cgi.escape(self.name), 'short_name': cgi.escape(self.short_name) if self.short_name else None, 'city': cgi.escape(self.city), 'urlslug': self.urlslug, 'state': cgi.escape(self.state), 'details_url': self.details_url, 'key_encoded': str(self.key()), 'has_real_time_data': self.has_real_time_data, 'latitude':(self.location.lat if self.location else None), 'longitude':(self.location.lon if self.location else None), 'executive':cgi.escape(self.executive) if self.executive else None, 'executive_email':cgi.escape(self.executive_email) if self.executive_email else None, 'agency_url':cgi.escape(self.agency_url) if self.agency_url else None, } @staticmethod def fetch_all_agencies_as_jsonable(): memcache_key = "all-agencies-as-jsonable" all_as_jsonable = memcache.get(memcache_key) if all_as_jsonable is None: all_as_jsonable = [agency.to_jsonable() for agency in Agency.all()] memcache.set(memcache_key, all_as_jsonable, time = settings.MEMCACHE_DEFAULT_SECONDS) return all_as_jsonable @property def details_url(self): return reverse("agencies_all_slugs", kwargs={"countryslug": self.countryslug, "stateslug": self.stateslug, "cityslug": self.cityslug, "nameslug": self.nameslug}) @property def is_public(self): return (self.date_opened != None) @staticmethod def fetch_agencies_near(latitude, longitude, query = None, max_results = 50, bbox_side_in_miles = settings.BBOX_SIDE_IN_MILES): bounding_box = square_bounding_box_centered_at(latitude, longitude, bbox_side_in_miles) if query is None: query = Agency.all() return Agency.bounding_box_fetch(query, bounding_box, max_results = max_results) @property def has_real_time_data(self): return (self.arrival_data != None) or (self.position_data != None) @staticmethod def all_public_agencies(): """Return a query to all Agency entities marked 'public' by Brandon's import scripts.""" return Agency.all().filter('date_opened !=', None) @staticmethod def get_state_list(): #factoring this out since we want all states all the time #and because we want to memcache this #todo: add other countries (return dict where value includes proper url) mem_result = memcache.get('all_states') if not mem_result: states = uniquify([(a.countryslug,a.stateslug) for a in Agency.all()]) states.sort(key=lambda x:x[1]) mc_added = memcache.add('all_states', states, settings.MEMCACHE_DEFAULT_SECONDS) else: states = mem_result return states @staticmethod def get_country_list(): #factoring this out since we want all states all the time #and because we want to memcache this #todo: add other countries (return dict where value includes proper url) mem_result = memcache.get('all_countries') if not mem_result: countries = uniquify([a.countryslug for a in Agency.all()]) countries.sort() mc_added = memcache.add('all_countries', countries, settings.MEMCACHE_DEFAULT_SECONDS) else: countries = mem_result return countries @staticmethod def fetch_for_slugs(countryslug = None, stateslug = None, cityslug = None): agency_query = Agency.all() if cityslug: agency_query = agency_query.filter('cityslug =', cityslug) if stateslug: agency_query = agency_query.filter('stateslug =', stateslug) if countryslug: agency_query = agency_query.filter('countryslug =',countryslug) return [agency for agency in agency_query] @staticmethod def fetch_explicitly_supported_for_transit_app(transit_app): """Return a list of Agency entities that are explicitly supported by the transit app.""" return Agency.get(transit_app.explicitly_supported_agency_keys) @staticmethod def iter_explicitly_supported_for_transit_app(transit_app): """Return an iterator over Agency entities that are explicitly supported by the transit app.""" # Not a true lazy iterator, but this should be plenty fast for agency in Agency.fetch_explicitly_supported_for_transit_app(transit_app): yield agency @staticmethod def fetch_for_transit_app(transit_app, uniquify = True): """Return a list of Agency entities, by default unique, that the given transit app supports""" return [agency for agency in Agency.iter_for_transit_app(transit_app, uniquify)] @staticmethod def iter_for_transit_app(transit_app, uniquify = True): """Return an iterator over Agency entities, by default unique, that the given transit app supports""" seen_set = set() for explicit_agency in iter_uniquify(Agency.iter_explicitly_supported_for_transit_app(transit_app), seen_set, uniquify): yield explicit_agency if transit_app.supports_all_public_agencies: for public_agency in iter_uniquify(Agency.all_public_agencies(), seen_set, uniquify): yield public_agency @staticmethod def fetch_for_transit_apps(transit_apps, uniquify = True): """Return a list of Agency entities, by default unique, that at least one transit application in the transit_apps list supports.""" return [agency for agency in Agency.iter_for_transit_apps(transit_apps, uniquify)] @staticmethod def iter_for_transit_apps(transit_apps, uniquify = True): """Return an iterator over Agency entities, by default unique, that at least one transit application in the transit_apps list supports.""" seen_set = set() for transit_app in transit_apps: for agency in iter_uniquify(Agency.iter_for_transit_app(transit_app, uniquify = False), seen_set, uniquify): yield agency
class Post(db.Model): title = db.StringProperty(required=True) url = db.LinkProperty(required=False) message = db.TextProperty() user = db.ReferenceProperty(User, collection_name='posts') created = db.DateTimeProperty(auto_now_add=True) karma = db.FloatProperty() def url_netloc(self): return urlparse(self.url).netloc def cached_comment_count(self): val = memcache.get("pc_" + str(self.key())) if val is not None: return str(val) else: val = self.comments.count() memcache.add("pc_" + str(self.key()), val, 3600) return str(val) def sum_votes(self): val = memcache.get("p_" + str(self.key())) if val is not None: return val else: val = self.votes.count() memcache.add("p_" + str(self.key()), val, 3600) return val def already_voted(self): session = get_current_session() if session.has_key('user'): user = session['user'] # hit memcache for this memValue = data = memcache.get("vp_" + str(self.key()) + "_" + str(user.key())) if memValue is not None: return memValue == 1 else: vote = Vote.all().filter("user ="******"post =", post).fetch(1) memcache.add("vp_" + str(self.key()) + "_" + str(user.key()), len(vote), 3600) return len(vote) == 1 else: return False def remove_from_memcache(self): memcache.delete("pc_" + str(self.key())) memcache.delete("p_" + str(self.key())) session = get_current_session() if session.has_key('user'): user = session['user'] user.remove_from_memcache() memcache.delete("vp_" + str(self.key()) + "_" + str(user.key())) self.calculate_karma() def calculate_karma(self): delta = (datetime.now() - self.created) seconds = delta.seconds + delta.days * 86400 hours = seconds / 3600 + 1 self.karma = self.sum_votes() / float(hours) self.karma = self.karma * self.karma self.put() @staticmethod def remove_cached_count_from_memcache(): memcache.delete("Post_count") @staticmethod def get_cached_count(): memValue = memcache.get("Post_count") if memValue is not None: return memValue else: count = Post.all().count() memcache.add("Post_count", count, 3600) return count
class Role(soc.models.linkable.Linkable): """Information common to Program participation for all Roles. Some details of a Role are considered "public" information, and nearly all of these are optional (except for given_name, surname, and email). Other details of a Role are kept "private" and are only provided to other Users in roles that "need to know" this information. How these fields are revealed is usually covered by Program terms of service. Role is the entity that is created when a User actually participates in some fashion in a Program. Role details could *possibly* be collected without actual participation (voluntary, opt-in, of course). A Role is a User's participation in a single Program. To avoid duplication of data entry, facilities will be available for selecting an existing Role associated with a particular User to be duplicated for participation in a new Program. A User has to have at least one Role in order to be able to create any Work (such as a Document) on the site. The easiest-to-obtain Role is probably Club Member (though Clubs can set their own membership criteria). A Role entity participates in the following relationships implemented as a db.ReferenceProperty elsewhere in another db.Model: documentation) a 1:many relationship of Documentation (tax forms, letters from schools, etc.) associated with the Role by Hosts. This relation is implemented as the 'documentation' back-reference Query of the Documentation model 'role' reference. works) a many:many relationship with Works, stored in a separate WorksRoles model, representing the Work authored by this Role. See the WorksRoles model class for details. """ #: A required many:1 relationship that ties (possibly multiple #: entities of) Role details to a unique User. A Role cannot #: exist unassociated from a login identity and credentials. The #: back-reference in the User model is a Query named 'roles'. user = db.ReferenceProperty(reference_class=soc.models.user.User, required=True, collection_name='roles') #==================================================================== # (public) name information #==================================================================== #: Required field storing the parts of the Role's name #: corresponding to the field names; displayed publicly. #: given_name can only be ASCII, not UTF-8 text, because it is #: used, for example, as part of the shipping (mailing) address. given_name = db.StringProperty(required=True, verbose_name=ugettext('First (given) name')) given_name.help_text = ugettext( '<a href="http://en.wikipedia.org/wiki/ASCII">ASCII</a> characters only' ) given_name.group = ugettext("1. Public Info") #: Required field storing the parts of the Role's name #: corresponding to the field names; displayed publicly. #: Surname can only be ASCII, not UTF-8 text, because it is #: used, for example, as part of the shipping (mailing) address. surname = db.StringProperty(required=True, verbose_name=ugettext('Last (family) name')) surname.help_text = ugettext( '<a href="http://en.wikipedia.org/wiki/ASCII">ASCII</a> characters only' ) surname.group = ugettext("1. Public Info") #: Optional field used as a display name, such as for awards #: certificates. Should be the entire name in the format #: the Role would like it displayed (could be surname followed by #: given name in some cultures, for example). Display names can be #: any valid UTF-8 text. name_on_documents = db.StringProperty( verbose_name=ugettext('Name on documents')) name_on_documents.help_text = ugettext( 'Optional field used as a display name, such as for documents like ' 'awards certificates. Should be the entire name in the format ' 'the person would like it displayed (could be family name followed ' 'by given name in some cultures, for example). Name on documents can be ' 'any valid UTF-8 text.') name_on_documents.group = ugettext("1. Public Info") #==================================================================== # (public) contact information #==================================================================== #: Optional field storing Instant Messaging network; displayed publicly. im_network = db.StringProperty(verbose_name=ugettext('IM Network')) im_network.help_text = ugettext( 'examples: irc:irc.freenode.net xmpp:gmail.com/Home') im_network.group = ugettext("1. Public Info") #: Optional field storing Instant Messaging handle; displayed publicly. im_handle = db.StringProperty(verbose_name=ugettext('IM Handle')) im_handle.help_text = ugettext( 'personal identifier, such as: screen name, IRC nick, user name') im_handle.group = ugettext("1. Public Info") #: Optional field storing a home page URL; displayed publicly. home_page = db.LinkProperty(verbose_name=ugettext('Home Page URL')) home_page.group = ugettext("1. Public Info") #: Optional field storing a blog URL; displayed publicly. blog = db.LinkProperty(verbose_name=ugettext('Blog URL')) blog.group = ugettext("1. Public Info") #: Optional field storing a URL to an image, expected to be a #: personal photo (or cartoon avatar, perhaps); displayed publicly. photo_url = db.LinkProperty(verbose_name=ugettext('Thumbnail Photo URL')) photo_url.help_text = ugettext('URL of 64x64 pixel thumbnail image') photo_url.group = ugettext("1. Public Info") #: Optional field storing the latitude provided by the Role; displayed #: publicly. latitude = db.FloatProperty(verbose_name=ugettext('Latitude')) latitude.help_text = ugettext( 'decimal degrees northerly (N), use minus sign (-) for southerly (S)') latitude.group = ugettext("1. Public Info") #: Optional field storing the longitude provided by the Role; displayed #: publicly. longitude = db.FloatProperty(verbose_name=ugettext('Longitude')) longitude.help_text = ugettext( 'decimal degrees easterly (E), use minus sign (-) for westerly (W)') longitude.group = ugettext("1. Public Info") #==================================================================== # (private) contact information #==================================================================== #: Required field used as the contact mechanism for the program #: Role (for example the address the system sends emails to). email = db.EmailProperty(required=True, verbose_name=ugettext('Email Address')) email.group = ugettext("2. Contact Info (Private)") #: Required field containing residence street address; kept private. #: Residence street address can only be ASCII, not UTF-8 text, because #: it may be used as a shipping address. res_street = db.StringProperty(required=True, verbose_name=ugettext('Street address')) res_street.help_text = ugettext( 'street number and name, ' '<a href="http://en.wikipedia.org/wiki/ASCII">ASCII</a> characters only' ) res_street.group = ugettext("2. Contact Info (Private)") #: Required field containing residence address city; kept private. #: Residence city can only be ASCII, not UTF-8 text, because it #: may be used as a shipping address. res_city = db.StringProperty(required=True, verbose_name=ugettext('City')) res_city.help_text = ugettext( '<a href="http://en.wikipedia.org/wiki/ASCII">ASCII</a> characters only' ) res_city.group = ugettext("2. Contact Info (Private)") #: Optional field containing residence address state or province; kept #: private. Residence state/province can only be ASCII, not UTF-8 #: text, because it may be used as a shipping address. res_state = db.StringProperty(verbose_name=ugettext('State/Province')) res_state.help_text = ugettext( 'optional if country/territory does not have states or provinces, ' '<a href="http://en.wikipedia.org/wiki/ASCII">ASCII</a> characters only' ) res_state.group = ugettext("2. Contact Info (Private)") #: Required field containing residence address country or territory; kept #: private. res_country = db.StringProperty( required=True, verbose_name=ugettext('Country/Territory'), choices=countries.COUNTRIES_AND_TERRITORIES) res_country.group = ugettext("2. Contact Info (Private)") #: Required field containing residence address postal code (ZIP code in #: the United States); kept private. Residence postal code can only be #: ASCII, not UTF-8 text, because it may be used as a shipping address. res_postalcode = db.StringProperty( required=True, verbose_name=ugettext('ZIP/Postal Code')) res_postalcode.help_text = ugettext( '<a href="http://en.wikipedia.org/wiki/ASCII">ASCII</a> characters only' ) res_postalcode.group = ugettext("2. Contact Info (Private)") #: Required field containing a phone number that will be used to #: contact the user, also supplied to shippers; kept private. phone = db.PhoneNumberProperty(required=True, verbose_name=ugettext('Phone Number')) phone.help_text = ugettext( 'include complete international calling number with country code') phone.example_text = ugettext( "e.g. 1650253000 for Google's Corp HQ number in the United States") phone.group = ugettext("2. Contact Info (Private)") #: field storing whether the User has agreed to publish his location publish_location = db.BooleanProperty( required=False, default=False, verbose_name=ugettext('Publish my location')) publish_location.help_text = ugettext( 'By checking this box, you are agreeing to allow your location to be' ' displayed, as given by the Marker below, on any map.' ' For instance on the map linking Students to Mentors or' ' by showing your location on your public profile page in the system.') publish_location.example_text = ugettext('You can set your location below') publish_location.group = ugettext("2. Contact Info (Private)") #: Optional field containing a separate shipping street address; kept #: private. If shipping address is not present in its entirety, the #: residence address will be used instead. Shipping street address can only #: be ASCII, not UTF-8 text, because, if supplied, it is used as a #: shipping address. ship_street = db.StringProperty( verbose_name=ugettext('Shipping Street address')) ship_street.help_text = ugettext( 'street number and name, ' '<a href="http://en.wikipedia.org/wiki/ASCII">ASCII</a> characters only' 'fill in only if not same as above') ship_street.group = ugettext("3. Shipping Info (Private and Optional)") #: Optional field containing shipping address city; kept private. #: Shipping city can only be ASCII, not UTF-8 text, because, if #: supplied, it is used as a shipping address. ship_city = db.StringProperty(verbose_name=ugettext('Shipping City')) ship_city.help_text = ugettext( '<a href="http://en.wikipedia.org/wiki/ASCII">ASCII</a> characters only' 'fill in only if not same as above') ship_city.group = ugettext("3. Shipping Info (Private and Optional)") #: Optional field containing shipping address state or province; kept #: private. Shipping state/province can only be ASCII, not UTF-8 #: text, because, if supplied, it is used as a shipping address. ship_state = db.StringProperty( verbose_name=ugettext('Shipping State/Province')) ship_state.help_text = ugettext( 'optional if country/territory does not have states or provinces, ' '<a href="http://en.wikipedia.org/wiki/ASCII">ASCII</a> characters only ' 'fill in only if not same as above') ship_state.group = ugettext("3. Shipping Info (Private and Optional)") #: Optional field containing shipping address country or territory; kept #: private. ship_country = db.StringProperty( verbose_name=ugettext('Shipping Country/Territory'), choices=countries.COUNTRIES_AND_TERRITORIES) ship_country.help_text = ugettext('fill in only if not same as above') ship_country.group = ugettext("3. Shipping Info (Private and Optional)") #: Optional field containing shipping address postal code (ZIP code in #: the United States); kept private. Shipping postal code can only be #: ASCII, not UTF-8 text, because, if supplied, it is used as a #: shipping address. ship_postalcode = db.StringProperty( verbose_name=ugettext('Shipping ZIP/Postal Code')) ship_postalcode.help_text = ugettext( '<a href="http://en.wikipedia.org/wiki/ASCII">ASCII</a> characters only' 'fill in only if not same as above') ship_postalcode.group = ugettext("3. Shipping Info (Private and Optional)") #==================================================================== # (private) personal information #==================================================================== #: Required field containing the Role's birthdate (for #: determining Program participation eligibility); kept private. birth_date = db.DateProperty(required=True, verbose_name=ugettext('Birth Date')) birth_date.help_text = ugettext( 'required for determining program eligibility') birth_date.group = ugettext("4. Private Info") birth_date.example_text = ugettext('e.g. 1999-12-31 or 12/31/1999') #: Optional field indicating choice of t-shirt fit; kept private. tshirt_style = db.StringProperty(verbose_name=ugettext('T-shirt Style'), choices=('male', 'female')) tshirt_style.group = ugettext("4. Private Info") #: Optional field indicating choice of t-shirt, from XXS to XXXL; #: kept private. tshirt_size = db.StringProperty(verbose_name=ugettext('T-shirt Size'), choices=('XXS', 'XS', 'S', 'M', 'L', 'XL', 'XXL', 'XXXL')) tshirt_size.group = ugettext("4. Private Info") tshirt_size.example_text = ugettext( 'See also ' '<a href="http://en.wikipedia.org/wiki/US_standard_clothing_size">' 'this article</a>.') #: field storing wheter the User has agreed to the site-wide Terms of Service. #: (Not a required field because the Terms of Service might not be present #: when the first User profile is created when bootstrapping the site.) agreed_to_tos = db.BooleanProperty( required=False, default=False, verbose_name=ugettext('I Agree to the Terms of Service')) agreed_to_tos.help_text = ugettext( 'Indicates whether the user agreed to this role Terms of Service.') agreed_to_tos.group = ugettext("99. Terms of Service") #: field storing when the User has agreed to the site-wide Terms of Service. #: (Not a required field because the Terms of Service might not be present #: when the first User profile is created when bootstrapping the site.) agreed_to_tos_on = db.DateTimeProperty( required=False, default=None, verbose_name=ugettext('Has agreed to the Terms of Service on')) agreed_to_tos_on.help_text = ugettext( 'Indicates when the user agreed to this role Terms of Service.') agreed_to_tos.group = ugettext("99. Terms of Service") #: field storing the status of this role #: Active means that this role can exercise all it's privileges. #: Invalid mean that this role cannot exercise it's privileges. #: Inactive means that this role cannot exercise it's data-editing #: privileges but should be able to see the data. For instance when a program #: has been marked inactive an Organization Admin should still be able to see #: the student applications. status = db.StringProperty(default='active', choices=['active', 'invalid', 'inactive'], verbose_name=ugettext('Status of this Role')) status.help_text = ugettext('Indicates the status of the role ' 'concerning which privileges may be used.') def name(self): """Property as 'name' for use in common templates. """ return '%s %s' % (self.given_name, self.surname) def document_name(self): """Property as 'document_name' used on for example award certificates. """ if self.name_on_documents: return self.name_on_documents else: return self.name() def shipping_street(self): """Property shipping_street that returns shipping street if shipping address is set else the residential street. """ return self.ship_street if self.hasShippingAddress( ) else self.res_street def shipping_city(self): """Property shipping_city that returns shipping city if shipping address is set else the residential city. """ return self.ship_city if self.hasShippingAddress() else self.res_city def shipping_state(self): """Property shipping_state that returns shipping state if shipping address is set else the residential state. """ return self.ship_state if self.hasShippingAddress() else self.res_state def shipping_country(self): """Property shipping_country that returns shipping country if shipping address is set else the residential country. """ return self.ship_country if self.hasShippingAddress( ) else self.res_country def shipping_postalcode(self): """Property shipping_postalcode that returns the shipping postal code if shipping address set else the residential postal code. """ return self.ship_postalcode if self.hasShippingAddress() else \ self.res_postalcode def hasShippingAddress(self): """Checks if the required fields for the shipping address are set. """ return self.ship_city and self.ship_country and self.ship_postalcode and \ self.ship_street def ccTld(self): """Property as 'ccTld' for use in Maps. """ return countries.COUNTRIES_TO_CCTLD[self.res_country]
class PugPe(db.Model): href = db.LinkProperty(required=True) created_at = db.DateTimeProperty(auto_now_add=True) KEY_BASE = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" BASE = 64 def code(self): ''' Return our base-62 encoded id ''' if not self.is_saved(): return None nid = self.key().id() s = [] while nid: nid, c = divmod(nid, PugPe.BASE) s.append(PugPe.KEY_BASE[c]) s.reverse() return "".join(s) def to_text(self): return 'http://pug.pe/%s' % self.code() def to_json(self): return "{\"code\":\"%s\",\"href\":\"%s\"}\n" % ('http://pug.pe/' + self.code(), self.href) def save_in_cache(self): """We don't really care if this fails""" memcache.set(self.code(), self) @staticmethod def find_or_create_by_href(href): query = db.Query(PugPe) query.filter('href =', href) u = query.get() if not u: u = PugPe(href=href) u.put() u.save_in_cache() return u @staticmethod def code_to_id(code): aid = 0L for c in code: aid *= PugPe.BASE aid += PugPe.KEY_BASE.index(c) return aid @staticmethod def find_by_code(code): try: u = memcache.get(code) except: # http://code.google.com/p/googleappengine/issues/detail?id=417 logging.error("PugPe.find_by_code() memcached error") u = None if u is not None: logging.info("PugPe.find_by_code() cache HIT: %s", str(code)) return u logging.info("PugPe.find_by_code() cache MISS: %s", str(code)) aid = PugPe.code_to_id(code) try: u = PugPe.get_by_id(int(aid)) if u is not None: u.save_in_cache() return u except db.BadValueError: return None
class Entity(db.Model): country = db.ReferenceProperty(Country, collection_name="country_entity") name = db.StringProperty() created = db.DateProperty() contact_info = db.StringProperty() myaiesec_link = db.LinkProperty() type = db.ReferenceProperty(EntityType, collection_name="Entity_Entity_Type") @staticmethod def store_entities(): ''' for et in EntityType.all(): et.delete() for country in Country.all(): country.delete() for entity in Entity.all(): entity.delete() ''' # create global and AI ai = EntityType(key_name="AI") ai.name = "AIESEC INTERNATIONAL" ai.level = 10 ai.put() mc = EntityType(key_name="MC") mc.name = "MEMBER COMMITTEE" mc.level = 20 mc.put() lc = EntityType(key_name="LC") lc.name = "LOCAL COMMITTEE" lc.level = 30 lc.put() country = Country(key_name='GLOBAL') country.name = 'GLOBAL' country.put() entity = Entity(parent=country, key_name="AIESEC INTERNATIONAL") entity.country = country entity.name = "AIESEC INTERNATIONAL" entity.type = ai entity.put() for item in aiesec_list: country_entities = item.split("=") country = Country(key_name=country_entities[0], name=country_entities[0]) country.put() if len(country_entities) > 1: entity = Entity(parent=country, key_name=mc.name + " " + country.name) entity.country = country entity.type = mc entity.name = country.name entity.put() entity_list = country_entities[1].split(";") for item in entity_list: entity = Entity(country, item) entity.name = item entity.type = lc entity.country = country entity.put() def get_active_committee(self): return Committee.gql("WHERE parent = :entity_key", entity_key=self.key()).get() def og_url(self): return "https://apps.facebook.com/iamanaiesecer/object_graph/entity/{0}/".format( self.key()) def og_image(self): return "https://i-am-an-aiesecer.appspot.com/static/object_entity.jpg"
class GSoCProject(db.Model): """Model for a GSoC project used in the GSoC workflow. Parent: soc.modules.gsoc.models.profile.Profile """ #: Required field indicating the "title" of the project title = db.StringProperty(required=True, verbose_name=ugettext('Title')) title.help_text = ugettext('Title of the project') #: Required, text field describing the project abstract = db.TextProperty(required=True, verbose_name=ugettext('Project abstract')) abstract.help_text = ugettext( 'Short abstract, summary, or snippet;' ' 500 characters or less, plain text displayed publicly') #: Text field containing all kinds of information about this project public_info = db.TextProperty( required=False, default='', verbose_name=ugettext('Additional information')) public_info.help_text = ugettext( 'Additional information about this project to be shown publicly') #: Optional, URL which can give more information about this project additional_info = db.URLProperty( required=False, verbose_name=ugettext('External resource URL')) additional_info.help_text = ugettext( 'Link to a resource containing more information about this project.') #: Optional field storing a feed URL; displayed publicly feed_url = db.LinkProperty(verbose_name=ugettext('Project Feed URL')) feed_url.help_text = ugettext( 'The URL should be a valid ATOM or RSS feed. ' 'Feed entries are shown on the public page.') #: The project can be marked to be featured on program home page. is_featured = db.BooleanProperty(default=False, required=True, verbose_name=ugettext('Featured')) is_featured.help_text = ugettext( 'Should this project be featured on the program homepage.') #: A property containing a list of Mentors assigned for this project mentors = db.ListProperty(item_type=db.Key, default=[], required=True) def getMentors(self): """Returns a list of profile_model.GSoCProfile entities which are mentors for this project. Returns: list of mentors for this project """ mentor_ndb_keys = map(ndb.Key.from_old_key, self.mentors) return [mentor for mentor in ndb.get_multi(mentor_ndb_keys) if mentor] #: The status of this project status = db.StringProperty(required=True, default=STATUS_ACCEPTED, choices=[ STATUS_ACCEPTED, STATUS_FAILED, STATUS_WITHDRAWN, STATUS_INVALID ]) #: List of all processed GradingRecords which state a pass for this project. #: This property can be used to determine how many evaluations someone has #: passed. And is also used to ensure that a GradingRecord has been #: processed. passed_evaluations = db.ListProperty(item_type=db.Key, default=[]) #: List of all processed GradingRecords which state a fail for this project. #: This is a ListProperty to ensure that the system keeps functioning when #: manual changes in GradingRecords occur. failed_evaluations = db.ListProperty(item_type=db.Key, default=[]) #: Organization which this project is in org = db.ReferenceProperty( reference_class=soc.models.organization.Organization, required=True, collection_name='student_projects') #: Program in which this project has been created program = db.ReferenceProperty(reference_class=soc.models.program.Program, required=True, collection_name='projects') #: Proposal to which this project corresponds to proposal = db.ReferenceProperty( reference_class=soc.modules.gsoc.models.proposal.GSoCProposal, required=False, collection_name='projects') #: Whether the student has submitted their code samples or not code_samples_submitted = db.BooleanProperty(default=False) def codeSamples(self): """Returns code_sample.GSoCCodeSample entities uploaded for this project. Returns: code sample entities for this project """ query = code_sample_model.GSoCCodeSample.all() query.ancestor(self) return query.fetch(1000) def countCodeSamples(self): """Returns number of code_sample.GSoCCodeSample entities uploaded for this project. Returns: number of code samples uploaded for this project """ query = code_sample_model.GSoCCodeSample.all(keys_only=True) query.ancestor(self) return query.count()
class Event(db.Model): title = db.StringProperty(required=True) start= db.DateTimeProperty(required=True) end=db.DateTimeProperty(required=True, indexed=True) location=db.PostalAddressProperty(required=False, indexed=False) link=db.LinkProperty(required=False, indexed=False) description=db.TextProperty() cost=db.TextProperty(required=False) submitted_by=db.ReferenceProperty(Profile,required=False, collection_name='events_submitted') submitted_at=db.DateTimeProperty(auto_now_add=True) site=db.ReferenceProperty(Eventsite, required=True) sort_dt=db.DateTimeProperty() status=db.StringProperty(required=True) score=db.IntegerProperty(indexed=False) approved_by=db.ReferenceProperty(Profile,indexed=False, collection_name="events_approved") approved_on=db.DateTimeProperty() tags=db.StringListProperty() credit_name=db.StringProperty(required=True, indexed=False) credit_link=db.LinkProperty(required=False, indexed=False) source=db.ReferenceProperty(required=False) last_seen=db.DateTimeProperty(required=False) source_uid=db.StringProperty(required=False) repeats_frequency=db.StringProperty(required=False) repeats_weekday=db.IntegerProperty(required=False) #repeats_setpos=db.IntegerProperty(required=False) repeats_monthday=db.IntegerProperty(required=False) def __unicode__(self): return "%s: %s" %(self.local_start.date().strftime("%a %b %d"), self.title) @DerivedProperty def local_start(self): if self.start.tzinfo == None: ls= utc.localize(self.start).astimezone(self.site.tz) return ls.replace(tzinfo=None) else: return self.start.astimezone(self.site.tz) + self.site.tz.utcoffset(self.site.tz) @DerivedProperty def local_end(self): if self.end.tzinfo == None: ls= utc.localize(self.end).astimezone(self.site.tz) return ls.replace(tzinfo=None) else: return self.end.astimezone(self.site.tz) + self.site.tz.utcoffset(self.site.tz) @DerivedProperty def multiday(self): return ((self.local_start.date() < self.local_end.date()) and (self.local_end -self.local_start) > timedelta(1)) @DerivedProperty def allday(self): return ((self.local_start.time() == time(0) and self.local_end.time() == time(0))) @DerivedProperty def continues(self): if self.multiday: rule=rrule(DAILY, dtstart=self.local_start, until=self.local_end) return [str(dt.date()) for dt in list(rule)] else: return None @DerivedProperty def repeats_setpos(self): month_start=(self.local_start+relativedelta(day=1)) weekday=(MO,TU,WE,TH,FR,SA,SU)[self.local_start.weekday()] tests=[(-1,month_start+relativedelta(day=31,weekday=weekday(-1))), (1,month_start+relativedelta(day=1,weekday=weekday(1))), (2,month_start+relativedelta(day=1,weekday=weekday(2))), (3,month_start+relativedelta(day=1,weekday=weekday(2))),] for pos, d in tests: logging.warning("comparing %s and %s" %(str(d),str(self.local_start.date()) )) if self.local_start.date()==d.date(): return pos @property def event_link(self): return self.link @property def item_pubdate(self): return self.approved_on def copy(self, rule): copies=[] delta=self.end-self.start #search_after=utc.localize(datetime.now()) logging.warning(rule) for dt in rule[1:6]: if not (dt.date() > self.local_start.date()): continue logging.warning(dt) new_start=self.site.tz.localize(self.local_start.replace(tzinfo=None)).replace(dt.year,dt.month,dt.day) e=Event(parent=self,key_name=dt.strftime("%Y%m%j"), title = self.title, start= new_start, end=new_start+delta, location=self.location, link=self.link, description=self.description, cost=self.cost, site=self.site, sort_dt=self.sort_dt, status=self.status, tags=self.tags, credit_name=self.credit_name, credit_link=self.credit_link, last_seen=datetime.now() ) copies.append(e) return copies def check_scores(self): if (self.approved_on or (self.score > 2)): pass @classmethod def from_gdata(cls,gevent, source): #logging.warning(type(parser.parse(gevent.when[0].start_time))) dtstart=parser.parse(gevent.when[0].start_time) dtend=parser.parse(gevent.when[0].end_time) summary=gevent.title.text location= gevent.where[0].value_string or None link= source.source_link uid=gevent.id.text key_name=uid #first check for duplicate key name existing_event=Event.get_by_key_name(key_name) #fail-over to name and start if not existing_event: existing_event=source.event_set.filter('title = ', summary).filter('start = ',dtstart).get() logging.warning("searched for existing event with key %s, found %s" %(key_name,existing_event)) if existing_event and source.trusted: existing_event.title=summary or "Untitled Event" existing_event.start=dtstart existing_event.end=dtend existing_event.last_seen=datetime.now() if location:existing_event.location=location if link: existing_event.link=link or source.source_link existing_event.put() return existing_event if existing_event and not source.trusted: return existing_event if source.trusted: status='approved' approved_on=datetime.now() else: status='submitted' approved_on=None event=Event(key_name=key_name, title=summary or "Untitled Event", start=dtstart, end=dtend, site=source.site, location=location, link=link, status=status, tags=source.default_tags, credit_name=source.name, credit_link=source.source_link, source=source, source_uid=uid, approved_on=approved_on, last_seen=datetime.now()) #source_ical_hash=source_ical_hash) event.put() source.site.expire_assets() return event @classmethod def from_vcal(cls,vcal, source): def start_dt(dateobject): if type(dateobject) == datetime: if hasattr (dateobject,'tzinfo') and dateobject.tzinfo: return dateobject else: return source.site.tz.localize(dateobject) if not dateobject: return None return source.site.tz.localize(datetime.combine(dateobject, time(0))) def end_dt(dateobject): if not dateobject: return None if type(dateobject) == datetime: if hasattr (dateobject,'tzinfo') and dateobject.tzinfo: return dateobject else: return source.site.tz.localize(dateobject) return source.site.tz.localize(datetime.combine(dateobject, time(0))) #logging.warning("hey!") parsedCal = vobject.readOne(vcal) vevent=parsedCal.vevent dtstart=start_dt(vevent.dtstart.value) if dtstart < utc.localize(datetime.today()) - timedelta(1): return logging.warning("skipping old event with dtstart %s" % dtstart) if (hasattr(vevent,'dtend') and vevent.dtend): dtend=(end_dt(vevent.dtend.value) or dtstart) else: dtend=dtstart summary=vevent.summary.value location= vevent.getChildValue('location') or None link=vevent.getChildValue('url') or source.source_link uid=vevent.uid.value or link or str(dtstart) key_name="%s-%s-%s"%(source.site.slug, source.slug,uid) #first check for duplicate key name existing_event=Event.get_by_key_name(key_name) #fail-over to name and start if not existing_event: existing_event=source.event_set.filter('title = ', summary).filter('start = ',dtstart).get() logging.warning("searched for existing event with key %s, found %s" %(key_name,existing_event)) if existing_event and source.trusted: existing_event.title=summary or "Untitled Event" existing_event.start=dtstart existing_event.end=dtend existing_event.last_seen=datetime.now() if location:existing_event.location=location if link: existing_event.link=link or source.source_link #existing_event.source_ical_hash=source_ical_hash existing_event.put() return existing_event if existing_event and not source.trusted: return existing_event if source.trusted: status='approved' approved_on=datetime.now() else: status='submitted' approved_on=None event=Event(key_name=key_name, title=summary or "Untitled Event", start=dtstart, end=dtend, site=source.site, location=location, link=link, status=status, tags=source.default_tags, credit_name=source.name, credit_link=source.source_link, source=source, source_uid=uid, approved_on=approved_on, last_seen=datetime.now()) #source_ical_hash=source_ical_hash) event.put() source.site.expire_assets() return event @classmethod def for_site_week(cls,site,day): monday=day+relativedelta(weekday=MO(-1)) sunday=monday+relativedelta(weekday=SU)
class Feature(DictModel): """Container for a feature.""" DEFAULT_MEMCACHE_KEY = '%s|features' % (settings.MEMCACHE_KEY_PREFIX) def format_for_template(self): d = self.to_dict() d['id'] = self.key().id() d['category'] = FEATURE_CATEGORIES[self.category] d['visibility'] = VISIBILITY_CHOICES[self.visibility] d['impl_status_chrome'] = IMPLEMENTATION_STATUS[self.impl_status_chrome] d['meta'] = { 'experimentalframework': self.impl_status_chrome == EXPERIMENTAL_FRAMEWORK, 'needsflag': self.impl_status_chrome == BEHIND_A_FLAG, 'milestone_str': self.shipped_milestone or d['impl_status_chrome'] } d['ff_views'] = {'value': self.ff_views, 'text': VENDOR_VIEWS[self.ff_views]} d['ie_views'] = {'value': self.ie_views, 'text': VENDOR_VIEWS[self.ie_views]} d['safari_views'] = {'value': self.safari_views, 'text': VENDOR_VIEWS[self.safari_views]} d['standardization'] = {'value': self.standardization, 'text': STANDARDIZATION[self.standardization]} d['web_dev_views'] = {'value': self.web_dev_views, 'text': WEB_DEV_VIEWS[self.web_dev_views]} #d['owner'] = ', '.join(self.owner) return d def format_for_edit(self): d = self.to_dict() #d['id'] = self.key().id d['owner'] = ', '.join(self.owner) d['doc_links'] = '\r\n'.join(self.doc_links) d['sample_links'] = '\r\n'.join(self.sample_links) d['search_tags'] = ', '.join(self.search_tags) return d @classmethod def get_all(self, limit=None, order='-updated', filterby=None, update_cache=False): KEY = '%s|%s|%s' % (Feature.DEFAULT_MEMCACHE_KEY, order, limit) # TODO(ericbidelman): Support more than one filter. if filterby is not None: s = ('%s%s' % (filterby[0], filterby[1])).replace(' ', '') KEY += '|%s' % s feature_list = memcache.get(KEY) if feature_list is None or update_cache: query = Feature.all().order(order) #.order('name') # TODO(ericbidelman): Support more than one filter. if filterby: query.filter(filterby[0], filterby[1]) features = query.fetch(limit) feature_list = [f.format_for_template() for f in features] memcache.set(KEY, feature_list) return feature_list @classmethod def get_all_with_statuses(self, statuses, update_cache=False): if not statuses: return [] KEY = '%s|%s' % (Feature.DEFAULT_MEMCACHE_KEY, sorted(statuses)) feature_list = memcache.get(KEY) if feature_list is None or update_cache: # There's no way to do an OR in a single datastore query, and there's a # very good chance that the self.get_all() results will already be in # memcache, so use an array comprehension to grab the features we # want from the array of everything. feature_list = [feature for feature in self.get_all(update_cache=update_cache) if feature['impl_status_chrome'] in statuses] memcache.set(KEY, feature_list) return feature_list @classmethod def get_feature(self, feature_id, update_cache=False): KEY = '%s|%s' % (Feature.DEFAULT_MEMCACHE_KEY, feature_id) feature = memcache.get(KEY) if feature is None or update_cache: unformatted_feature = Feature.get_by_id(feature_id) if unformatted_feature: feature = unformatted_feature.format_for_template() feature['updated_display'] = unformatted_feature.updated.strftime("%Y-%m-%d") memcache.set(KEY, feature) return feature @classmethod def get_chronological(self, limit=None, update_cache=False): KEY = '%s|%s|%s' % (Feature.DEFAULT_MEMCACHE_KEY, 'cronorder', limit) feature_list = memcache.get(KEY) if feature_list is None or update_cache: q = Feature.all() q.order('-shipped_milestone') q.order('name') features = q.fetch(None) features = [f for f in features if (IN_DEVELOPMENT < f.impl_status_chrome < NO_LONGER_PURSUING)] # Append no active, in dev, proposed features. q = Feature.all() q.order('impl_status_chrome') q.order('name') q.filter('impl_status_chrome <=', IN_DEVELOPMENT) pre_release = q.fetch(None) pre_release.extend(features) # Append no longer pursuing features. q = Feature.all() q.order('impl_status_chrome') q.order('name') q.filter('impl_status_chrome =', NO_LONGER_PURSUING) no_longer_pursuing = q.fetch(None) pre_release.extend(no_longer_pursuing) feature_list = [f.format_for_template() for f in pre_release] memcache.set(KEY, feature_list) return feature_list @classmethod def get_shipping_samples(self, limit=None, update_cache=False): KEY = '%s|%s|%s' % (Feature.DEFAULT_MEMCACHE_KEY, 'samples', limit) feature_list = memcache.get(KEY) if feature_list is None or update_cache: # Get all shipping features. Ordered by shipping milestone (latest first). q = Feature.all() q.filter('impl_status_chrome IN', [ENABLED_BY_DEFAULT, EXPERIMENTAL_FRAMEWORK]) q.order('-impl_status_chrome') q.order('-shipped_milestone') q.order('name') features = q.fetch(None) # Get non-shipping features (sans removed or deprecated ones) and # append to bottom of list. q = Feature.all() q.filter('impl_status_chrome <', ENABLED_BY_DEFAULT) q.order('-impl_status_chrome') q.order('-shipped_milestone') q.order('name') others = q.fetch(None) features.extend(others) # Filter out features without sample links. feature_list = [f.format_for_template() for f in features if len(f.sample_links)] memcache.set(KEY, feature_list) return feature_list # Metadata. created = db.DateTimeProperty(auto_now_add=True) updated = db.DateTimeProperty(auto_now=True) updated_by = db.UserProperty(auto_current_user=True) created_by = db.UserProperty(auto_current_user_add=True) # General info. category = db.IntegerProperty(required=True) name = db.StringProperty(required=True) summary = db.StringProperty(required=True, multiline=True) # Chromium details. bug_url = db.LinkProperty() impl_status_chrome = db.IntegerProperty(required=True) shipped_milestone = db.IntegerProperty() shipped_android_milestone = db.IntegerProperty() shipped_ios_milestone = db.IntegerProperty() shipped_webview_milestone = db.IntegerProperty() shipped_opera_milestone = db.IntegerProperty() shipped_opera_android_milestone = db.IntegerProperty() owner = db.ListProperty(db.Email) footprint = db.IntegerProperty() visibility = db.IntegerProperty(required=True) #webbiness = db.IntegerProperty() # TODO: figure out what this is # Standards details. standardization = db.IntegerProperty(required=True) spec_link = db.LinkProperty() prefixed = db.BooleanProperty() ff_views = db.IntegerProperty(required=True, default=NO_PUBLIC_SIGNALS) ie_views = db.IntegerProperty(required=True, default=NO_PUBLIC_SIGNALS) safari_views = db.IntegerProperty(required=True, default=NO_PUBLIC_SIGNALS) ff_views_link = db.LinkProperty() ie_views_link = db.LinkProperty() safari_views_link = db.LinkProperty() # Web dev details. web_dev_views = db.IntegerProperty(required=True) doc_links = db.StringListProperty() sample_links = db.StringListProperty() #tests = db.StringProperty() search_tags = db.StringListProperty() comments = db.StringProperty(multiline=True)
class User(db.Model): lowercase_nickname = db.StringProperty(required=True) nickname = db.StringProperty(required=True) password = db.StringProperty(required=True) created = db.DateTimeProperty(auto_now_add=True) about = db.TextProperty(required=False) hnuser = db.StringProperty(required=False, default="") github = db.StringProperty(required=False, default="") location = db.StringProperty(required=False, default="") twitter = db.StringProperty(required=False, default="") email = db.EmailProperty(required=False) url = db.LinkProperty(required=False) admin = db.BooleanProperty(default=False) karma = db.IntegerProperty(required=False) @staticmethod def slow_hash(password, iterations=1000): h = hashlib.sha1() h.update(password) h.update(keys.salt_key) for x in range(iterations): h.update(h.digest()) return h.hexdigest() def average_karma(self): delta = (datetime.now() - self.created) days = delta.days votes = self.karma if votes is None: votes = 0 if days > 0: return votes / float(days) else: return votes def sum_votes(self): val = memcache.get("u_" + str(self.key())) if val is not None: return val else: val = Vote.all().filter("user !=", self).filter("target_user ="******"u_" + str(self.key()), val, 3600) return val def remove_from_memcache(self): memcache.delete("u_" + str(self.key())) def has_notifications(self): count_notificationes = memcache.get("user_notification_" + str(self.key())) if count_notificationes is not None: return count_notificationes > 0 else: count_notificationes = Notification.all().filter( "target_user ="******"read =", False).count() memcache.add("user_notification_" + str(self.key()), count_notificationes, 3600) return count_notificationes > 0 def remove_notifications_from_memcache(self): memcache.delete("user_notification_" + str(self.key()))
class ICalendarSource(db.Model): site = db.ReferenceProperty(Eventsite, required=True) name = db.StringProperty(required=True, indexed=False) source_link = db.LinkProperty(indexed=False, required=False) ical_href = db.LinkProperty(required=True, indexed=False) status = db.StringProperty(required=True) trusted = db.BooleanProperty() default_tags = db.StringListProperty() submitted_by = db.ReferenceProperty(Profile, required=False, collection_name='icals_submitted') submitted_at = db.DateTimeProperty(auto_now_add=True) approved_by = db.ReferenceProperty(Profile, indexed=False, collection_name="icals_approved") approved_on = db.DateTimeProperty(indexed=False) last_fetch = db.DateTimeProperty(required=False) content = db.TextProperty(required=False) is_rssfeed = db.BooleanProperty() @property def approval_form(self): return sources.forms.ICalApprovalForm( initial={ 'trusted': self.trusted, 'tags': ','.join(self.default_tags) }) @DerivedProperty def slug(self): return str(slugify(self.name)) def fetch(self, started=None, timestamp=None): format_start = "%Y%m%d%H%M" if not started: started = str(datetime.now()) if not timestamp: timestamp = datetime.now().strftime("%Y%m%d%H%M") if self.ical_href.startswith( 'http://www.google.com/calendar/ical/' ) or self.ical_href.startswith( 'https://www.google.com/calendar/ical/'): gcal_id = unquote(self.ical_href[36:].split('/')[0]) query = gdata.calendar.service.CalendarEventQuery( gcal_id, 'public', 'full-noattendees') query.start_min = self.site.today.strftime("%Y-%m-%d") query.recurrence_expansion_end = ( date.today() + relativedelta(months=3)).strftime("%Y-%m-%d") query.start_max = (date.today() + relativedelta(months=3)).strftime("%Y-%m-%d") query.singleevents = 'true' result = urlfetch.fetch(query.ToUri(), allow_truncated=False, deadline=10) logging.warning("fetching %s" % result.content) if result.status_code == 200: detection = chardet.detect(result.content) self.last_fetch = datetime.now() self.content = result.content.decode(detection['encoding']) self.put() process_gdata(self) #cache_key="%s-%s-%s" %(self.site.slug, self.slug,timestamp) #memcache.add(cache_key, ,600) #logging.warning("cached gdata with key %s"% cache_key) #taskqueue.add(url='/sources/split_gdata/', params={'ical_key': self.key(), # 'cache_key':cache_key, # 'timestamp':timestamp}, # name=cache_key # ) #logging.warning("enqueued splitting of %s" % self.ical_href) return result = urlfetch.fetch(self.ical_href, allow_truncated=False, deadline=5) if result.status_code == 200: detection = chardet.detect(result.content) self.last_fetch = datetime.now() self.content = result.content.decode(detection['encoding']) self.put() process_ical(self)
class ChromiumTryTasks(BaseTelemetryModel): """Data model for Chromium Try tasks.""" username = db.StringProperty(required=True) benchmark_name = db.StringProperty(required=True) benchmark_arguments = db.StringProperty() browser_args_1 = db.StringProperty() browser_args_2 = db.StringProperty() target_platform = db.StringProperty() pageset_type = db.StringProperty() skia_patch = db.BlobProperty() chromium_patch = db.BlobProperty() blink_patch = db.BlobProperty() num_repeated_runs = db.IntegerProperty() variance_threshold = db.FloatProperty(required=True) discard_outliers = db.FloatProperty(required=True) requested_time = db.DateTimeProperty(required=True) completed_time = db.DateTimeProperty() description = db.StringProperty() skia_patch_link = db.LinkProperty() chromium_patch_link = db.LinkProperty() blink_patch_link = db.LinkProperty() build_log_link = db.LinkProperty() telemetry_nopatch_log_link = db.LinkProperty() telemetry_withpatch_log_link = db.LinkProperty() html_output_link = db.LinkProperty() def get_json_repr(self): """Returns a JSON representation of this Data Model.""" return { 'ChromiumTryTask': { 'key': self.key().id_or_name(), 'username': self.username, 'benchmark_name': self.benchmark_name, 'benchmark_arguments': self.benchmark_arguments, 'browser_args_1': self.browser_args_1, 'browser_args_2': self.browser_args_2, 'target_platform': self.target_platform, 'pageset_type': self.pageset_type, 'skia_patch': self.skia_patch, 'chromium_patch': self.chromium_patch, 'blink_patch': self.blink_patch, 'num_repeated_runs': self.num_repeated_runs, 'variance_threshold': self.variance_threshold, 'discard_outliers': self.discard_outliers, 'requested_time': str(self.requested_time) } } @classmethod def get_all_chromium_try_tasks(cls, offset): return (cls.all() .order('-requested_time') .fetch(offset=offset, limit=PAGINATION_LIMIT)) @classmethod def get_all_chromium_try_tasks_of_user(cls, user): return (cls.all() .filter('username ='******'-requested_time') .fetch(limit=FETCH_LIMIT)) @classmethod def get_chromium_try_task(cls, key): return db.GqlQuery( 'SELECT * FROM ChromiumTryTasks WHERE __key__ = ' 'Key(\'ChromiumTryTasks\', %s);' % key) @classmethod def delete_chromium_try_task(cls, key): chromium_try_tasks = cls.get_chromium_try_task(key) if chromium_try_tasks.count(): chromium_try_tasks[0].delete()
class Session(db.Expando): """An in-progress OpenID login.""" claimed_id = db.StringProperty() server_url = db.LinkProperty()
class SubCategory(db.Model): name = db.StringProperty(required=True) image = db.LinkProperty() description = db.TextProperty() category = db.StringProperty() _id = db.IntegerProperty()
class StudentProject(soc.models.linkable.Linkable): """Model for a student project used in the GSoC workflow. """ #: Required field indicating the "title" of the project title = db.StringProperty(required=True, verbose_name=ugettext('Title')) title.help_text = ugettext('Title of the project') #: Required, text field describing the project abstract = db.TextProperty(required=True) abstract.help_text = ugettext( 'Short abstract, summary, or snippet;' ' 500 characters or less, plain text displayed publicly') #: Optional, text field containing all kinds of information about this project public_info = db.TextProperty(required=False, default='') public_info.help_text = ugettext( 'Additional information about this project to be shown publicly') #: Optional, URL which can give more information about this project additional_info = db.URLProperty(required=False) additional_info.help_text = ugettext( 'Link to a resource containing more information about this project.') #: Optional field storing a feed URL; displayed publicly feed_url = db.LinkProperty(verbose_name=ugettext('Project Feed URL')) feed_url.help_text = ugettext( 'The URL should be a valid ATOM or RSS feed. ' 'Feed entries are shown on the public page.') #: A property containing which mentor has been assigned to this project. #: A project must have a mentor at all times. mentor = db.ReferenceProperty(reference_class=soc.models.mentor.Mentor, required=True, collection_name='student_projects') #: A property containing a list of additional Mentors for this project additional_mentors = db.ListProperty(item_type=db.Key, default=[]) #: The status of this project #: accepted: This project has been accepted into the program #: failed: This project has failed an evaluation. #: completed: This project has completed the program successfully. This #: should be set automatically when a program has been deemed #: finished. #: withdrawn: This project has been withdrawn from the program by a Program #: Administrator or higher. #: invalid: This project has been marked as invalid because it was deleted status = db.StringProperty( required=True, default='accepted', choices=['accepted', 'failed', 'completed', 'withdrawn', 'invalid']) #: List of all processed GradingRecords which state a pass for this project. #: This property can be used to determine how many evaluations someone has #: passed. And is also used to ensure that a GradingRecord has been #: processed. passed_evaluations = db.ListProperty(item_type=db.Key, default=[]) #: List of all processed GradingRecords which state a fail for this project. #: This is a ListProperty to ensure that the system keeps functioning when #: manual changes in GradingRecords occur. failed_evaluations = db.ListProperty(item_type=db.Key, default=[]) #: Student which this project is from student = db.ReferenceProperty(reference_class=soc.models.student.Student, required=True, collection_name='student_projects') #: Program in which this project has been created program = db.ReferenceProperty(reference_class=soc.models.program.Program, required=True, collection_name='student_projects')
class SplitLunch(db.Model): name = db.LinkProperty()
class Feature(DictModel): """Container for a feature.""" DEFAULT_MEMCACHE_KEY = '%s|features' % (settings.MEMCACHE_KEY_PREFIX) def format_for_template(self): d = self.to_dict() d['id'] = self.key().id() d['category'] = FEATURE_CATEGORIES[self.category] d['visibility'] = VISIBILITY_CHOICES[self.visibility] d['impl_status_chrome'] = IMPLEMENATION_STATUS[self.impl_status_chrome] d['meta'] = { 'needsflag': self.impl_status_chrome == BEHIND_A_FLAG, 'milestone_str': self.shipped_milestone or d['impl_status_chrome'] } d['ff_views'] = { 'value': self.ff_views, 'text': VENDOR_VIEWS[self.ff_views] } d['ie_views'] = { 'value': self.ie_views, 'text': VENDOR_VIEWS[self.ie_views] } d['safari_views'] = { 'value': self.safari_views, 'text': VENDOR_VIEWS[self.safari_views] } d['standardization'] = { 'value': self.standardization, 'text': STANDARDIZATION[self.standardization] } d['web_dev_views'] = { 'value': self.web_dev_views, 'text': WEB_DEV_VIEWS[self.web_dev_views] } #d['owner'] = ', '.join(self.owner) return d def format_for_edit(self): d = self.to_dict() #d['id'] = self.key().id d['owner'] = ', '.join(self.owner) d['doc_links'] = ', '.join(self.doc_links) d['sample_links'] = ', '.join(self.sample_links) return d @classmethod def get_all(self, limit=None, order='-updated', filterby=None, update_cache=False): KEY = '%s|%s|%s' % (Feature.DEFAULT_MEMCACHE_KEY, order, limit) # TODO(ericbidelman): Support more than one filter. if filterby is not None: s = ('%s%s' % (filterby[0], filterby[1])).replace(' ', '') KEY += '|%s' % s feature_list = memcache.get(KEY) if feature_list is None or update_cache: query = Feature.all().order(order) #.order('name') # TODO(ericbidelman): Support more than one filter. if filterby: query.filter(filterby[0], filterby[1]) features = query.fetch(limit) feature_list = [f.format_for_template() for f in features] memcache.set(KEY, feature_list) return feature_list @classmethod def get_chronological(limit=None, update_cache=False): KEY = '%s|%s|%s' % (Feature.DEFAULT_MEMCACHE_KEY, 'cronorder', limit) feature_list = memcache.get(KEY) if feature_list is None or update_cache: q = Feature.all() q.order('-shipped_milestone') q.order('name') features = q.fetch(None) features = [ f for f in features if (IN_DEVELOPMENT < f.impl_status_chrome < NO_LONGER_PURSUING) ] # Get no active, in dev, proposed features. q = Feature.all() q.order('impl_status_chrome') q.order('name') q.filter('impl_status_chrome <=', IN_DEVELOPMENT) pre_release = q.fetch(None) pre_release.extend(features) # Get no longer pursuing features. q = Feature.all() q.order('impl_status_chrome') q.order('name') q.filter('impl_status_chrome =', NO_LONGER_PURSUING) no_longer_pursuing = q.fetch(None) pre_release.extend(no_longer_pursuing) feature_list = [f.format_for_template() for f in pre_release] memcache.set(KEY, feature_list) return feature_list # Metadata. created = db.DateTimeProperty(auto_now_add=True) updated = db.DateTimeProperty(auto_now=True) updated_by = db.UserProperty(auto_current_user=True) created_by = db.UserProperty(auto_current_user_add=True) # General info. category = db.IntegerProperty(required=True) name = db.StringProperty(required=True) summary = db.StringProperty(required=True, multiline=True) # Chromium details. bug_url = db.LinkProperty() impl_status_chrome = db.IntegerProperty(required=True) shipped_milestone = db.IntegerProperty() shipped_android_milestone = db.IntegerProperty() shipped_ios_milestone = db.IntegerProperty() shipped_webview_milestone = db.IntegerProperty() shipped_opera_milestone = db.IntegerProperty() shipped_opera_android_milestone = db.IntegerProperty() owner = db.ListProperty(db.Email) footprint = db.IntegerProperty() visibility = db.IntegerProperty(required=True) #webbiness = db.IntegerProperty() # TODO: figure out what this is # Standards details. standardization = db.IntegerProperty(required=True) spec_link = db.LinkProperty() prefixed = db.BooleanProperty() ff_views = db.IntegerProperty(required=True, default=NO_PUBLIC_SIGNALS) ie_views = db.IntegerProperty(required=True, default=NO_PUBLIC_SIGNALS) safari_views = db.IntegerProperty(required=True, default=NO_PUBLIC_SIGNALS) ff_views_link = db.LinkProperty() ie_views_link = db.LinkProperty() safari_views_link = db.LinkProperty() # Web dev details. web_dev_views = db.IntegerProperty(required=True) doc_links = db.StringListProperty() sample_links = db.StringListProperty() #tests = db.StringProperty() comments = db.StringProperty(multiline=True)
class CrawlUrl(db.Model): url = db.LinkProperty() lastseen = db.DateTimeProperty(auto_now_add=True) headers = db.TextProperty()
class Feature(DictModel): """Container for a feature.""" DEFAULT_MEMCACHE_KEY = '%s|features' % (settings.MEMCACHE_KEY_PREFIX) def format_for_template(self, version=None): d = self.to_dict() if version == 2: d['id'] = self.key().id() d['category'] = FEATURE_CATEGORIES[self.category] d['created'] = { 'by': d.pop('created', None), 'when': d.pop('created_by', None), } d['updated'] = { 'by': d.pop('updated', None), 'when': d.pop('updated_by', None), } d['standards'] = { 'spec': d.pop('spec_link', None), 'status': { 'text': STANDARDIZATION[self.standardization], 'val': d.pop('standardization', None), }, 'visibility': { 'text': VISIBILITY_CHOICES[self.visibility], 'val': d.pop('visibility', None), }, 'footprint': { 'val': d.pop('footprint', None), #'text': FOOTPRINT_CHOICES[self.footprint] } } d['resources'] = { 'samples': d.pop('sample_links', []), 'docs': d.pop('doc_links', []), } d['tags'] = d.pop('search_tags', []) d['browsers'] = { 'chrome': { 'bug': d.pop('bug_url', None), 'crbug_component': d.pop('bug_component', None), 'owners': d.pop('owner', []), 'origintrial': self.impl_status_chrome == ORIGIN_TRIAL, 'intervention': self.impl_status_chrome == INTERVENTION, 'prefixed': d.pop('prefixed', False), 'flag': self.impl_status_chrome == BEHIND_A_FLAG, 'status': { 'text': IMPLEMENTATION_STATUS[self.impl_status_chrome], 'val': d.pop('impl_status_chrome', None) }, 'desktop': d.pop('shipped_milestone', None), 'android': d.pop('shipped_android_milestone', None), 'webview': d.pop('shipped_webview_milestone', None), 'ios': d.pop('shipped_ios_milestone', None), }, 'opera': { 'desktop': d.pop('shipped_opera_milestone', None), 'android': d.pop('shipped_opera_android_milestone', None), }, 'ff': { 'view': { 'text': VENDOR_VIEWS[self.ff_views], 'val': d.pop('ff_views', None), 'url': d.pop('ff_views_link', None), } }, 'edge': { 'view': { 'text': VENDOR_VIEWS[self.ie_views], 'val': d.pop('ie_views', None), 'url': d.pop('ie_views_link', None), } }, 'safari': { 'view': { 'text': VENDOR_VIEWS[self.safari_views], 'val': d.pop('safari_views', None), 'url': d.pop('safari_views_link', None), } }, 'webdev': { 'view': { 'text': WEB_DEV_VIEWS[self.web_dev_views], 'val': d.pop('web_dev_views', None), } } } if self.shipped_milestone: d['browsers']['chrome']['status'][ 'milestone_str'] = self.shipped_milestone elif self.shipped_milestone is None and self.shipped_android_milestone: d['browsers']['chrome']['status'][ 'milestone_str'] = self.shipped_android_milestone else: d['browsers']['chrome']['status']['milestone_str'] = d[ 'browsers']['chrome']['status']['text'] del d['created'] del d['updated'] del_none(d) # Further prune response by removing null/[] values. else: d['id'] = self.key().id() d['category'] = FEATURE_CATEGORIES[self.category] d['visibility'] = VISIBILITY_CHOICES[self.visibility] d['impl_status_chrome'] = IMPLEMENTATION_STATUS[ self.impl_status_chrome] d['meta'] = { 'origintrial': self.impl_status_chrome == ORIGIN_TRIAL, 'intervention': self.impl_status_chrome == INTERVENTION, 'needsflag': self.impl_status_chrome == BEHIND_A_FLAG, } if self.shipped_milestone: d['meta']['milestone_str'] = self.shipped_milestone elif self.shipped_milestone is None and self.shipped_android_milestone: d['meta']['milestone_str'] = self.shipped_android_milestone else: d['meta']['milestone_str'] = d['impl_status_chrome'] d['ff_views'] = { 'value': self.ff_views, 'text': VENDOR_VIEWS[self.ff_views] } d['ie_views'] = { 'value': self.ie_views, 'text': VENDOR_VIEWS[self.ie_views] } d['safari_views'] = { 'value': self.safari_views, 'text': VENDOR_VIEWS[self.safari_views] } d['standardization'] = { 'value': self.standardization, 'text': STANDARDIZATION[self.standardization] } d['web_dev_views'] = { 'value': self.web_dev_views, 'text': WEB_DEV_VIEWS[self.web_dev_views] } return d def format_for_edit(self): d = self.to_dict() #d['id'] = self.key().id d['owner'] = ', '.join(self.owner) d['doc_links'] = '\r\n'.join(self.doc_links) d['sample_links'] = '\r\n'.join(self.sample_links) d['search_tags'] = ', '.join(self.search_tags) return d @classmethod def get_all(self, limit=None, order='-updated', filterby=None, update_cache=False): KEY = '%s|%s|%s' % (Feature.DEFAULT_MEMCACHE_KEY, order, limit) # TODO(ericbidelman): Support more than one filter. if filterby is not None: s = ('%s%s' % (filterby[0], filterby[1])).replace(' ', '') KEY += '|%s' % s feature_list = memcache.get(KEY) if feature_list is None or update_cache: query = Feature.all().order(order) #.order('name') # TODO(ericbidelman): Support more than one filter. if filterby: query.filter(filterby[0], filterby[1]) features = query.fetch(limit) feature_list = [f.format_for_template() for f in features] memcache.set(KEY, feature_list) return feature_list @classmethod def get_all_with_statuses(self, statuses, update_cache=False): if not statuses: return [] KEY = '%s|%s' % (Feature.DEFAULT_MEMCACHE_KEY, sorted(statuses)) feature_list = memcache.get(KEY) if feature_list is None or update_cache: # There's no way to do an OR in a single datastore query, and there's a # very good chance that the self.get_all() results will already be in # memcache, so use an array comprehension to grab the features we # want from the array of everything. feature_list = [ feature for feature in self.get_all(update_cache=update_cache) if feature['impl_status_chrome'] in statuses ] memcache.set(KEY, feature_list) return feature_list @classmethod def get_feature(self, feature_id, update_cache=False): KEY = '%s|%s' % (Feature.DEFAULT_MEMCACHE_KEY, feature_id) feature = memcache.get(KEY) if feature is None or update_cache: unformatted_feature = Feature.get_by_id(feature_id) if unformatted_feature: feature = unformatted_feature.format_for_template() feature[ 'updated_display'] = unformatted_feature.updated.strftime( "%Y-%m-%d") feature['new_crbug_url'] = unformatted_feature.new_crbug_url() memcache.set(KEY, feature) return feature @classmethod def get_chronological(self, limit=None, update_cache=False, version=None): KEY = '%s|%s|%s|%s' % (Feature.DEFAULT_MEMCACHE_KEY, 'cronorder', limit, version) feature_list = memcache.get(KEY) if feature_list is None or update_cache: # Features with no active, in dev, proposed features. q = Feature.all() q.order('impl_status_chrome') q.order('name') q.filter('impl_status_chrome <=', IN_DEVELOPMENT) pre_release = q.fetch(None) # Shipping features. Exclude features that do not have a desktop # shipping milestone. q = Feature.all() q.order('-shipped_milestone') q.order('name') q.filter('shipped_milestone !=', None) shipping_features = q.fetch(None) # Features with an android shipping milestone but no desktop milestone. q = Feature.all() q.order('-shipped_android_milestone') q.order('name') q.filter('shipped_milestone =', None) android_only_shipping_features = q.fetch(None) # No longer pursuing features. q = Feature.all() q.order('impl_status_chrome') q.order('name') q.filter('impl_status_chrome =', NO_LONGER_PURSUING) no_longer_pursuing_features = q.fetch(None) shipping_features.extend(android_only_shipping_features) shipping_features = [ f for f in shipping_features if (IN_DEVELOPMENT < f.impl_status_chrome < NO_LONGER_PURSUING) ] def getSortingMilestone(feature): feature._sort_by_milestone = ( feature.shipped_milestone or feature.shipped_android_milestone) return feature # Sort the feature list on either Android shipping milestone or desktop # shipping milestone, depending on which is specified. If a desktop # milestone is defined, that will take default. shipping_features = map(getSortingMilestone, shipping_features) # First sort by name, then sort by feature milestone (latest first). shipping_features.sort(key=lambda f: f.name, reverse=False) shipping_features.sort(key=lambda f: f._sort_by_milestone, reverse=True) # Constructor the proper ordering. pre_release.extend(shipping_features) pre_release.extend(no_longer_pursuing_features) feature_list = [ f.format_for_template(version) for f in pre_release ] memcache.set(KEY, feature_list) return feature_list @classmethod def get_shipping_samples(self, limit=None, update_cache=False): KEY = '%s|%s|%s' % (Feature.DEFAULT_MEMCACHE_KEY, 'samples', limit) feature_list = memcache.get(KEY) if feature_list is None or update_cache: # Get all shipping features. Ordered by shipping milestone (latest first). q = Feature.all() q.filter('impl_status_chrome IN', [ENABLED_BY_DEFAULT, ORIGIN_TRIAL, INTERVENTION]) q.order('-impl_status_chrome') q.order('-shipped_milestone') q.order('name') features = q.fetch(None) # Get non-shipping features (sans removed or deprecated ones) and # append to bottom of list. q = Feature.all() q.filter('impl_status_chrome <', ENABLED_BY_DEFAULT) q.order('-impl_status_chrome') q.order('-shipped_milestone') q.order('name') others = q.fetch(None) features.extend(others) # Filter out features without sample links. feature_list = [ f.format_for_template() for f in features if len(f.sample_links) ] memcache.set(KEY, feature_list) return feature_list def crbug_number(self): if not self.bug_url: return m = re.search(r'[\/|?id=]([0-9]+)$', self.bug_url) if m: return m.group(1) def new_crbug_url(self): url = 'https://bugs.chromium.org/p/chromium/issues/entry' params = ['components=' + self.bug_component or DEFAULT_BUG_COMPONENT] crbug_number = self.crbug_number() if crbug_number and self.impl_status_chrome in ( NO_ACTIVE_DEV, PROPOSED, IN_DEVELOPMENT, BEHIND_A_FLAG, ORIGIN_TRIAL, INTERVENTION): params.append('blocking=' + crbug_number) if self.owner: params.append('cc=' + ','.join(self.owner)) return url + '?' + '&'.join(params) # Metadata. created = db.DateTimeProperty(auto_now_add=True) updated = db.DateTimeProperty(auto_now=True) updated_by = db.UserProperty(auto_current_user=True) created_by = db.UserProperty(auto_current_user_add=True) # General info. category = db.IntegerProperty(required=True) name = db.StringProperty(required=True) summary = db.StringProperty(required=True, multiline=True) # Chromium details. bug_url = db.LinkProperty() bug_component = db.StringProperty(required=False, default=DEFAULT_BUG_COMPONENT) impl_status_chrome = db.IntegerProperty(required=True) shipped_milestone = db.IntegerProperty() shipped_android_milestone = db.IntegerProperty() shipped_ios_milestone = db.IntegerProperty() shipped_webview_milestone = db.IntegerProperty() shipped_opera_milestone = db.IntegerProperty() shipped_opera_android_milestone = db.IntegerProperty() owner = db.ListProperty(db.Email) footprint = db.IntegerProperty() visibility = db.IntegerProperty(required=True) #webbiness = db.IntegerProperty() # TODO: figure out what this is # Standards details. standardization = db.IntegerProperty(required=True) spec_link = db.LinkProperty() prefixed = db.BooleanProperty() ff_views = db.IntegerProperty(required=True, default=NO_PUBLIC_SIGNALS) ie_views = db.IntegerProperty(required=True, default=NO_PUBLIC_SIGNALS) safari_views = db.IntegerProperty(required=True, default=NO_PUBLIC_SIGNALS) ff_views_link = db.LinkProperty() ie_views_link = db.LinkProperty() safari_views_link = db.LinkProperty() # Web dev details. web_dev_views = db.IntegerProperty(required=True) doc_links = db.StringListProperty() sample_links = db.StringListProperty() #tests = db.StringProperty() search_tags = db.StringListProperty() comments = db.StringProperty(multiline=True)
class Feature(DictModel): """Container for a feature.""" DEFAULT_MEMCACHE_KEY = '%s|features' % (settings.MEMCACHE_KEY_PREFIX) MAX_CHUNK_SIZE = 500 # max num features to save for each memcache chunk. @classmethod def get_feature_chunk_memcache_keys(self, key_prefix): num_features = len(Feature.all().fetch(limit=None, keys_only=True)) l = list_to_chunks(range(0, num_features), self.MAX_CHUNK_SIZE) return ['%s|chunk%s' % (key_prefix, i) for i,val in enumerate(l)] @classmethod def set_feature_chunk_memcache_keys(self, key_prefix, feature_list): chunks = list_to_chunks(feature_list, self.MAX_CHUNK_SIZE) vals = [] for i, chunk in enumerate(chunks): vals.append(('%s|chunk%s' % (key_prefix, i), chunk)) # d = OrderedDict(sorted(dict(vals).items(), key=lambda t: t[0])) d = dict(vals) return d @classmethod def _first_of_milestone(self, feature_list, milestone, start=0): for i in xrange(start, len(feature_list)): f = feature_list[i] if (str(f['shipped_milestone']) == str(milestone) or f['impl_status_chrome'] == str(milestone)): return i elif (f['shipped_milestone'] == None and str(f['shipped_android_milestone']) == str(milestone)): return i return -1 @classmethod def _first_of_milestone_v2(self, feature_list, milestone, start=0): for i in xrange(start, len(feature_list)): f = feature_list[i] desktop_milestone = f['browsers']['chrome'].get('desktop', None) android_milestone = f['browsers']['chrome'].get('android', None) status = f['browsers']['chrome']['status'].get('text', None) if (str(desktop_milestone) == str(milestone) or status == str(milestone)): return i elif (desktop_milestone == None and str(android_milestone) == str(milestone)): return i return -1 @classmethod def _annotate_first_of_milestones(self, feature_list, version=None): try: omaha_data = util.get_omaha_data() win_versions = omaha_data[0]['versions'] # Find the latest canary major version from the list of windows versions. canary_versions = [x for x in win_versions if x.get('channel') and x.get('channel').startswith('canary')] LATEST_VERSION = int(canary_versions[0].get('version').split('.')[0]) milestones = range(1, LATEST_VERSION + 1) milestones.reverse() versions = [ IMPLEMENTATION_STATUS[NO_ACTIVE_DEV], IMPLEMENTATION_STATUS[PROPOSED], IMPLEMENTATION_STATUS[IN_DEVELOPMENT], ] versions.extend(milestones) versions.append(IMPLEMENTATION_STATUS[NO_LONGER_PURSUING]) first_of_milestone_func = Feature._first_of_milestone if version == 2: first_of_milestone_func = Feature._first_of_milestone_v2 last_good_idx = 0 for i, ver in enumerate(versions): idx = first_of_milestone_func(feature_list, ver, start=last_good_idx) if idx != -1: feature_list[idx]['first_of_milestone'] = True last_good_idx = idx except Exception as e: logging.error(e) def format_for_template(self, version=None): d = self.to_dict() if version == 2: if self.is_saved(): d['id'] = self.key().id() else: d['id'] = None d['category'] = FEATURE_CATEGORIES[self.category] if self.intent_stage is not None: d['intent_stage'] = INTENT_STAGES[self.intent_stage] d['created'] = { 'by': d.pop('created_by', None), 'when': d.pop('created', None), } d['updated'] = { 'by': d.pop('updated_by', None), 'when': d.pop('updated', None), } d['standards'] = { 'spec': d.pop('spec_link', None), 'status': { 'text': STANDARDIZATION[self.standardization], 'val': d.pop('standardization', None), }, 'visibility': { 'text': VISIBILITY_CHOICES[self.visibility], 'val': d.pop('visibility', None), }, 'footprint': { 'val': d.pop('footprint', None), #'text': FOOTPRINT_CHOICES[self.footprint] } } d['resources'] = { 'samples': d.pop('sample_links', []), 'docs': d.pop('doc_links', []), } d['tags'] = d.pop('search_tags', []) d['browsers'] = { 'chrome': { 'bug': d.pop('bug_url', None), 'blink_components': d.pop('blink_components', []), 'owners': d.pop('owner', []), 'origintrial': self.impl_status_chrome == ORIGIN_TRIAL, 'intervention': self.impl_status_chrome == INTERVENTION, 'prefixed': d.pop('prefixed', False), 'flag': self.impl_status_chrome == BEHIND_A_FLAG, 'status': { 'text': IMPLEMENTATION_STATUS[self.impl_status_chrome], 'val': d.pop('impl_status_chrome', None) }, 'desktop': d.pop('shipped_milestone', None), 'android': d.pop('shipped_android_milestone', None), 'webview': d.pop('shipped_webview_milestone', None), 'ios': d.pop('shipped_ios_milestone', None), }, 'ff': { 'view': { 'text': VENDOR_VIEWS[self.ff_views], 'val': d.pop('ff_views', None), 'url': d.pop('ff_views_link', None), 'notes': d.pop('ff_views_notes', None), } }, 'edge': { 'view': { 'text': VENDOR_VIEWS[self.ie_views], 'val': d.pop('ie_views', None), 'url': d.pop('ie_views_link', None), 'notes': d.pop('ie_views_notes', None), } }, 'safari': { 'view': { 'text': VENDOR_VIEWS[self.safari_views], 'val': d.pop('safari_views', None), 'url': d.pop('safari_views_link', None), 'notes': d.pop('safari_views_notes', None), } }, 'webdev': { 'view': { 'text': WEB_DEV_VIEWS[self.web_dev_views], 'val': d.pop('web_dev_views', None), 'url': d.pop('web_dev_views_link', None), 'notes': d.pop('web_dev_views_notes', None), } } } if self.shipped_milestone: d['browsers']['chrome']['status']['milestone_str'] = self.shipped_milestone elif self.shipped_milestone is None and self.shipped_android_milestone: d['browsers']['chrome']['status']['milestone_str'] = self.shipped_android_milestone else: d['browsers']['chrome']['status']['milestone_str'] = d['browsers']['chrome']['status']['text'] del d['created'] del_none(d) # Further prune response by removing null/[] values. else: if self.is_saved(): d['id'] = self.key().id() else: d['id'] = None d['category'] = FEATURE_CATEGORIES[self.category] if self.intent_stage is not None: d['intent_stage'] = INTENT_STAGES[self.intent_stage] d['visibility'] = VISIBILITY_CHOICES[self.visibility] d['impl_status_chrome'] = IMPLEMENTATION_STATUS[self.impl_status_chrome] d['meta'] = { 'origintrial': self.impl_status_chrome == ORIGIN_TRIAL, 'intervention': self.impl_status_chrome == INTERVENTION, 'needsflag': self.impl_status_chrome == BEHIND_A_FLAG, } if self.shipped_milestone: d['meta']['milestone_str'] = self.shipped_milestone elif self.shipped_milestone is None and self.shipped_android_milestone: d['meta']['milestone_str'] = self.shipped_android_milestone else: d['meta']['milestone_str'] = d['impl_status_chrome'] d['ff_views'] = {'value': self.ff_views, 'text': VENDOR_VIEWS[self.ff_views]} d['ie_views'] = {'value': self.ie_views, 'text': VENDOR_VIEWS[self.ie_views]} d['safari_views'] = {'value': self.safari_views, 'text': VENDOR_VIEWS[self.safari_views]} d['standardization'] = {'value': self.standardization, 'text': STANDARDIZATION[self.standardization]} d['web_dev_views'] = {'value': self.web_dev_views, 'text': WEB_DEV_VIEWS[self.web_dev_views]} return d def format_for_edit(self): d = self.to_dict() #d['id'] = self.key().id d['owner'] = ', '.join(self.owner) d['explainer_links'] = '\r\n'.join(self.explainer_links) d['doc_links'] = '\r\n'.join(self.doc_links) d['sample_links'] = '\r\n'.join(self.sample_links) d['search_tags'] = ', '.join(self.search_tags) d['blink_components'] = self.blink_components[0] #TODO: support more than one component. return d @classmethod def get_all(self, limit=None, order='-updated', filterby=None, update_cache=False): KEY = '%s|%s|%s' % (Feature.DEFAULT_MEMCACHE_KEY, order, limit) # TODO(ericbidelman): Support more than one filter. if filterby is not None: s = ('%s%s' % (filterby[0], filterby[1])).replace(' ', '') KEY += '|%s' % s feature_list = memcache.get(KEY) if feature_list is None or update_cache: query = Feature.all().order(order) #.order('name') # TODO(ericbidelman): Support more than one filter. if filterby: query.filter(filterby[0], filterby[1]) features = query.fetch(limit) feature_list = [f.format_for_template() for f in features] memcache.set(KEY, feature_list) return feature_list @classmethod def get_all_with_statuses(self, statuses, update_cache=False): if not statuses: return [] KEY = '%s|%s' % (Feature.DEFAULT_MEMCACHE_KEY, sorted(statuses)) feature_list = memcache.get(KEY) if feature_list is None or update_cache: # There's no way to do an OR in a single datastore query, and there's a # very good chance that the self.get_all() results will already be in # memcache, so use an array comprehension to grab the features we # want from the array of everything. feature_list = [feature for feature in self.get_all(update_cache=update_cache) if feature['impl_status_chrome'] in statuses] memcache.set(KEY, feature_list) return feature_list @classmethod def get_feature(self, feature_id, update_cache=False): KEY = '%s|%s' % (Feature.DEFAULT_MEMCACHE_KEY, feature_id) feature = memcache.get(KEY) if feature is None or update_cache: unformatted_feature = Feature.get_by_id(feature_id) if unformatted_feature: feature = unformatted_feature.format_for_template() feature['updated_display'] = unformatted_feature.updated.strftime("%Y-%m-%d") feature['new_crbug_url'] = unformatted_feature.new_crbug_url() memcache.set(KEY, feature) return feature @classmethod def get_chronological(self, limit=None, update_cache=False, version=None): KEY = '%s|%s|%s|%s' % (Feature.DEFAULT_MEMCACHE_KEY, 'cronorder', limit, version) keys = Feature.get_feature_chunk_memcache_keys(KEY) feature_list = memcache.get_multi(keys) # If we didn't get the expected number of chunks back (or a cache update # was requested), do a db query. if len(feature_list.keys()) != len(keys) or update_cache: # Features with no active, in dev, proposed features. q = Feature.all() q.order('impl_status_chrome') q.order('name') q.filter('impl_status_chrome <=', IN_DEVELOPMENT) pre_release = q.fetch(None) # Shipping features. Exclude features that do not have a desktop # shipping milestone. q = Feature.all() q.order('-shipped_milestone') q.order('name') q.filter('shipped_milestone !=', None) shipping_features = q.fetch(None) # Features with an android shipping milestone but no desktop milestone. q = Feature.all() q.order('-shipped_android_milestone') q.order('name') q.filter('shipped_milestone =', None) android_only_shipping_features = q.fetch(None) # No longer pursuing features. q = Feature.all() q.order('impl_status_chrome') q.order('name') q.filter('impl_status_chrome =', NO_LONGER_PURSUING) no_longer_pursuing_features = q.fetch(None) shipping_features.extend(android_only_shipping_features) shipping_features = [f for f in shipping_features if (IN_DEVELOPMENT < f.impl_status_chrome < NO_LONGER_PURSUING)] def getSortingMilestone(feature): feature._sort_by_milestone = (feature.shipped_milestone or feature.shipped_android_milestone) return feature # Sort the feature list on either Android shipping milestone or desktop # shipping milestone, depending on which is specified. If a desktop # milestone is defined, that will take default. shipping_features = map(getSortingMilestone, shipping_features) # First sort by name, then sort by feature milestone (latest first). shipping_features.sort(key=lambda f: f.name, reverse=False) shipping_features.sort(key=lambda f: f._sort_by_milestone, reverse=True) # Constructor the proper ordering. pre_release.extend(shipping_features) pre_release.extend(no_longer_pursuing_features) feature_list = [f.format_for_template(version) for f in pre_release] self._annotate_first_of_milestones(feature_list, version=version) # Memcache doesn't support saving values > 1MB. Break up features list into # chunks so we don't hit the limit. memcache.set_multi(Feature.set_feature_chunk_memcache_keys(KEY, feature_list)) else: temp_feature_list = [] # Reconstruct feature list by ordering chunks. for key in sorted(feature_list.keys()): temp_feature_list.extend(feature_list[key]) feature_list = temp_feature_list return feature_list @classmethod def get_shipping_samples(self, limit=None, update_cache=False): KEY = '%s|%s|%s' % (Feature.DEFAULT_MEMCACHE_KEY, 'samples', limit) feature_list = memcache.get(KEY) if feature_list is None or update_cache: # Get all shipping features. Ordered by shipping milestone (latest first). q = Feature.all() q.filter('impl_status_chrome IN', [ENABLED_BY_DEFAULT, ORIGIN_TRIAL, INTERVENTION]) q.order('-impl_status_chrome') q.order('-shipped_milestone') q.order('name') features = q.fetch(None) # Get non-shipping features (sans removed or deprecated ones) and # append to bottom of list. q = Feature.all() q.filter('impl_status_chrome <', ENABLED_BY_DEFAULT) q.order('-impl_status_chrome') q.order('-shipped_milestone') q.order('name') others = q.fetch(None) features.extend(others) # Filter out features without sample links. feature_list = [f.format_for_template() for f in features if len(f.sample_links)] memcache.set(KEY, feature_list) return feature_list def crbug_number(self): if not self.bug_url: return m = re.search(r'[\/|?id=]([0-9]+)$', self.bug_url) if m: return m.group(1) def new_crbug_url(self): url = 'https://bugs.chromium.org/p/chromium/issues/entry' params = ['components=' + self.blink_components[0] or BlinkComponent.DEFAULT_COMPONENT] crbug_number = self.crbug_number() if crbug_number and self.impl_status_chrome in ( NO_ACTIVE_DEV, PROPOSED, IN_DEVELOPMENT, BEHIND_A_FLAG, ORIGIN_TRIAL, INTERVENTION): params.append('blocking=' + crbug_number) if self.owner: params.append('cc=' + ','.join(self.owner)) return url + '?' + '&'.join(params) def __init__(self, *args, **kwargs): super(Feature, self).__init__(*args, **kwargs) # Stash existing values when entity is created so we can diff property # values later in put() to know what's changed. https://stackoverflow.com/a/41344898 for prop_name, prop in self.properties().iteritems(): old_val = getattr(self, prop_name, None) setattr(self, '_old_' + prop_name, old_val) def __notify_feature_subscribers_of_changes(self, is_update): """Async notifies subscribers of new features and property changes to features by posting to a task queue.""" # Diff values to see what properties have changed. changed_props = [] for prop_name, prop in self.properties().iteritems(): new_val = getattr(self, prop_name, None) old_val = getattr(self, '_old_' + prop_name, None) if new_val != old_val: changed_props.append({ 'prop_name': prop_name, 'old_val': old_val, 'new_val': new_val}) payload = json.dumps({ 'changes': changed_props, 'is_update': is_update, 'feature': self.format_for_template(version=2) }) # Create task to email subscribers. queue = taskqueue.Queue()#name='emailer') task = taskqueue.Task(method="POST", url='/tasks/email-subscribers', target='notifier', payload=payload) queue.add(task) # Create task to send push notifications queue = taskqueue.Queue() task = taskqueue.Task(method="POST", url='/tasks/send_notifications', target='notifier', payload=payload) queue.add(task) def put(self, **kwargs): is_update = self.is_saved() key = super(Feature, self).put(**kwargs) self.__notify_feature_subscribers_of_changes(is_update) return key # Metadata. created = db.DateTimeProperty(auto_now_add=True) updated = db.DateTimeProperty(auto_now=True) updated_by = db.UserProperty(auto_current_user=True) created_by = db.UserProperty(auto_current_user_add=True) intent_template_use_count = db.IntegerProperty(default = 0) # General info. category = db.IntegerProperty(required=True) name = db.StringProperty(required=True) intent_stage = db.IntegerProperty(default=0) summary = db.StringProperty(required=True, multiline=True) intent_to_implement_url = db.LinkProperty() origin_trial_feedback_url = db.LinkProperty() # A list of intent threads in the format "date|subject|url" intent_threads = db.StringListProperty() motivation = db.StringProperty(multiline=True) # Chromium details. bug_url = db.LinkProperty() blink_components = db.StringListProperty(required=True, default=[BlinkComponent.DEFAULT_COMPONENT]) impl_status_chrome = db.IntegerProperty(required=True) shipped_milestone = db.IntegerProperty() shipped_android_milestone = db.IntegerProperty() shipped_ios_milestone = db.IntegerProperty() shipped_webview_milestone = db.IntegerProperty() owner = db.ListProperty(db.Email) footprint = db.IntegerProperty() interop_compat_risks = db.StringProperty(multiline=True) ergonomics_risks = db.StringProperty(multiline=True) activation_risks = db.StringProperty(multiline=True) security_risks = db.StringProperty(multiline=True) debuggability = db.StringProperty(multiline=True) all_platforms = db.BooleanProperty() all_platforms_descr = db.StringProperty(multiline=True) wpt = db.BooleanProperty() wpt_descr = db.StringProperty(multiline=True) visibility = db.IntegerProperty(required=True) #webbiness = db.IntegerProperty() # TODO: figure out what this is # Standards details. standardization = db.IntegerProperty(required=True) spec_link = db.LinkProperty() tag_review = db.StringProperty(multiline=True) prefixed = db.BooleanProperty() explainer_links = db.StringListProperty() ff_views = db.IntegerProperty(required=True, default=NO_PUBLIC_SIGNALS) ie_views = db.IntegerProperty(required=True, default=NO_PUBLIC_SIGNALS) safari_views = db.IntegerProperty(required=True, default=NO_PUBLIC_SIGNALS) web_dev_views = db.IntegerProperty(required=True) ff_views_link = db.LinkProperty() ie_views_link = db.LinkProperty() safari_views_link = db.LinkProperty() web_dev_views_link = db.LinkProperty() ff_views_notes = db.StringProperty(multiline=True) ie_views_notes = db.StringProperty(multiline=True) safari_views_notes = db.StringProperty(multiline=True) web_dev_views_notes = db.StringProperty(multiline=True) doc_links = db.StringListProperty() sample_links = db.StringListProperty() #tests = db.StringProperty() search_tags = db.StringListProperty() comments = db.StringProperty(multiline=True) experiment_goals = db.StringProperty(multiline=True) experiment_timeline = db.StringProperty(multiline=True) experiment_risks = db.StringProperty(multiline=True) experiment_extension_reason = db.StringProperty(multiline=True) ongoing_constraints = db.StringProperty(multiline=True)
class Student(soc.models.role.Role): """Student details for a specific Program. """ school_name = db.StringProperty(required=True, verbose_name=ugettext('School Name')) school_name.group = ugettext("5. Education") school_name.help_text = ugettext( 'Please enter the full name of your school, college or university in' ' this field. Please use the complete formal name of your school, e.g.' ' UC Berkeley instead of Cal or UCB. It would be most wonderful if you' ' could provide your school\'s name in English, as all the program ' 'administrators speak English as their first language and it will make' ' it much easier for us to assemble program statistics, etc., later if' ' we can easily read the name of your school.') school_country = db.StringProperty(required=True, verbose_name=ugettext('School Country/Territory'), choices=countries.COUNTRIES_AND_TERRITORIES) school_country.group = ugettext("5. Education") #: School home page URL, not required here but enforced in the form for #: backwards compatibility. school_home_page = db.LinkProperty( required=False, verbose_name=ugettext("School Home Page URL")) school_home_page.group = ugettext("5. Education") #: School type can be only High school for GCI and can be University #: for GSoC. school_type = db.StringProperty(required=False, verbose_name=ugettext('School Type'), choices=['University', 'High School']) school_type.group = ugettext("5. Education") major = db.StringProperty(required=False, verbose_name=ugettext('Major Subject')) major.group = ugettext("5. Education") degree = db.StringProperty(required=False, verbose_name=ugettext('Degree'), choices=['Undergraduate', 'Master', 'PhD']) degree.group = ugettext("5. Education") expected_graduation = db.IntegerProperty(required=True, verbose_name=ugettext('Expected Graduation Year')) expected_graduation.help_text = ugettext("Pick your expected graduation year") expected_graduation.group = ugettext("5. Education") #: A many:1 relationship that ties multiple Students to the #: School that they attend. school = db.ReferenceProperty(reference_class=soc.models.school.School, required=False, collection_name='the_students') can_we_contact_you = db.BooleanProperty(verbose_name=ugettext( 'Can we contact you?')) can_we_contact_you.help_text = ugettext( 'Please check here if you would not mind being contacted by the Program' ' Administrators for follow up with members of the press who would like' ' to interview you about the program. You will not be contacted unless ' ' you successfully complete your project. <br />' '<b>Please note that checking this box has no effect on your chances' ' of being accepted into the program</b>.') can_we_contact_you.group = ugettext("2. Contact Info (Private)")
class Link(db.Model): name = db.StringProperty() href = db.LinkProperty() status = db.StringProperty()
class Dbevent(db.Model): """豆瓣同城事件数据模型""" id = db.IntegerProperty(required=True) title = db.StringProperty(required=True) self_link = db.StringProperty(required=True) image_link = db.LinkProperty() #TODO 活动照片 alternate_link = db.StringProperty(required=True) #TODO 关联对象 category = db.StringProperty(required=True) summary = db.TextProperty(required=True) content = db.TextProperty() #TODO 是否必填 invite_only = db.BooleanProperty() #需要邀请才能参加 can_invite = db.BooleanProperty() #允许参加者邀请其友邻来参加 participants = db.IntegerProperty() #参与人数 wishers = db.IntegerProperty() #想参加的人 album = db.StringProperty() #TODO 这个属性干嘛的? status = db.StringProperty() #TODO location_id = db.StringProperty(required=True) #城市id/拼音 location_name = db.StringProperty(required=True) #城市/名称 start_time = db.DateTimeProperty() end_time = db.DateTimeProperty(required=True) length = db.IntegerProperty(required=True) where = db.StringProperty() #地点 geo_point = db.StringProperty() #坐标 create_at = db.DateTimeProperty(required=True) # 时间戳 @staticmethod def getDbevents(location_id, category, length=None): """ 从数据库查询 Dbevents """ query = db.Query(Dbevent) query.filter('location_id =', location_id) #地点 if category != 'all': #类别 query.filter('category =', 'event.' + category) if isinstance(length, int) and length > 0: #活动长度 query.filter('length <=', length) dbevents = query.fetch(limit=1000) if len(dbevents) == 0: db.put(SyncQueue(key_name=location_id, location_id=location_id)) return dbevents def parse_event(self): """转换豆瓣数据模型到iCal数据模型""" event = Event() event.add('summary', self.title) desc = self.summary if isinstance(self.participants, int): desc += '\n\n' + u'参与人数 %d, 感兴趣人数 %d' \ %(self.participants, self.wishers) desc += '\n\n' + self.alternate_link event.add('DESCRIPTION', desc) #event.add('dtstart', self.start_time) event['dtstart'] = datetime.strftime( get_utc_datetime(self.start_time), '%Y%m%dT%H%M%SZ' ) #event.add('dtend', self.end_time) event['dtend'] = datetime.strftime( get_utc_datetime(self.end_time), '%Y%m%dT%H%M%SZ' ) event.add('STATUS', 'CONFIRMED') location = self.where if self.geo_point != None: location += u' @(%s)' %self.geo_point event.add('location', location) #event.add('dtstamp', datetime.now()) event['dtstamp'] = datetime.strftime(self.create_at, '%Y%m%dT%H%M%SZ') event['uid'] = self.id return event def isMoreThenAWeek(self): """活动距今时间是否大于一周了""" delta = datetime.utcnow() - get_utc_datetime(self.end_time) return delta.days > 7 @staticmethod def updateDb(location_id): """更新日历中活动""" dbevents = [] start = 1 max = config['sync']['max'] fetch_page_count = config['sync']['fetch_page_count'] # 当出现类型筛选时候 while len(dbevents) < max: try: xml = fetchEvent(location_id, max=fetch_page_count, start=start) except GetDoubanDataError, e1: logging.error(u'获取数据超时了,先存一些') break except OverQuotaError, e2: logging.error(u'url fetch 中断,限额了') break dbevents_new = Dbevent.xml2dbevents(xml) dbevents += dbevents_new if len(dbevents_new) < fetch_page_count: # 到最后一页了 break start += fetch_page_count