Exemplo n.º 1
0
    def bulk_retrieve(cls, user_data_objects=None, content_id=None):
        args = locals()
        storage_handler = orm.StorageHandler()
        if user_data_objects is not None:
            user_ids = [user.user_id for user in user_data_objects]
            try:
                info_tuples = storage_handler.call(select.get_user_info,
                                                   user_ids=user_ids)
            except:
                raise
        elif content_id is not None:
            try:
                info_tuples = storage_handler.call(select.get_user_info,
                                                   content_id=content_id)
            except:
                raise
        else:
            raise InputError("No arguments provided.",
                             message="No data provided.",
                             inputs=args)

        return [
            UserData(user_id=tup[0], user_name=tup[1], email=tup[2])
            for tup in info_tuples
        ]
Exemplo n.º 2
0
 def bulk_retrieve(cls, content_ids):
     storage_handler = orm.StorageHandler()
     try:
         name_object_tuples = storage_handler.call(select.get_names,
                                                   content_ids=content_ids)
     except:
         raise
     else:
         return {
             content_id: Name(name_id=name.name_id,
                              name=name.name,
                              name_type=name.name_type,
                              timestamp=name.timestamp)
             for content_id, name in name_object_tuples
         }
Exemplo n.º 3
0
class Content:
    """
    Attributes:
        content_id: Integer. Defaults to None.
        timestamp: Datetime. Defaults to None.
        last_edited_timestamp: Datetime. Defaults to None.
        deleted_timestamp: Datetime. Defaults to None.
        first_author: UserData object. Defaults to None.
        authors: List of UserData objects. Defaults to None.
        content_type: String. Defaults to None.
        name: Name object. Defaults to None.
        alternate_names: List of Name objects. Defaults to None.
        text: Text object. Defaults to None.
        keywords: List of Strings. Defaults to None.
        citations: List of Strings. Defaults to None.
        notification: String. Defaults to None.

    Properties:
        json_ready: Dictionary.

    Instance Methods:
        _transfer, store, _delete

    Class Methods:
        bulk_retrieve, get_content_types, check_uniqueness, filter_by,
        search, autocomplete, update
    """

    storage_handler = orm.StorageHandler()

    content_id = None  # Integer.
    timestamp = None  # Datetime.
    last_edited_timestamp = None  # Datetime.
    deleted_timestamp = None  # Datetime.
    first_author = None  # UserData object.
    authors = None  # List of UserData objects.
    content_type = None  # String.
    name = None  # Name object.
    alternate_names = None  # List of Name objects.
    text = None  # Text object.
    keywords = None  # List of Strings.
    citations = None  # List of Strings.
    notification = None

    def __init__(self,
                 content_id=None,
                 accepted_edit_id=None,
                 rejected_edit_id=None,
                 first_author_id=None,
                 content_type=None,
                 name=None,
                 alternate_names=None,
                 text=None,
                 keywords=None,
                 citations=None,
                 content_piece=None):
        """
        Args:
            content_id: Integer.
            accepted_edit_id: Integer.
            rejected_edit_id: Integer.
            first_author_id: Integer.
            content_type: String.
            name: String.
            alternate_names: List of Strings.
            text: String.
            keywords: List of Strings.
            citations: List of Strings.
            content_piece: ContentPiece object.
        """
        args = locals()
        if (content_id is not None or accepted_edit_id is not None
                or rejected_edit_id is not None):
            try:
                content_piece = self.storage_handler.call(
                    select.get_content_piece,
                    content_id=content_id,
                    accepted_edit_id=accepted_edit_id,
                    rejected_edit_id=rejected_edit_id)
            except:
                raise
            else:
                self._transfer(content_piece)
                self.stored = True
        elif content_piece is not None:
            self._transfer(content_piece)
            self.stored = True
        else:
            if (not first_author_id or not content_type or not name or not text
                    or not keywords):
                raise InputError("Invalid argument(s) provided.",
                                 message="Insufficient data provided.",
                                 inputs=args)
            self.timestamp = datetime.utcnow()
            self.last_edited_timestamp = self.timestamp
            self.first_author = UserData(user_id=first_author_id)
            self.authors = [self.first_author]
            self.content_type = content_type
            self.name = Name(name=name,
                             name_type="primary",
                             timestamp=self.timestamp,
                             last_edited_timestamp=self.timestamp)
            self.alternate_names = [
                Name(name=alt_name,
                     name_type="alternate",
                     timestamp=self.timestamp,
                     last_edited_timestamp=self.last_edited_timestamp)
                for alt_name in list(set(alternate_names))
                if alt_name and alt_name != self.name.name
            ]
            self.text = Text(text=text,
                             timestamp=self.timestamp,
                             last_edited_timestamp=self.timestamp)
            self.keywords = list(set(keywords))
            self.citations = list(set(citations))
            Content._check_legal(name, alternate_names, text, keywords,
                                 citations)
            self.stored = False

    @staticmethod
    def _check_legal(name, alternate_names, text, keywords, citations):
        """
        Checks that all content parts are legal, that is, satisfy
        the content requirements.

        Args:
            name: String.
            alternate_names: List of strings.
            text: String.
            keywords: List of strings.
            citations: List of strings.
        """
        names = [name] + alternate_names
        if any([
                config.SMALL_PART_MAX_CHARS <
                len(alt_name) - alt_name.count(" ")
                or config.SMALL_PART_MIN_CHARS >
                len(alt_name) - alt_name.count(" ") for alt_name in names
        ]):
            raise ContentError(message="Please provide name(s) containing "
                               "between " + str(config.SMALL_PART_MIN_CHARS) +
                               " and " + str(config.SMALL_PART_MAX_CHARS) +
                               " characters.")
        if (config.LARGE_PART_MAX_CHARS < len(text) - text.count(" ")
                or config.LARGE_PART_MIN_CHARS > len(text) - text.count(" ")):
            raise ContentError(message="Please provide a text body containing "
                               "between " + str(config.LARGE_PART_MIN_CHARS) +
                               " and " + str(config.LARGE_PART_MAX_CHARS) +
                               " characters.")
        if any([
                config.LARGE_PART_MAX_CHARS <
                len(citation) - citation.count(" ")
                or config.SMALL_PART_MIN_CHARS >
                len(citation) - citation.count(" ") for citation in citations
        ]):
            raise ContentError(message="Please provide citation(s) containing "
                               "between " + str(config.SMALL_PART_MIN_CHARS) +
                               " and " + str(config.SMALL_PART_MAX_CHARS) +
                               " characters.")
        if any([
                config.SMALL_PART_MAX_CHARS < len(keyword) - keyword.count(" ")
                or
                config.SMALL_PART_MIN_CHARS > len(keyword) - keyword.count(" ")
                for keyword in keywords
        ]):
            raise ContentError(message="Please provide keyword(s) containing "
                               "between " + str(config.SMALL_PART_MIN_CHARS) +
                               " and " + str(config.SMALL_PART_MAX_CHARS) +
                               " characters.")

    def _transfer(self, content_piece):
        """
        Transfers the data in a ContentPiece object to this Content object.

        Args:
            content_piece: ContentPiece object.
        """
        self.content_id = content_piece.content_id
        self.timestamp = content_piece.timestamp
        self.last_edited_timestamp = content_piece.last_edited_timestamp
        self.deleted_timestamp = content_piece.deleted_timestamp
        self.first_author = UserData(
            user_id=content_piece.first_author.user_id,
            user_name=content_piece.first_author.user_name)
        self.authors = [
            UserData(user_id=author.user_id, user_name=author.user_name)
            for author in content_piece.authors
        ]
        self.content_type = content_piece.content_type.content_type
        self.name = Name(
            name_id=content_piece.name.name_id,
            name=content_piece.name.name,
            name_type=content_piece.name.name_type,
            timestamp=content_piece.name.timestamp,
            last_edited_timestamp=content_piece.name.last_edited_timestamp)
        self.alternate_names = [
            Name(name_id=name.name_id,
                 name=name.name,
                 name_type=name.name_type,
                 timestamp=name.timestamp,
                 last_edited_timestamp=name.last_edited_timestamp)
            for name in content_piece.alternate_names
        ]
        self.text = Text(
            text_id=content_piece.text.text_id,
            text=content_piece.text.text,
            timestamp=content_piece.text.timestamp,
            last_edited_timestamp=content_piece.text.last_edited_timestamp)
        self.keywords = [keyword.keyword for keyword in content_piece.keywords]
        self.citations = [
            citation.citation_text for citation in content_piece.citations
        ]

    @classmethod
    def bulk_retrieve(cls,
                      sort="created_at",
                      content_part=None,
                      user_id=None,
                      page_num=0,
                      return_count=False,
                      ids_only=False):
        """
        Args:
            sort: String, accepts 'created_at' or 'last_edited_at'.
                Defaults to 'created_at'.
            content_part: String, accepts 'keyword', 'content_type', 'name',
                or 'citation'. Defaults to None.
            user_id: Integer. Defaults to None.
            page_num: Integer. Defaults to 0.
            return_count: Boolean. Defaults to False.
            ids_only: Boolean. Defaults to False.
        Returns:
            List of Content objects.
        """
        if user_id is not None:
            try:
                content_pieces = cls.storage_handler.call(
                    select.get_content_pieces, user_id=user_id)
            except:
                raise
            else:
                content = [
                    Content(content_piece=content_piece)
                    for content_piece in content_pieces
                ]
                content = sorted(content,
                                 key=lambda piece: piece.last_edited_timestamp,
                                 reverse=True)
                content_count = len(content)
                if ids_only:
                    content = [
                        content_piece.content_id for content_piece in content
                    ]
                if page_num != 0 and return_count:
                    return content[10 * (page_num - 1):10 *
                                   page_num], content_count
                elif page_num != 0:
                    return content[10 * (page_num - 1):10 * page_num]
                elif return_count:
                    return content, content_count
                else:
                    return content
        else:
            try:
                if page_num < 1:
                    raise InputError("Invalid argument(s) provided.",
                                     message="Invalid data provided.",
                                     inputs={"page_num": page_num})
                content_pieces = cls.storage_handler.call(
                    select.get_content_pieces,
                    sort=sort,
                    content_part=content_part,
                    page_num=page_num)
                content = [
                    Content(content_piece=content_piece)
                    for content_piece in content_pieces
                ]
            except:
                raise
            else:
                return content

    @classmethod
    def get_parts(cls, content_part, page_num=None, per_page=None):
        """
        Args:
            content_part: String, accepts 'content_type', 'keyword',
                or 'citation'.
            page_num: Integer. Defaults to None.
            per_page: Integer. Defaults to None.
        Returns:
            List of content part strings.
        """
        try:
            if content_part == "content_type":
                content_types = cls.storage_handler.call(
                    select.get_content_types)
                content_types = [
                    content_type.content_type for content_type in content_types
                ]
                return content_types
            elif content_part == "keyword":
                keywords = cls.storage_handler.call(select.get_keywords,
                                                    page_num=page_num,
                                                    per_page=per_page)
                keywords = [keyword.keyword for keyword in keywords]
                return keywords
            elif content_part == "citation":
                citations = cls.storage_handler.call(select.get_citations,
                                                     page_num=page_num,
                                                     per_page=per_page)
                citations = [citation.citation_text for citation in citations]
                return citations
            else:
                raise InputError("Invalid argument(s) provided.",
                                 message="Invalid data provided.",
                                 inputs={"content_part": content_part})
        except:
            raise

    @classmethod
    def check_uniqueness(cls, content_id, part_string, content_part):
        """
        Args:
            content_id: Integer.
            part_string: String.
            content_part: String, accepts 'name', 'alternate_name', 
                keyword', or 'citation'.
        Returns:
            Boolean indicating whether the name, keyword, or citation is
            unique among the content piece's (identified by content_id)
            names (incl. alternate names), keywords, or citations,
            respectively.
        """
        if content_part == "name" or content_part == "alternate_name":
            try:
                name = cls.storage_handler.call(select.get_names,
                                                content_id=content_id)
                alternate_names = cls.storage_handler.call(
                    select.get_alternate_names, content_id)
            except:
                raise
            else:
                return (part_string != name.name and part_string
                        not in [name.name for name in alternate_names])
        elif content_part == "keyword":
            try:
                keywords = cls.storage_handler.call(select.get_keywords,
                                                    content_id)
            except:
                raise
            else:
                return part_string not in [
                    keyword.keyword for keyword in keywords
                ]
        elif content_part == "citation":
            try:
                citations = cls.storage_handler.call(select.get_citations,
                                                     content_id)
            except:
                raise
            else:
                return part_string not in [
                    citation.citation_text for citation in citations
                ]
        else:
            raise InputError("Invalid argument(s) provided.",
                             message="Invalid data provided.",
                             inputs={"content_part": content_part})

    @classmethod
    def filter_by(cls, content_part, part_string, page_num=1):
        """
        Args:
            content_part: String, accepts 'keyword', 'content_type',
                'name', or 'citation'.
            part_string: String.
            page_num: Positive integer. Defaults to None.
        Returns:
            Dictionary of results.
        """
        try:
            results = search_api.filter_by(content_part,
                                           part_string,
                                           page_num=page_num)
        except:
            raise
        else:
            for i in range(len(results["results"])):
                try:
                    del results["results"][i]["score"]
                except KeyError:
                    pass
            return results

    @classmethod
    def search(cls, query, page_num=1):
        """
        Args:
            query: String.
            page_num: Integer. Defaults to 1.
        Returns:
            Dictionary of results.
        """
        try:
            results = search_api.search(query, page_num)
        except:
            raise
        else:
            for i in range(len(results["results"])):
                del results["results"][i]["score"]
            return results

    @classmethod
    def autocomplete(cls, content_part, query):
        """
        Args:
            content_part: String, expects 'name', 'keyword', or 'citation'.
            query: String.
        Returns:
            List of completion dictionaries.
        """
        try:
            completions = search_api.autocomplete(content_part, query)
        except:
            raise
        else:
            return completions

    def store(self):
        if self.stored:
            return
        else:
            keywords_for_storage = []
            for keyword_string in self.keywords:
                try:
                    keyword = self.storage_handler.call(
                        select.get_keyword, keyword_string)
                except SelectError:
                    keyword = orm.Keyword(keyword=keyword_string,
                                          timestamp=self.timestamp)
                    keywords_for_storage.append(keyword)
                except:
                    raise
                else:
                    keywords_for_storage.append(keyword)
            citations_for_storage = []
            for citation_string in self.citations:
                try:
                    citation = self.storage_handler.call(
                        select.get_citation, citation_string)
                except SelectError:
                    citation = orm.Citation(citation_text=citation_string,
                                            timestamp=self.timestamp)
                    citations_for_storage.append(citation)
                except:
                    raise
                else:
                    citations_for_storage.append(citation)
            try:
                content_id = self.storage_handler.call(
                    action.store_content_piece,
                    self.first_author.user_id,
                    self.name.storage_object,
                    self.text.storage_object,
                    self.storage_handler.call(select.get_content_type,
                                              self.content_type),
                    keywords_for_storage,
                    self.timestamp,
                    citations=citations_for_storage,
                    alternate_names=[
                        name.storage_object for name in self.alternate_names
                    ],
                )
                index.index_content_piece(
                    content_id, self.name.name,
                    [name.name for name in self.alternate_names],
                    self.text.text, self.content_type, self.keywords,
                    self.citations if self.citations is not None else [])
            except:
                raise
            else:
                self.content_id = content_id
                self.stored = True

    @classmethod
    def update(cls,
               content_id,
               content_part,
               update_type,
               timestamp,
               part_text=None,
               part_id=None,
               edited_citations=None):
        """
        Args:
            content_id: Integer.
            content_part: String, expects 'name', 'alternate_name',
                'text', 'content_type', 'keyword', or 'citation'.
            update_type: String, accepts 'modify', 'add', or 'remove'.
            timestamp: Datetime.
            part_text: String or orm.Name. Defaults to None.
            part_id: Integer. Defaults to None.
            edited_citations: List of orm.Citation objects.
                Defaults to None.
        """
        args = locals()
        if content_part == "content_type" and update_type == "modify":
            try:
                content_type = select.get_content_type(part_text)
                cls.storage_handler.call(action.update_content_type,
                                         content_id, content_type)
                index.update_content_piece(
                    content_id,
                    content_part,
                    part_string=content_type.content_type)
            except:
                raise
        elif update_type == "add":
            try:
                if isinstance(part_text, orm.Name):
                    cls.storage_handler.call(action.store_content_part,
                                             part_text, content_id)
                    index.add_to_content_piece(content_id, "alternate_name",
                                               content_part.name)
                elif content_part == "keyword" and part_text is not None:
                    try:
                        keyword = cls.storage_handler.call(
                            select.get_keyword, part_text)
                    except SelectError:
                        keyword = orm.Keyword(keyword=part_text,
                                              timestamp=timestamp)
                    cls.storage_handler.call(action.store_content_part,
                                             keyword, content_id)
                    index.add_to_content_piece(content_id, content_part,
                                               keyword.keyword)
                elif content_part == "citation" and part_text is not None:
                    try:
                        citation = cls.storage_handler.call(
                            select.get_citation, part_text)
                    except SelectError:
                        citation = orm.Citation(citation_text=part_text,
                                                timestamp=timestamp)
                    cls.storage_handler.call(action.store_content_part,
                                             citation,
                                             content_id,
                                             edited_citations=edited_citations)
                    index.add_to_content_piece(content_id, content_part,
                                               citation.citation_text)
                else:
                    raise InputError("Invalid argument(s) provided.",
                                     message="Invalid data provided.",
                                     inputs=args)
            except:
                raise
        elif part_id is not None and update_type == "remove":
            try:
                cls.storage_handler.call(action.remove_content_part,
                                         content_id, part_id, content_part)
                if content_part == "alternate_name":
                    alternate_names = cls.storage_handler.call(
                        select.get_alternate_names, content_id)
                    alternate_names = [name.name for name in alternate_names]
                    index.update_content_piece(content_id,
                                               content_part,
                                               part_strings=alternate_names)
                elif content_part == "keyword":
                    keywords = cls.storage_handler.call(
                        select.get_keywords, content_id)
                    keywords = [keyword.keyword for keyword in keywords]
                    index.update_content_piece(content_id,
                                               content_part,
                                               part_strings=keywords)
                elif content_part == "citation":
                    citations = cls.storage_handler.call(
                        select.get_citations, content_id)
                    citations = [
                        citation.citation_text for citation in citations
                    ]
                    index.update_content_piece(content_id,
                                               content_part,
                                               part_strings=citations)
                else:
                    raise InputError("Invalid argument(s) provided.",
                                     message="Invalid data provided.",
                                     inputs=args)
            except:
                raise
        elif part_id is not None and update_type == "modify":
            try:
                if content_part == "name" or content_part == "alternate_name":
                    cls.storage_handler.call(action.update_content_part,
                                             part_id, "name", part_text)
                elif content_part == "text":
                    cls.storage_handler.call(action.update_content_part,
                                             part_id, content_part, part_text)
                if content_part == "name" or content_part == "text":
                    index.update_content_piece(content_id,
                                               content_part,
                                               part_string=part_text)
                elif content_part == "alternate_name":
                    alternate_names = cls.storage_handler.call(
                        select.get_alternate_names, content_id)
                    alternate_names = [name.name for name in alternate_names]
                    index.update_content_piece(content_id,
                                               content_part,
                                               part_strings=alternate_names)
                elif content_part == "keyword":
                    try:
                        cls.update(content_id, content_part, "remove", part_id)
                    except MissingDataError:
                        pass
                    cls.update(content_id,
                               content_part,
                               "add",
                               timestamp,
                               part_text=part_text)
                    keywords = cls.storage_handler.call(
                        select.get_keywords, content_id)
                    keywords = [keyword.keyword for keyword in keywords]
                    index.update_content_piece(content_id,
                                               content_part,
                                               part_strings=keywords)
                elif content_part == "citation":
                    try:
                        cls.update(content_id, content_part, "remove", part_id)
                    except MissingDataError:
                        other_edits = cls.storage_handler.call(
                            select.get_citations,
                            content_id,
                            edited_citation_id=part_id)
                        if len(other_edits) != 1:
                            raise ApplicationError(
                                error_data=other_edits,
                                message="Multiple citations found. "
                                "Cannot resolve citation ambiguity.")
                        else:
                            previous_citation = other_edits[0]
                            edited_citations = (
                                [previous_citation] +
                                previous_citation.edited_citations)
                            cls.update(content_id, content_part, "remove",
                                       previous_citation.citation_id)
                    cls.update(content_id,
                               content_part,
                               "add",
                               timestamp,
                               part_text=part_text,
                               edited_citations=edited_citations)
                    citations = cls.storage_handler.call(
                        select.get_citations, content_id)
                    citations = [
                        citation.citation_text for citation in citations
                    ]
                    index.update_content_piece(content_id,
                                               content_part,
                                               part_strings=citations)
                else:
                    raise InputError("Invalid argument(s) provided.",
                                     message="Invalid data provided.",
                                     inputs={"content_part": content_part})
            except:
                raise
        else:
            raise InputError("Invalid argument(s) provided.",
                             message="Invalid data provided.",
                             inputs=args)

    def _delete(self):
        if not self.stored:
            return
        else:
            deleted_timestamp = datetime.now()
            try:
                self.storage_handler.call(action.delete_content_piece,
                                          self.content_id, deleted_timestamp)
                index.remove_content_piece(self.content_id)
            except:
                raise
            else:
                self.deleted_timestamp = deleted_timestamp

    @property
    def json_ready(self):
        return {
            "content_id":
            self.content_id,
            "timestamp":
            (str(self.timestamp) if self.timestamp is not None else None),
            "deleted_timestamp":
            (str(self.deleted_timestamp)
             if self.deleted_timestamp is not None else None),
            "first_author": (self.first_author.json_ready
                             if self.first_author is not None else None),
            "authors": ([author.json_ready for author in self.authors]
                        if self.authors is not None else None),
            "content_type":
            self.content_type,
            "name":
            self.name.json_ready if self.name is not None else None,
            "alternate_names":
            ([name.json_ready for name in self.alternate_names]
             if self.alternate_names is not None else None),
            "text":
            self.text.json_ready if self.text is not None else None,
            "keywords":
            self.keywords,
            "citations":
            self.citations,
            "notification":
            self.notification,
            "stored":
            self.stored,
        }
Exemplo n.º 4
0
 def load_info(self):
     try:
         user_id, self.user_name, self.email = orm.StorageHandler().call(
             select.get_user_info, user_id=self.user_id)
     except:
         raise
Exemplo n.º 5
0
class AuthorVote:
    """
    Attributes:
        edit_id: Integer. Defaults to None.
        vote_status: String, expects 'in-progress' or 'ended'.
            Defaults to None.
        vote: String, expects 'Y' or 'N'. Defaults to None.
        timestamp: Datetime. Defaults to None.
        close_timestamp: Datetime. Defaults to None.
        author: UserData object. Defaults to None.

    Properties:
        json_ready: Dictionary.

    Instance Methods:
        check_vote_order, save

    Class Methods:
        unpack_vote_summary, _retrieve_from_storage,
        _retrieve_from_redis, bulk_retrieve, get_vote_summary
    """

    storage_handler = orm.StorageHandler()

    edit_id = None          # Integer.
    vote_status = None      # String, expects 'in-progress' or 'ended'.
    vote = None             # String, expects 'Y' or 'N'.
    timestamp = None        # Datetime.
    close_timestamp = None  # Datetime.
    author = None           # UserData object.

    def __init__(self, vote_status, edit_id, vote, voter_id,
                 timestamp=None, close_timestamp=None):
        args = locals()
        if (not edit_id or (vote != "Y" and vote != "N") or not voter_id or
                (vote_status is not None and vote_status != "in-progress"
                and vote_status != "ended")):
            raise InputError("Invalid argument(s) provided.",
                             message="Invalid data provided.",
                             inputs=args)
        else:
            self.edit_id = edit_id
            self.vote_status = vote_status
            self.vote = vote
            self.timestamp = (datetime.utcnow() if timestamp is None
                              else timestamp)
            self.close_timestamp = close_timestamp
            self.author = UserData(user_id=voter_id)

    @classmethod
    def get_vote_summary(cls, votes):
        close_timestamp = votes[0].close_timestamp
        vote_summary = (str(close_timestamp) + " "
                        if close_timestamp is not None else "")
        for vote in votes:
            vote_summary += ("<" + vote.vote + ", " + str(vote.author.user_id) +
                             ", " + str(vote.timestamp) + ">, ")
        return vote_summary[:-2]

    @classmethod
    def unpack_vote_summary(cls, vote_summary):
        if vote_summary[0] != "<":
            cutoff_index = vote_summary.find("<")   # Should not be -1
            vote_summary = vote_summary[cutoff_index:]
        vote_lists = [
            vote_string[:-3].split(", ")
            if vote_string.endswith(">, ") else vote_string[:-1].split(", ")
            for vote_string in vote_summary.split("<")
        ]
        vote_dict = {
            int(vote_list[1]): vote_list[0] + str("; ") + vote_list[2]
            for vote_list in vote_lists[1:]
        }
        return vote_dict

    @classmethod
    def _retrieve_from_storage(cls, edit_id=None, vote_id=None,
                               validation_status=None):
        if edit_id is not None:
            try:
                if validation_status == "accepted":
                    vote_object = cls.storage_handler.call(
                        select.get_accepted_votes, edit_id=edit_id)
                elif validation_status == "rejected":
                    vote_object = cls.storage_handler.call(
                        select.get_rejected_votes, edit_id=edit_id)
                else:
                    raise InputError("Invalid argument(s) provided.",
                        message="Invalid data provided.",
                        inputs={"validation_status": validation_status})
            except:
                raise
        elif vote_id is not None:
            try:
                if validation_status == "accepted":
                    vote_object = cls.storage_handler.call(
                        select.get_accepted_votes, vote_id=vote_id)
                elif validation_status == "rejected":
                    vote_object = cls.storage_handler.call(
                        select.get_rejected_votes, vote_id=vote_id)
                else:
                    raise InputError("Invalid argument(s) provided.",
                        message="Invalid data provided.",
                        inputs={"validation_status": validation_status})
            except:
                raise

        return vote_object

    @classmethod
    def _retrieve_from_redis(cls, edit_id):
        try:
            vote_dict = redis_api.get_validation_data(edit_id)["votes"]
        except:
            raise
        else:
            return vote_dict

    @classmethod
    def bulk_retrieve(cls, vote_status, edit_id=None, vote_id=None,
                      content_id=None, validation_status=None):
        """
        Args:
            vote_status: String, expects 'in-progress' or 'ended'.
            edit_id: Integer. Defaults to None.
            vote_id: Integer. Defaults to None.
            content_id: Integer. Defaults to None.
            validation_status: String, expects 'validating', 'accepted',
                or 'rejected'. Defaults to None.
        Returns:
            List of AuthorVotes or dictionary of the form
            {edit_id1: list1 of AuthorVotes,
             edit_id2: list2 of AuthorVotes,
             ...}
        """
        args = locals()
        if vote_status == "in-progress":
            if edit_id is not None:
                vote_dict = cls._retrieve_from_redis(edit_id)
                votes = [AuthorVote(vote_status, edit_id, value[0],
                                    key, timestamp=dateparse.parse(value[3:]))
                         for key, value in vote_dict.items()]
            elif content_id is not None:
                try:
                    vote_dicts = redis_api.get_votes(content_id)
                except:
                    raise
                else:
                    votes = {edit_id: [AuthorVote(vote_status, edit_id, value[0],
                              key, timestamp=dateparse.parse(value[3:]))
                              for key, value in vote_dict.items()]
                             for edit_id, vote_dict in vote_dicts.items()}
            else:
                raise InputError("Invalid argument(s) provided.",
                                 message="Invalid data provided.",
                                 inputs=args)
        elif vote_status == "ended":
            if edit_id is not None:
                vote_object = cls._retrieve_from_storage(
                    edit_id=edit_id, validation_status=validation_status)
            elif vote_id is not None:
                vote_object = cls._retrieve_from_storage(
                    vote_id=vote_id, validation_status=validation_status)
            elif content_id is not None:
                if validation_status == "accepted":
                    vote_objects = cls.storage_handler.call(
                        select.get_accepted_votes, content_id=content_id)
                    votes = {}
                    for vote_object in vote_objects:
                        edit_id = vote_object.accepted_edit_id
                        vote_dict = cls.unpack_vote_summary(vote_object.vote)
                        vote_list = [AuthorVote(vote_status, edit_id, value[0],
                                     key, timestamp=dateparse.parse(value[3:]),
                                     close_timestamp=vote_object.close_timestamp)
                                     for key, value in vote_dict.items()]
                        votes[edit_id] = vote_list
                elif validation_status == "rejected":
                    vote_objects = cls.storage_handler.call(
                        select.get_rejected_votes, content_id=content_id)
                    votes = {}
                    for vote_object in vote_objects:
                        edit_id = vote_object.rejected_edit_id
                        vote_dict = cls.unpack_vote_summary(vote_object.vote)
                        vote_list = [AuthorVote(vote_status, edit_id, value[0],
                                     key, timestamp=dateparse.parse(value[3:]),
                                     close_timestamp=vote_object.close_timestamp)
                                     for key, value in vote_dict.items()]
                        votes[edit_id] = vote_list
                else:
                    raise InputError("Invalid argument(s) provided.",
                        message="Invalid data provided.",
                        inputs={"validation_status": validation_status})
                return votes
            else:
                raise InputError("Invalid argument(s) provided.",
                                 message="Invalid data provided.",
                                 inputs=args)
            vote_dict = cls.unpack_vote_summary(vote_object.vote)
            if edit_id is None:
                if validation_status == "accepted":
                    edit_id = vote_object.accepted_edit_id
                elif validation_status == "rejected":
                    edit_id = vote_object.rejected_edit_id
                else:
                    raise InputError("Invalid argument(s) provided.",
                        message="Invalid data provided.",
                        inputs={"validation_status": validation_status})
            votes = [AuthorVote(vote_status, edit_id, value[0],
                                key, timestamp=dateparse.parse(value[3:]),
                                close_timestamp=vote_object.close_timestamp)
                     for key, value in vote_dict.items()]
        else:
            raise InputError("Invalid argument(s) provided.",
                             message="Invalid data provided.",
                             inputs={"vote_status": vote_status})

        return votes

    def check_vote_order(self):
        """
        Returns:
            Tuple of the form (bool, integer), where bool is True if
            and only if this vote is the earliest (chronologically)
            submitted validating edit and integer is the edit id of the
            earliest submitted validating edit.

        Note: Properly ordered vote boolean returned despite redundancy
            to keep logic within method.
        """
        from . import edit as edit_api  # Here to resolve circular imports
        edit = edit_api.Edit(edit_id=self.edit_id,
                             validation_status="validating")
        if (edit.content_part == "name" or
                edit.content_part == "alternate_name"):
            edit_ids = edit_api.Edit.bulk_retrieve(
                validation_status="validating", name_id=edit.part_id,
                page_num=0, ids_only=True)
        elif edit.content_part == "text":
            edit_ids = edit_api.Edit.bulk_retrieve(
                validation_status="validating", text_id=edit.part_id,
                page_num=0, ids_only=True)
        elif edit.content_part == "keyword":
            edit_ids = edit_api.Edit.bulk_retrieve(
                validation_status="validating", content_id=edit.content_id,
                keyword_id=edit.part_id, page_num=0, ids_only=True)
        elif edit.content_part == "content_type":
            edit_ids = edit_api.Edit.bulk_retrieve(
                validation_status="validating", content_id=edit.content_id,
                content_type_id=edit.part_id, page_num=0, ids_only=True)
        elif edit.content_part == "citation":
            edit_ids = edit_api.Edit.bulk_retrieve(
                validation_status="validating", content_id=edit.content_id,
                citation_id=edit.part_id, page_num=0, ids_only=True)
        else:
            raise ApplicationError(error_data=edit.json_ready,
                message="Invalid content part detected.")

        return edit_ids[-1] == self.edit_id, edit_ids[-1]

    def save(self):
        if self.vote_status != "in-progress":
            raise NotImplementedError
        valid_vote, edit_to_vote_on = self.check_vote_order()
        if not valid_vote:
            return edit_to_vote_on
        else:
            try:
                redis_api.store_vote(self.edit_id, self.author.user_id,
                                     self.vote + "; " + str(self.timestamp))
            except DuplicateVoteError:
                raise
            except MissingKeyError as e:
                raise VoteStatusError(exception=e, message=e.message)
            except:
                raise

    @classmethod
    def votes_needed(cls, user_id, content_ids=None):
        """
        Args:
            user_id: Integer.
            content_ids: List of integers. Defaults to None.

        Returns:
            Dictionary of the form

                {content_id1: list of edit ids of edits not yet voted on,
                 content_id2: list of edit ids of edits not yet voted on,
                 ...},

            where the each list is sorted in descending chronological
            order by submission time.
        """
        if content_ids is None:
            content_ids = Content.bulk_retrieve(
                user_id=user_id, ids_only=True)
        try:
            edit_ids = redis_api.get_edits(
                content_ids=content_ids, only_ids=True)
            edits_voted_on = redis_api.get_edits(voter_id=user_id, only_ids=True)
        except:
            raise
        else:
            return {content_id: [edit_id for edit_id in edit_ids[content_id]
                                 if edit_id not in edits_voted_on]
                    for content_id in edit_ids}

    @property
    def json_ready(self):
        return {
            "edit_id": self.edit_id,
            "vote_status": self.vote_status,
            "timestamp": str(self.timestamp),
            "close_timestamp": (str(self.close_timestamp)
                                if self.close_timestamp is not None
                                else None),
            self.author.user_id: self.vote,
        }
Exemplo n.º 6
0
class RegisteredUser:

    storage_handler = orm.StorageHandler()

    user_id = None  # Integer.
    user_type = None  # String, 'admin' or 'standard'.
    user_name = None  # String.
    email = None  # String.
    confirmed_timestamp = None  # Datetime.
    pass_hash = None  # String.
    pass_hash_type = None  # String.
    pass_salt = None  # String.
    remember_id = None  # Integer.
    remember_token_hash = None  # String.
    remember_hash_type = None  # String.
    timestamp = None  # Datetime.
    deleted_timestamp = None  # Datetime.

    def __init__(self,
                 user_id=None,
                 email=None,
                 password=None,
                 user_name=None,
                 remember_id=None,
                 remember_token=None,
                 remember_user=False):
        """
        Args:
            user_id: Integer. Defaults to None.
            email: String. Defaults to None.
            password: String. Defaults to None.
            user_name: String. Defaults to None.
            remember_id: Integer. Defaults to None.
            remember_token: String. Defaults to None.
            remember_user: Boolean. Defaults to False.
        """
        args = locals()
        if user_id is not None:
            try:
                user_object = self.storage_handler.call(select.get_user,
                                                        user_id=user_id)
            except:
                raise
            else:
                self._transfer(user_object)
        elif email is not None and password is not None and user_name is None:
            try:
                user_object = self.storage_handler.call(select.get_user,
                                                        email=email)
            except:
                raise
            else:
                if user_object is None:
                    raise AuthenticationError(
                        message="Incorrect email address or password.")
                else:
                    authenticated = pass_handler.verify(
                        password, user_object.pass_hash)
                    if authenticated:
                        self._transfer(user_object)
                        if remember_user:
                            self.remember_user()
                    else:
                        raise AuthenticationError(
                            message="Incorrect email address or password.")
        elif remember_id is not None and remember_token is not None:
            try:
                user_object = self.storage_handler.call(
                    select.get_user, remember_id=remember_id)
            except:
                raise
            else:
                if user_object is None:
                    raise RememberUserError(
                        message="Incorrect 'Remember Me' ID or token.")
                else:
                    authenticated = pass_handler.verify(
                        remember_token, user_object.remember_token_hash)
                    if authenticated:
                        self._transfer(user_object)
                    else:
                        raise RememberUserError(
                            message="Incorrect 'Remember Me' ID or token.")
        else:
            if not email or not password or not user_name:
                raise InputError("Invalid argument(s) provided.",
                                 message="Invalid data provided.",
                                 inputs=args)
            else:
                email = email.strip()
                password = password.strip()
                user_name = user_name.strip()
                RegisteredUser._check_legal(email, password, user_name)
                self.email = email
                self.pass_hash = pass_handler.encrypt(password)
                self.pass_hash_type = "sha512_crypt"
                self.user_type = "standard"
                self.user_name = user_name
                self.timestamp = datetime.utcnow()
                self.remember_id = SystemRandom().getrandbits(48)

    def __eq__(self, other):
        return (self.user_id == other.user_id or self.email == other.email
                or self.remember_id == other.remember_id)

    @staticmethod
    def _check_legal(email, password, user_name):
        if PASS_REGEX.fullmatch(password) is None:
            raise PasswordError(
                message="Please provide a password of "
                "between 8 and 160 characters with at least one letter "
                "and one number or special character.")
        if not validate_email(email):
            raise EmailAddressError(
                message="Please provide a valid email address.")
        if USER_NAME_REGEX.fullmatch(user_name) is None:
            raise UserNameError(message="Please provide a valid first "
                                "and last name, separated by a space.")

    def _transfer(self, stored_user_object):
        self.user_id = stored_user_object.user_id
        self.user_type = stored_user_object.user_type
        self.user_name = stored_user_object.user_name
        self.email = stored_user_object.email
        self.confirmed_timestamp = stored_user_object.confirmed_timestamp
        self.pass_hash = stored_user_object.pass_hash
        self.pass_hash_type = stored_user_object.pass_hash_type
        self.remember_id = stored_user_object.remember_id
        self.remember_token_hash = stored_user_object.remember_token_hash
        self.remember_hash_type = stored_user_object.remember_hash_type
        self.timestamp = stored_user_object.timestamp
        self.deleted_timestamp = stored_user_object.deleted_timestamp

    def register(self):
        self.store()
        self.send_welcome.apply_async()
        confirmation_id = self.initiate_confirm(self.timestamp)
        return confirmation_id

    def store(self):
        try:
            user_id = self.storage_handler.call(
                action.store_new_user, self.user_type, self.user_name,
                self.email, self.pass_hash, self.pass_hash_type,
                self.pass_salt, self.remember_id, self.timestamp)
        except:
            raise
        else:
            self.user_id = user_id

    @celery_app.task(name="user.send_welcome", filter=task_method)
    def send_welcome(self):
        email = Email(self.email, self.user_name, welcome=True)
        try:
            send_email(email.message, self.email)
        except:
            raise

    def initiate_confirm(self, timestamp=None):
        if timestamp is None:
            timestamp = datetime.utcnow()
        expire_timestamp = timestamp + timedelta(days=3)
        confirmation_id = generate_password(size=60)
        confirmation_id_hash = pass_handler.encrypt(confirmation_id)
        redis_api.store_confirm(self.email, confirmation_id_hash,
                                expire_timestamp)
        self.request_confirm.apply_async(args=[confirmation_id, 3])
        self.request_confirm.apply_async(args=[confirmation_id, 1],
                                         eta=timestamp + timedelta(days=2))
        self.expire_confirm.apply_async(args=[confirmation_id_hash],
                                        eta=expire_timestamp)
        return confirmation_id

    @celery_app.task(name="user.request_confirm", filter=task_method)
    def request_confirm(self, confirmation_id, days_until_expiration):
        email = Email(self.email,
                      self.user_name,
                      days_remaining=days_until_expiration,
                      confirmation_id=confirmation_id)
        try:
            send_email(email.message, self.email)
        except:
            raise

    @celery_app.task(name="user.expire_confirm", filter=task_method)
    def expire_confirm(self, confirmation_id_hash):
        try:
            confirmation_dict = redis_api.get_confirm_info(self.email)
        except:
            raise
        else:
            if confirmation_dict and (max(confirmation_dict.items(),
                                          key=lambda item: item[1])[0]
                                      == confirmation_id_hash):
                redis_api.expire_confirm(self.email)
                RegisteredUser.delete(self.user_id)

    @classmethod
    def process_confirm(cls, email, confirmation_id):
        try:
            confirmation_dict = redis_api.get_confirm_info(email)
            user_id = cls.storage_handler.call(select.get_user,
                                               email=email).user_id
        except:
            raise
        else:
            for confirmation_id_hash in confirmation_dict:
                if pass_handler.verify(confirmation_id, confirmation_id_hash):
                    confirmed_timestamp = datetime.utcnow()
                    cls.update(user_id,
                               confirmed_timestamp=confirmed_timestamp)
                    break
            else:
                raise ConfirmationError(message="Incorrect confirmation code.")

    def remember_user(self):
        remember_token = generate_password(size=40)
        self.remember_token_hash = pass_handler.encrypt(remember_token)
        try:
            self.storage_handler.call(
                action.update_user,
                self.user_id,
                new_remember_token_hash=self.remember_token_hash,
                new_remember_hash_type="sha512_crypt")
        except:
            raise
        else:
            self.remember_token = remember_token

    @classmethod
    def update(cls,
               user_id,
               new_user_name=None,
               new_email=None,
               new_password=None,
               confirmed_timestamp=None):
        """
        Args:
            user_id: Integer.
            new_user_name: String. Defaults to None.
            new_email: String. Defaults to None.
            new_password: String. Defaults to None.
            confirmed_timestamp: Datetime. Defaults to None.
        """
        args = locals()
        if new_user_name is not None:
            new_user_name = new_user_name.strip()
            try:
                cls.storage_handler.call(action.update_user,
                                         user_id,
                                         new_user_name=new_user_name)
            except:
                raise
        elif new_email is not None:
            new_email = new_email.strip()
            try:
                cls.storage_handler.call(action.update_user,
                                         user_id,
                                         new_email=new_email)
            except:
                raise
        elif new_password is not None:
            new_password = new_password.strip()
            new_pass_hash = pass_handler.encrypt(new_password)
            new_pass_hash_type = "sha512_crypt"
            new_pass_salt = ""
            try:
                cls.storage_handler.call(action.update_user,
                                         user_id,
                                         new_pass_hash=new_pass_hash,
                                         new_pass_hash_type=new_pass_hash_type,
                                         new_pass_salt=new_pass_salt)
            except:
                raise
        elif confirmed_timestamp is not None:
            try:
                cls.storage_handler.call(
                    action.update_user,
                    user_id,
                    confirmed_timestamp=confirmed_timestamp)
            except:
                raise
        else:
            raise InputError("No arguments provided.",
                             message="No data provided.",
                             inputs=args)

    @classmethod
    def delete(cls, user_id):
        deleted_timestamp = datetime.utcnow()
        try:
            cls.storage_handler.call(action.delete_user, user_id,
                                     deleted_timestamp)
        except:
            raise

    @property
    def json_ready(self):
        return {
            "user_id": self.user_id,
            "user_name": self.user_name,
            "user_type": self.user_type,
            "email": self.email,
            "timestamp": self.timestamp,
            "deleted_timestamp": self.deleted_timestamp,
        }
Exemplo n.º 7
0
class Report:

    storage_handler = orm.StorageHandler()

    report_id = None  # Integer.
    content_id = None  # Integer.
    report_text = None  # String.
    report_type = None  # String, accepts 'content' or 'authors'.
    author_type = None  # String, accepts 'U' or IP address.
    author_id = None  # Integer.
    admin_report = None  # String.
    admin_id = None  # Integer.
    report_status = None  # String, accepts 'pending', 'open', 'resolved'.
    timestamp = None  # Datetime.
    res_timestamp = None  # Datetime.

    def __init__(self,
                 report_id=None,
                 report_status=None,
                 content_id=None,
                 report_text=None,
                 report_type=None,
                 author_type=None,
                 author_id=None,
                 report_object=None):
        """
        Args:
            report_id: Integer. Defaults to None.
            report_status: String, accepts 'open' or 'resolved'. 
                Defaults to None.
            content_id: Integer. Defaults to None.
            report_text: String. Defaults to None.
            report_type: String, accepts 'content' or 'authors'. 
                Defaults to None.
            author_type: String, accepts 'U' or IP address. 
                Defaults to None.
            author_id: Integer. Defaults to None.
            report_object: orm.UserReport object.  Defaults to None.
        """
        args = locals()
        self.report_status = report_status
        if report_id is not None:
            try:
                if report_status == "open":
                    report_object = redis_api.get_reports(report_id)
                elif report_status == "resolved":
                    report_object = self.storage_handler.call(
                        select.get_user_reports, report_id=report_id)
                else:
                    raise InputError("Invalid argument(s) provided.",
                                     message="Invalid data provided.",
                                     inputs={"report_status": report_status})
            except:
                raise
            else:
                self._transfer(report_object)
        elif report_object is not None:
            self._transfer(report_object)
        else:
            if not content_id or not report_text or not report_type or (
                    author_type is not None and not is_ip_address(author_type)
                    and author_type != "U"
            ) or (author_type == "U"
                  and author_id is None) or (report_type != "content"
                                             and report_type != "authors"):
                raise InputError("Invalid argument(s) provided.",
                                 message="Invalid data provided.",
                                 inputs=args)
            else:
                self.report_status = "pending"
                self.report_text = report_text.strip()
                self.content_id = content_id
                self.report_type = report_type
                self.author_type = author_type
                self.author_id = author_id
                self.timestamp = datetime.utcnow()

    def __eq__(self, other):
        return self.report_id == other.report_id or (
            self.content_id == other.content_id and self.report_text
            == other.report_text and self.report_type == other.report_type
            and self.author_type == other.author_type and self.author_id
            == other.author_id and self.admin_report == other.admin_report
            and self.admin_id == other.admin_id and self.report_status
            == other.report_status and self.timestamp == other.timestamp)

    @staticmethod
    def _check_legal():
        pass

    def _transfer(self, report_object):
        if self.report_status == "open":
            self.report_id = int(report_object.get("report_id"))
            self.content_id = int(report_object.get("content_id"))
            self.report_text = report_object.get("report_text")
            self.report_type = report_object.get("report_type")
            self.admin_id = int(report_object.get("admin_id"))
            self.author_type = report_object.get("author_type")
            self.author_id = report_object.get("author_id")
            if self.author_id:
                self.author_id = int(self.author_id)
            self.timestamp = report_object.get("timestamp")
        elif self.report_status == "resolved":
            self.report_id = report_object.report_id
            self.report_text = report_object.report_text
            self.report_type = report_object.report_type
            self.author_type = report_object.author_type
            self.admin_report = report_object.admin_report
            self.timestamp = report_object.timestamp
            self.res_timestamp = report_object.res_timestamp
            self.author_id = report_object.author_id
            self.admin_id = report_object.admin_id
            self.content_id = report_object.content_id

    @classmethod
    def bulk_retrieve(cls,
                      user_id=None,
                      admin_id=None,
                      content_id=None,
                      ip_address=None,
                      report_status="open",
                      page_num=1):
        """
        Args:
            user_id: Integer. Defaults to None.
            admin_id: Integer. Defaults to None.
            content_id: Integer. Defaults to None.
            ip_address: String. Defaults to None.
            report_status: String, accepts 'open' or 'resolved'. 
                Defaults to 'open'.
            page_num: Integer. Defaults to 1.
        Returns:
            List of Report objects.
        """
        args = locals()
        if user_id is not None:
            try:
                if report_status == "open":
                    report_objects = redis_api.get_reports(user_id=user_id)
                elif report_status == "resolved":
                    report_objects = cls.storage_handler.call(
                        select.get_user_reports, user_id=user_id)
            except:
                raise
        elif admin_id is not None:
            try:
                if report_status == "open":
                    report_objects = redis_api.get_reports(admin_id=admin_id)
                elif report_status == "resolved":
                    report_objects = cls.storage_handler.call(
                        select.get_user_reports, admin_id=admin_id)
            except:
                raise
        elif content_id is not None:
            try:
                if report_status == "open":
                    report_objects = redis_api.get_reports(
                        content_id=content_id)
                elif report_status == "resolved":
                    report_objects = cls.storage_handler.call(
                        select.get_user_reports, content_id=content_id)
            except:
                raise
        elif ip_address is not None:
            try:
                if report_status == "open":
                    raise NotImplementedError
                elif report_status == "resolved":
                    report_objects = cls.storage_handler.call(
                        select.get_user_reports, ip_address=ip_address)
            except:
                raise
        else:
            raise InputError("Invalid argument(s) provided.",
                             message="Insufficient data provided.",
                             inputs=args)
        if page_num != 0:
            reports = [
                Report(report_status=report_status,
                       report_object=report_object)
                for report_object in report_objects[10 * (page_num - 1):10 *
                                                    page_num]
            ]
        else:
            reports = [
                Report(report_status=report_status,
                       report_object=report_object)
                for report_object in report_objects
            ]

        return reports

    def submit(self):
        self.assign_admin()
        self.save()

    def assign_admin(self):
        try:
            admin_ids = self.storage_handler.call(select.get_admin_ids)
            admin_assignments = redis_api.get_admin_assignments(admin_ids)
        except:
            raise
        else:
            assignment_counts = {}
            for admin_id in admin_assignments:
                reports_assigned = len(admin_assignments[admin_id])
                admin_ids = assignment_counts.get(reports_assigned, [])
                admin_ids.append(admin_id)
                assignment_counts[reports_assigned] = admin_ids
            counts = list(assignment_counts.keys())
            counts.sort()
            self.admin_id = choice(assignment_counts[counts[0]])

    def resolve(self, admin_report):
        self.admin_report = admin_report
        self.res_timestamp = datetime.utcnow()
        self.store()
        self.report_status = "resolved"

    def save(self):
        try:
            report_id = redis_api.store_report(self.content_id,
                                               self.report_text,
                                               self.report_type, self.admin_id,
                                               self.timestamp,
                                               self.author_type,
                                               self.author_id)
        except:
            raise
        else:
            self.report_id = report_id
            self.report_status = "open"

    def store(self):
        try:
            report_id = self.storage_handler.call(action.store_user_report,
                                                  self.content_id,
                                                  self.report_text,
                                                  self.report_type,
                                                  self.admin_report,
                                                  self.timestamp,
                                                  self.res_timestamp,
                                                  self.admin_id,
                                                  self.author_type,
                                                  user_id=self.author_id)
            redis_api.delete_report(self.report_id)
        except:
            raise
        else:
            self.report_id = report_id

    @property
    def json_ready(self):
        return {
            "report_id": self.report_id,
            "content_id": self.content_id,
            "report_text": self.report_text,
            "report_type": self.report_type,
            "author_type": self.author_type,
            "author_id": self.author_id,
            "admin_report": self.admin_report,
            "admin_id": self.admin_id,
            "report_status": self.report_status,
            "timestamp": self.timestamp,
            "res_timestamp": self.res_timestamp,
        }