Пример #1
0
    def get(self, book_id=None):
        user = self.get_current_user()
        expand = ''
        if user is None:
            self.redirect('/books/{}'.format(book_id))
            return

        if book_id is not None:
            book_id = Book.get_long_uid(book_id)
            book = self.api.get_by_id(book_id)

            if not user.is_admin and user.uid not in book.authors:
                self.redirect('/books/{}'.format(book_id))

            book.book_image = util.extract_value_from_json(
                book.book_image, 'link')
            icon_link = util.extract_value_from_json(book.icon, 'link')
            book.icon = icon_link + '?size=360' if icon_link else icon_link
            book_json = json.dumps(book.to_client_dict())

        expand = Chapter.get_long_uid(self.request.get('expand'))

        self.write(
            'book-manage.html',
            book=book.to_client_dict(),
            book_json=book_json,
            expand=expand,
        )
Пример #2
0
    def get_chapters_with_pages(self, map_function=None):
        """Return ordered array of page data, organized by chapter"""
        pages_dict = {};
        chapters_dict = {};
        chapters = Chapter.get_by_id(self.chapters)
        if chapters is None:
            return {}
        chapter_pages = []
        page_ids = []
        for chapter in chapters:
            if chapter:
                chapters_dict[chapter.uid] = chapter.to_client_dict()
                page_ids.extend(chapter.pages)
                page_ids = list(set(page_ids))
                pages = Page.get_by_id(page_ids)
                pages = pages if pages is not None else []
                for page in pages:
                    page.icon = util.extract_value_from_json(page.icon, 'link')
                    page.icon = page.icon + '?size=360' if page.icon else page.icon
                    pages_dict[page.uid] = page.to_client_dict()
            # Returns a dict with the page uid and a link to the page detail
            def get_page_dict(page_id):
                page = pages_dict[page_id]
                # page['icon'] = util.extract_value_from_json(page['icon'], 'link')
                return page;

        for c in chapters:
            if c:
                chapter_dict = chapters_dict[c.uid]
                chapter_dict['pages'] = map(lambda page_id: get_page_dict(page_id), c.pages)
                chapter_pages.append(chapter_dict)
        return chapter_pages
Пример #3
0
    def get(self, page_id=None):
        user = self.get_current_user()
        full_chapter_id = ""
        book_id = ""
        if user is None:
            self.redirect('/discover')
            return

        # Check if user is practice creator or admin

        page_json = "''"
        if page_id:
            page = self.api.get_by_id(Page.get_long_uid(page_id))
            pageDict = page.to_client_dict()
            if page.icon:
                icon_path = util.extract_value_from_json(page.icon, 'link')
                pageDict[
                    'iconPath'] = icon_path + '?size=360' if icon_path else icon_path
            page_json = json.dumps(pageDict)

        if not user.is_admin and page_id:
            if user.uid not in page.authors:
                self.redirect('/pages/{}'.format(page_id))

        chapter_id = self.request.get('chapter_id')
        if chapter_id:
            full_chapter_id = Chapter.get_long_uid(chapter_id)
            book_id = Chapter.get_by_id(full_chapter_id).books[0]

        self.write(
            'page-upload.html',
            page_id=page_id,
            page_json=page_json,
            chapter_id=full_chapter_id,
            book_id=book_id,
        )
Пример #4
0
    def search_content(self, params):
        index = search.Index(config.content_index)
        search_text = ''

        # This search_results objects has properties `number_found`, `results`,
        # and `cursor`. See
        # https://cloud.google.com/appengine/docs/python/search/searchresultsclass
        # search.Query docs:
        # https://cloud.google.com/appengine/docs/python/search/queryclass
        # search.QueryOptions docs:
        # https://cloud.google.com/appengine/docs/python/search/queryoptionsclass

        # Pagination using a 'page' argument pulls n arguments offset by n*page
        # pull first set of results with page=0
        page_size = 20
        offset = 0
        if 'page' in params:
            offset = int(params.pop('page')) * page_size

        # Build the SortOptions with 2 sort keys
        sort1 = search.SortExpression(
            expression='display_order',
            direction=search.SortExpression.DESCENDING,
            default_value=0)
        sort2 = search.SortExpression(
            expression='votes_for',
            direction=search.SortExpression.DESCENDING,
            default_value=0)
        sort3 = search.SortExpression(
            expression='created',
            direction=search.SortExpression.DESCENDING,
            default_value=0)
        sort_opts = search.SortOptions(expressions=[sort1, sort2, sort3])

        search_results = index.search(
            search.Query(query_string=self._stringify_search_params(params),
                         options=search.QueryOptions(
                             limit=page_size,
                             offset=offset,
                             snippeted_fields=['summary', 'body'],
                             sort_options=sort_opts,
                         )))
        for result in search_results.results:
            if 'Chapter_' in result.doc_id:
                book_id = None
                for field in result.fields:
                    if field.name == 'books':
                        book_id = field.value

                if book_id:
                    book = Book.get_by_id(book_id)
                    result.fields.append(search.TextField('bookUID', book.uid))
                    result.fields.append(
                        search.TextField('bookTitle', book.title))
                    result.fields.append(
                        search.TextField(
                            'bookIcon',
                            util.extract_value_from_json(book.icon, 'link')))
                    result.fields.append(
                        search.TextField(
                            'chapterNumber',
                            str(book.chapters.index(result.doc_id) + 1)))

        result_dicts = [
            util.search_document_to_dict(doc) for doc in search_results.results
        ]

        result_dicts = self._annotate_search_content(result_dicts)

        return result_dicts
Пример #5
0
    def get(self, book_id):
        user = self.get_current_user()
        section_props = [{
            'default_value': '',
            'data_prop': 'acknowledgements',
            'scope_prop': 'pageAcknowledgements',
            'heading_title': 'Acknowledgements',
            'value_suffix': None,
        }, {
            'default_value': '',
            'data_prop': 'preferred_citation',
            'scope_prop': 'pageCitations',
            'heading_title': 'Preferred Citation',
            'value_suffix': None,
        }]
        id = Book.get_long_uid(book_id)
        book = self.api.get_by_id(id)
        first_chapter_link = ''
        first_page_link = ''
        authors = []
        if book is not None:
            if book.authors:
                authors_data = User.get_by_id(book.authors)
                for a in authors_data:
                    authors.append(a.to_client_dict())
            chapter_page_data = book.get_chapters_with_pages()
            pages_count = 0
            if book.chapters:
                for chapter in chapter_page_data:
                    pages_count += len(chapter['pages'])
                    # Only pass page data if they are an author
                    chapter_pages = []
                    for chapter_page in chapter['pages']:
                        if chapter_page['status'] == 'approved':
                            chapter_pages.append(chapter_page)
                        elif user and (user.is_admin
                                       or user.uid in chapter_page['authors']):
                            chapter_pages.append(chapter_page)
                    chapter.pages = chapter_pages
                first_page_link = '/books/{}'.format(book.short_uid)
                has_first_page = chapter_page_data and chapter_page_data[
                    0] and chapter_page_data[0]['pages']
                book.page_count = pages_count
                if has_first_page:
                    first_page_link = '/{}/{}/{}'.format(
                        first_page_link, chapter_page_data[0]['short_uid'],
                        chapter_page_data[0]['pages'][0]['short_uid'])
            for file_prop in Book.file_props:
                json_file = getattr(book, file_prop)
                setattr(book, file_prop,
                        util.extract_value_from_json(json_file, 'link'))

            book.color = '#58b070'
            #TODO
            book.target_audience = 'TODO'
            book.estimated_duration = 9999
            #TODO: should you just sum pages durations? Any default value?

            # Get translated text and locale
            if book.locale in config.available_locales:
                locale = book.locale
            else:
                locale = default_locale

            locale = 'en'

            creator = authors[0]
            last_editor = User.get_by_id(
                book.last_edited_by) if book.last_edited_by else None
            user = self.get_current_user()
            is_author = user.uid in book.authors if user is not None else False
            additional_authors = authors[1:].remove(
                last_editor
            ) if last_editor and last_editor in authors[1:] else authors[1:]

            self.write(
                'book-page.html',
                book=book,
                first_page_link=first_page_link,
                locale=locale,
                translation=locales.translations[locale]["books"],
                chapters_json=json.dumps(book.chapters),
                chapter_page_data=chapter_page_data,
                chapter_page_data_json=json.dumps(chapter_page_data),
                section_props=section_props,
                last_editor=last_editor,
                creator=creator,
                additional_authors=additional_authors,
                is_author=is_author or (user and user.is_admin),
                is_admin=user and user.is_admin,
                entity_type='book',
            )
        else:
            return self.http_not_found()
Пример #6
0
class Page(Content):
    ####
    chapters = sndb.StringProperty(repeated=True)  # unordered parents
    authors = sndb.StringProperty(
        repeated=True, search_type=search.TextField)  # unordered children

    icon = sndb.StringProperty(default='', search_type=search.TextField)
    iconPath = sndb.ComputedProperty(
        lambda self: util.extract_value_from_json(self.icon, 'link'))

    # "Challenges & Preconditions"
    preconditions_for_success = sndb.TextProperty(
        default=''
    )  # Up to 10,000 characters, HTML text with ability to include links and images.
    # "Connection to Equity"
    advances_equity = sndb.TextProperty(
        default='')  # up to 5,000 characters, HTML
    # "Time Required"
    time_required = sndb.StringProperty(default='')  # minutes
    # "Required Materials"
    required_materials = sndb.TextProperty(
        default='')  # up to 10,000 characters
    # "Associated Measures"
    associated_measures = sndb.TextProperty(
        default='')  # Up to 5000 characters, HTML
    # "Evidence of Effectiveness"
    evidence_of_effectiveness = sndb.TextProperty(
        default=''
    )  # Up to 10,000 characters, HTML text with ability to include links and images.
    # "Related BELE Library Pages"
    related_pages = sndb.StringProperty(
        repeated=True)  # up to 10 related pages.
    # "Optional Contact Information"
    acknowledgements = sndb.TextProperty(
        default='')  # Up to 5000 characters, HTML
    # "Preferred Citation"
    preferred_citation = sndb.StringProperty(
        default='')  # Up to 1000 characters, text
    use_license = sndb.StringProperty(default='')

    status = sndb.StringProperty(
        default='pending',
        choices=['draft', 'pending', 'approved', 'rejected',
                 'deleted'])  # enum: draft, pending, approved, or rejected
    ####

    mindset_tags = sndb.StringProperty(repeated=True)
    page_tags = sndb.StringProperty(repeated=True)
    time_of_year = sndb.StringProperty(default='')
    class_period = sndb.StringProperty(default='')
    type = sndb.StringProperty(default='text', search_type=search.AtomField)
    body = sndb.TextProperty(default='', search_type=search.TextField)
    youtube_id = sndb.StringProperty(default='')
    iframe_src = sndb.StringProperty(default='')
    has_files = sndb.BooleanProperty(default=False,
                                     search_type=search.AtomField)
    pending = sndb.BooleanProperty(default=True)
    display_order = sndb.IntegerProperty(default=1,
                                         search_type=search.NumberField)
    votes_for = sndb.IntegerProperty(default=0, search_type=search.NumberField)
    num_comments = sndb.IntegerProperty(default=0,
                                        search_type=search.NumberField)

    # class properties that contain files as json strings
    file_props = ['icon']

    @property
    def ui_props(self):
        return {
            'time_required': {
                'default_value': Page.time_required._default,
                'data_prop': 'time_required',
                'scope_prop': 'pageTime',
                'heading_title': 'Time Required',
                'value_suffix': None,
            },
            'required_materials': {
                'default_value': Page.required_materials._default,
                'data_prop': 'required_materials',
                'scope_prop': 'pageMaterials',
                'heading_title': 'Required Materials',
                'value_suffix': None,
            },
            'preconditions_for_success': {
                'default_value': Page.preconditions_for_success._default,
                'data_prop': 'preconditions_for_success',
                'scope_prop': 'pagePreconditions',
                'heading_title': 'Preconditions for Success',
                'value_suffix': None,
            },
            'advances_equity': {
                'default_value': Page.advances_equity._default,
                'data_prop': 'advances_equity',
                'scope_prop': 'advancesEquity',
                'heading_title': 'Connection to Equity',
                'value_suffix': None,
            },
            'evidence_of_effectiveness': {
                'default_value': Page.evidence_of_effectiveness._default,
                'data_prop': 'evidence_of_effectiveness',
                'scope_prop': 'pageEvidence',
                'heading_title': 'Evidence of Effectiveness',
                'value_suffix': None,
            },
            'associated_measures': {
                'default_value': Page.associated_measures._default,
                'data_prop': 'associated_measures',
                'scope_prop': 'pageMeasures',
                'heading_title': 'Associated Measures',
                'value_suffix': None,
            },
            'acknowledgements': {
                'default_value': Page.acknowledgements._default,
                'data_prop': 'acknowledgements',
                'scope_prop': 'pageAcknowledgements',
                'heading_title': 'Acknowledgements',
                'value_suffix': None,
            },
            'preferred_citation': {
                'default_value': Page.preferred_citation._default,
                'data_prop': 'preferred_citation',
                'scope_prop': 'pageCitations',
                'heading_title': 'Preferred Citation',
                'value_suffix': None,
            },
        }

    @classmethod
    def create(klass, **kwargs):
        """Sends email to interested parties.
        """
        page = super(klass, klass).create(**kwargs)

        # Email interested parties that a page has been uploaded.
        # mandrill.send(
        #     to_address=config.page_upload_recipients,
        #     subject="Page Uploaded to Mindset Kit!",
        #     template="page_upload_notification.html",
        #     template_data={'user': page.get_parent_user(),
        #                    'page': page,
        #                    'domain': 'https://{}'.format(os.environ['HOSTING_DOMAIN'])},
        # )

        # logging.info('model.Page queueing an email to: {}'
        #              .format(config.page_upload_recipients))
        logging.info('creating page')
        return page

    @classmethod
    def convert_uid(klass, short_or_long_uid):
        """Changes long-form uid's to short ones, and vice versa.

        Overrides method provided in Model.

        Long form example: Page_Pb4g9gus
        Short form exmaple: Pb4g9gus
        """
        if '_' in short_or_long_uid:
            return ''.join([short_or_long_uid.split('_')[1]])
        else:
            return 'Page_{}'.format(short_or_long_uid[:4])

    @classmethod
    def get_long_uid(klass, short_or_long_uid):
        """Changes short of long-form uid's to long ones.

        Overrides method provided in Model.

        Long form example: Page_Pb4g9gus.User_oha4tp8a
        Short form exmaple: Pb4g9gusoha4tp8a
        """
        if '_' in short_or_long_uid:  # is long
            return short_or_long_uid
        else:  # is short
            return 'Page_{}'.format(short_or_long_uid[-8:])

    @classmethod
    def get_popular_pages(klass):
        """Fetches popular pages to display on landing page

        @todo: figure out a way to generate this list
        - Possibly adding a field to pages and flagging x pages / week
        - Needs more discussion
        """
        pages = []
        query = Page.query(
            Page.deleted == False,
            Page.listed == True,
            Page.promoted == True,
        )
        query.order(-Page.created)
        pages = query.fetch(20)
        if len(pages) > 6:
            pages = random.sample(pages, 6)
        return pages

    def add_file_data(self, file_dicts, entity_field=None):
        """Save dictionaries of uploaded file meta data."""
        # Process specific field json files, specified by entity_field parameter
        entity_fields_whitelist = self.file_props
        if entity_field in entity_fields_whitelist:
            for k, v in file_dicts[0].items():
                if hasattr(v, 'isoformat'):
                    file_dicts[0][k] = v.isoformat()

            setattr(self, entity_field,
                    json.dumps(file_dicts[0], default=util.json_dumps_default))

        jp = self.json_properties

        # Process generic json files if not an entity field
        if entity_field is None:
            if 'files' not in jp:
                jp['files'] = []
            jp['files'].extend(file_dicts)

            self.json_properties = jp

        self.has_files = 'files' in jp and len(jp['files']) > 0

    def remove_file_data(self, file_key):
        """Remove file dictionaries from existing json_properties"""
        jp = self.json_properties
        # Find and remove file from 'files' in json_properties
        if 'files' in jp:
            for index, file_dict in enumerate(jp['files']):
                if file_key == file_dict[u'gs_object_name']:
                    jp['files'].pop(index)

        self.json_properties = jp
        self.has_files = len(jp['files']) > 0

    def get_parent_user(self):
        return {}

    def check_status_update(self, **kwargs):
        """Checks the status of an updated page to determine if the creator
        should be notified of approval or rejection.
        """
        new_status = kwargs.get('status', None)
        now_reviewed = new_status in ('approved', 'rejected')
        changing = self.status != new_status
        if not now_reviewed or not changing:
            return

        authors = User.get_by_id(self.authors) or []
        for author in authors:
            short_name = author.first_name or ''
            full_name = author.full_name
            if new_status == 'approved':
                # Send acceptance message
                # @todo: add name to subject line
                mandrill.send(
                    to_address=author.email,
                    subject="Your page upload is approved!",
                    template="accepted_notification.html",
                    template_data={
                        'short_name': short_name,
                        'full_name': full_name,
                        'entity_name': self.title,
                        'entity_url': '/pages/' + self.short_uid,
                        'domain':
                        'https://{}'.format(os.environ['HOSTING_DOMAIN']),
                        'year': datetime.date.today().year,
                    },
                )

            else:
                # Send rejection message
                mandrill.send(
                    to_address=author.email,
                    subject="We couldn't approve your page...",
                    template="rejected_notification.html",
                    template_data={
                        'short_name': short_name,
                        'full_name': full_name,
                        'entity_name': self.title,
                        'entity_url': '/pages/' + self.short_uid,
                        'edit_url': '/pages/edit/' + self.short_uid,
                        'domain':
                        'https://{}'.format(os.environ['HOSTING_DOMAIN']),
                        'year': datetime.date.today().year,
                    },
                )

    def to_search_document(self, rank=None):
        """Extends inherited method in Model."""
        fields = super(Page, self)._get_search_fields()

        # Add information about the parent user to the search document.
        for author_id in self.authors:
            author = self.get_by_id(author_id)
            # Allow for empty first/last names, and default to an empty string.
            if author is not None:
                user_name = ''.join([(author.first_name or ''),
                                     (author.last_name or '')])
                fields.append(search.TextField(name='author', value=user_name))

        # Simplify checking for video and file attachments
        if self.has_files:
            fields.append(search.AtomField(name='content_type', value='files'))
        if self.youtube_id != '':
            fields.append(search.AtomField(name='content_type', value='video'))

        return search.Document(doc_id=self.uid,
                               fields=fields,
                               rank=rank,
                               language='en')

    def to_client_dict(self):
        d = super(Page, self).to_client_dict()
        official_tags = []
        additional_tags = []
        for tag in d['tags']:
            if tag in OFFICIAL_TAGS:
                official_tags.append(tag)
            else:
                additional_tags.append(tag)
        d['tags'] = official_tags
        d['additional_tags'] = ', '.join(additional_tags)

        return d