Esempio n. 1
0
 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()
Esempio n. 2
0
    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
Esempio n. 3
0
def delete_validation_data(content_id, edit_id, user_id, part_id,
                           content_part):
    """
    Args:
        content_id: Integer.
        edit_id: Integer.
        user_id: Integer.
        part_id: Integer.
        content_part: String, expects 'text', 'citation', 'keyword',
            'name', 'alternate_name', or 'content_type'.
    """
    voters = get_validation_data(edit_id)["votes"]
    with redis.pipeline() as pipe:
        pipe.lrem("user:"******"voter:" + str(user_id), 0, edit_id)
        if content_part == "text":
            pipe.lrem("text:" + str(part_id), 0, edit_id)
        elif content_part == "citation":
            pipe.lrem("citation:" + str(part_id), 0, edit_id)
        elif content_part == "name" or content_part == "alternate_name":
            pipe.lrem("name:" + str(part_id), 0, edit_id)
        elif content_part == "keyword":
            pipe.lrem("keyword:" + str(part_id), 0, edit_id)
        elif content_part == "content_type":
            pipe.lrem("content_type:" + str(part_id), 0, edit_id)
        else:
            raise InputError("Invalid argument(s) provided.",
                             message="Invalid data provided.",
                             inputs={"content_part": content_part})
        pipe.lrem("content:" + str(content_id), 0, edit_id)
        pipe.delete("edit:" + str(edit_id))
        pipe.delete("votes:" + str(edit_id))
        for voter_id in voters:
            pipe.lrem("voter:" + str(voter_id), 0, edit_id)
        pipe.execute()
Esempio n. 4
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
        ]
Esempio n. 5
0
def add_to_content_piece(content_id, content_part, part_string):
    """
    Args:
        content_id: Integer.
        content_part: String, accepts 'alternate_name', 'keyword',
            or 'citation'.
        part_string: String.
    """
    try:
        content_piece = SearchableContentPiece.get(id=content_id)
    except NotFoundError as e:
        raise IndexAccessError(str(e))
    else:
        if content_part == "alternate_name":
            content_piece.alternate_names.append(part_string)
            content_piece.alternate_names_suggest["input"].append(part_string)
        elif content_part == "keyword":
            content_piece.keywords.append(part_string)
            content_piece.keywords_suggest["input"].append(part_string)
        elif content_part == "citation":
            content_piece.citations.append(part_string)
            content_piece.citations_suggest["input"].append(part_string)
        else:
            raise InputError("Invalid argument(s) provided.",
                             message="Invalid data provided.",
                             inputs={"content_part": content_part})
        content_piece.save()
Esempio n. 6
0
 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)
Esempio n. 7
0
def filter_by(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 1.

    Returns:
        A dictionary of the form

        {"count": int, "results": result_list},

        where "count" holds a count of the number of results returned by
        the search and result_list is a list, sorted in descending order
        by query matching score, containing dict elements of the form:

        {
            "score": float,
            "content_id": int,
            "name": string,
            "alternate_names": list of strings,
            "text_fragment": 200 character string fragment,
        }
    """
    search = SearchableContentPiece.search()
    search = search[10 * (page_num - 1):10 * page_num]
    if content_part == "keyword":
        query = Q("bool", filter=[Q("term", keywords=part_string)])
    elif content_part == "content_type":
        query = Q("bool", filter=[Q("term", content_type=part_string)])
    elif content_part == "citation":
        query = Q("bool", filter=[Q("term", citations=part_string)])
    elif content_part == "name":
        query = (Q("bool", filter=[Q("term", name=part_string)])
                 | Q("bool", filter=[Q("term", alternate_names=part_string)]))
    else:
        raise InputError("Invalid argument(s) provided.",
                         message="Invalid data provided.",
                         inputs={"content_part": content_part})
    search = search.query(query)
    response = search.execute()
    results = {"count": response.hits.total, "results": []}
    for hit in response:
        body = {
            "score": hit.meta.score,
            "content_id": int(hit.meta.id),
            "name": hit.name,
            "alternate_names": hit.alternate_names,
            "text_fragment": hit.text[:201],
        }
        results["results"].append(body)

    return results
Esempio n. 8
0
 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})
Esempio n. 9
0
def update_content_piece(content_id,
                         content_part,
                         part_string=None,
                         part_strings=None):
    """
    Args:
        content_id: Integer.
        content_part: String, accepts 'name', 'alternate_name', 'text',
            'content_type', 'keyword', or 'citation'.
        part_string: String.
        part_strings: List of strings.
    """
    try:
        content_piece = SearchableContentPiece.get(id=content_id)
    except NotFoundError as e:
        raise IndexAccessError(str(e))
    else:
        if content_part == "name":
            content_piece.name = part_string
            content_piece.name_suggest = {
                "input": part_string,
                "payload": {
                    "content_id": content_id
                }
            }
        elif content_part == "alternate_name":
            content_piece.alternate_names = part_strings
            content_piece.alternate_names_suggest = {
                "input": part_strings,
                "payload": {
                    "content_id": content_id
                }
            }
        elif content_part == "text":
            content_piece.text = part_string
        elif content_part == "content_type":
            content_piece.content_type = part_string
        elif content_part == "keyword":
            content_piece.keywords = part_strings
            content_piece.keywords_suggest = {"input": part_strings}
        elif content_part == "citation":
            content_piece.citations = part_strings
            content_piece.citations_suggest = {"input": part_strings}
        else:
            raise InputError("Invalid argument(s) provided.",
                             message="Invalid data provided.",
                             inputs={"content_part": content_part})
        content_piece.save()
Esempio n. 10
0
 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)
Esempio n. 11
0
def get_reports(report_id=None, content_id=None, user_id=None, admin_id=None):
    """
    Args:
        report_id: Integer. Defaults to None.
        content_id: Integer. Defaults to None.
        user_id: Integer. Defaults to None.
        admin_id: Integer. Defaults to None.
    Returns:
        Dictionary of the form
        {
            "report_id": int,
            "content_id": int,
            "report_text": string,
            "report_type": string,
            "admin_id": int,
            "timestamp": string,
            "author_type": string,
            "author_id": int
        }
    """
    args = locals()
    if report_id is not None:
        report_dict = redis.hgetall("report:" + str(report_id))
        return report_dict
    elif content_id is not None:
        report_ids = redis.lrange("content_reports:" + str(content_id), 0, -1)
    elif user_id is not None:
        report_ids = redis.lrange("user_reports:" + str(user_id), 0, -1)
    elif admin_id is not None:
        report_ids = redis.lrange("admin_reports:" + str(admin_id), 0, -1)
    else:
        raise InputError("No arguments provided.",
                         message="No data provided.",
                         inputs=args)
    with redis.pipeline() as pipe:
        for report_id in report_ids:
            pipe.hgetall("report:" + str(report_id))
        report_dicts = pipe.execute()

    return report_dicts
Esempio n. 12
0
 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
Esempio n. 13
0
def restore(diff, version="original"):
    """
    Args:
        version: String, accepts 'original' and 'edit'.
            Defaults to 'original'.
    Returns:
        Non-diffed part text string, either the original or edited version.
    """
    if version == "original":
        part_text = ""
        for line in diff.splitlines():
            if line.startswith("-") or line.startswith(" "):
                part_text += line[6:]
    elif version == "edit":
        part_text = ""
        for line in diff.splitlines():
            if line.startswith("+") or line.startswith(" "):
                part_text += line[6:]
    else:
        raise InputError("Invalid argument(s) provided.",
                         message="Invalid data provided.",
                         inputs={"version": version})

    return part_text
Esempio n. 14
0
    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
Esempio n. 15
0
def autocomplete(content_part, query_string):
    """
    Args:
        content_part: String, accepts 'name', 'keyword', or 'citation'.
        query_string: String.

    Returns:
        A list. If content_part == 'name', contains dict
        elements of the form:

        {"completion": completed string, "content_id": int}

        Otherwise, contains dict elements of the form:

        {"completion": completed string}
    """
    # Setup autocomplete search.
    autocomplete_search = SearchableContentPiece.search()
    if content_part == "name":
        autocomplete_search = autocomplete_search.suggest(
            "suggestions",
            query_string,
            completion={
                "field": "name_suggest",
                "fuzzy": True,
                "size": 10
            }).suggest("alt_suggestions",
                       query_string,
                       completion={
                           "field": "alternate_names_suggest",
                           "fuzzy": True,
                           "size": 10
                       })
    elif content_part == "keyword":
        autocomplete_search = autocomplete_search.suggest(
            "suggestions",
            query_string,
            completion={
                "field": "keywords_suggest",
                "fuzzy": True,
                "size": 10
            })
    elif content_part == "citation":
        autocomplete_search = autocomplete_search.suggest(
            "suggestions",
            query_string,
            completion={
                "field": "citations_suggest",
                "fuzzy": True,
                "size": 10
            })
    else:
        raise InputError("Invalid argument(s) provided.",
                         message="Invalid data provided.",
                         inputs={"content_part": content_part})

    # Execute the search and reformat the result.
    response = autocomplete_search.execute()
    completions = []
    if response:
        if len(response.suggest.suggestions) > 1:
            raise NotImplementedError
        if content_part == "name":
            for result in response.suggest.suggestions:
                for suggestion in result.options:
                    completions.append({
                        "completion":
                        suggestion.text,
                        "content_id":
                        int(suggestion.payload.content_id)
                    })
        elif content_part == "keyword" or content_part == "citation":
            for result in response.suggest.suggestions:
                for suggestion in result.options:
                    completions.append({"completion": suggestion.text})

    return completions
Esempio n. 16
0
 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)
Esempio n. 17
0
def store_edit(content_id,
               edit_text,
               edit_rationale,
               content_part,
               part_id,
               timestamp,
               start_timestamp,
               author_type,
               user_id=None):
    """
    Args:
        content_id: Integer.
        edit_text: String.
        edit_rationale: String.
        original_part_text: String.
        content_part: String, expects 'text', 'name', 'alternate_name',
            'keyword', 'content_type', or 'citation'.
        part_id: Integer.
        timestamp: Datetime.
        start_timestamp: Datetime.
        author_type: String, expects 'U' or an IP address.
        user_id: Integer.
    Returns:
        An integer, the id of the edit in Redis.
    """
    with redis.pipeline() as pipe:
        # Get a unique edit id and increment it for the next edit
        while True:
            try:
                pipe.watch("next_edit_id")
                edit_id = int(pipe.get("next_edit_id"))
                next_edit_id = edit_id + 1
                pipe.multi()
                pipe.set("next_edit_id", next_edit_id).execute()
            except WatchError:
                continue
            except:
                raise
            else:
                break

        # Now store the edit with the edit id
        if user_id is not None:
            pipe.lpush("user:"******"citation":
            pipe.lpush("citation:" + str(part_id), edit_id)
        elif content_part == "text":
            pipe.lpush("text:" + str(part_id), edit_id)
        elif content_part == "name" or content_part == "alternate_name":
            pipe.lpush("name:" + str(part_id), edit_id)
        elif content_part == "keyword":
            pipe.lpush("keyword:" + str(part_id), edit_id)
        elif content_part == "content_type":
            pipe.lpush("content_type:" + str(part_id), edit_id)
        else:
            raise InputError("Invalid argument(s) provided.",
                             message="Invalid data provided.",
                             inputs={"content_part": content_part})
        pipe.lpush("content:" + str(content_id), edit_id)
        pipe.hmset(
            "edit:" + str(edit_id), {
                "edit_id": edit_id,
                "content_id": content_id,
                "edit_text": edit_text,
                "edit_rationale": edit_rationale if edit_rationale else "",
                "content_part": content_part,
                "part_id": part_id,
                "timestamp": timestamp,
                "start_timestamp": start_timestamp,
                "author_type": author_type,
            })
        if user_id is not None:
            pipe.hset("edit:" + str(edit_id), "user_id", user_id)
        pipe.execute()

    return edit_id
Esempio n. 18
0
 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
Esempio n. 19
0
 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
Esempio n. 20
0
def get_edits(content_id=None,
              content_ids=None,
              user_id=None,
              voter_id=None,
              text_id=None,
              citation_id=None,
              keyword_id=None,
              name_id=None,
              content_type_id=None,
              only_ids=False):
    """
    Args:
        content_id: Integer. Defaults to None.
        user_id: Integer. Defaults to None.
        voter_id: Integer. Defaults to None.
        text_id: Integer. Defaults to None.
        citation_id: Integer. Defaults to None.
        keyword_id: Integer. Defaults to None.
        user_id: Integer. Defaults to None.
        content_type_id: Integer. Defaults to None.
        only_ids: Boolean. Defaults to False. Determines whether to
            return edits or only edit ids.
    Returns:
        If only_ids is False and content_ids is None:
            Dictionary of the form
            {edit_id1: edit_dict1, edit_id2: edit_dict2, ...}.
        If only_ids is True and content_ids is None:
            List of integers.
        If only_ids is True and content_ids is not None:
            Dictionary of the form
            {content_id1: List of edit IDs,
             content_id2: List of edit IDS, ...}
    """
    args = locals()
    if content_id is not None:
        edit_ids = redis.lrange("content:" + str(content_id), 0, -1)
    elif content_ids is not None:
        with redis.pipeline() as pipe:
            for content_id in content_ids:
                pipe.lrange("content:" + str(content_id), 0, -1)
            edit_id_lists = pipe.execute()
            if only_ids:
                return {
                    content_id: edit_ids
                    for content_id, edit_ids in zip(content_ids, edit_id_lists)
                }
            else:
                raise InputError("Invalid argument(s) provided.",
                                 message="Invalid data provided.",
                                 inputs=args)
    elif user_id is not None:
        edit_ids = redis.lrange("user:"******"voter:" + str(voter_id), 0, -1)
    elif text_id is not None:
        edit_ids = redis.lrange("text:" + str(text_id), 0, -1)
    elif citation_id is not None:
        edit_ids = redis.lrange("citation:" + str(citation_id), 0, -1)
    elif keyword_id is not None:
        edit_ids = redis.lrange("keyword:" + str(keyword_id), 0, -1)
    elif name_id is not None:
        edit_ids = redis.lrange("name:" + str(name_id), 0, -1)
    elif content_type_id is not None:
        edit_ids = redis.lrange("content_type:" + str(content_type_id), 0, -1)
    else:
        raise InputError("No arguments provided.",
                         message="No data provided.",
                         inputs=args)
    if only_ids:
        return edit_ids
    else:
        with redis.pipeline() as pipe:
            for edit_id in edit_ids:
                pipe.hgetall("edit:" + str(edit_id))
            edits = pipe.execute()

        return {edit_id: edit for edit_id, edit in zip(edit_ids, edits)}
Esempio n. 21
0
 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)
Esempio n. 22
0
def _compute_combined_diff(first_diff, later_diff, base="common"):
    """
    Args:
        first_diff: String, specifically expects an edit diff.
        later_diff: String, specifically expects an edit diff that was
            accepted later than first_diff and has the same original text
            as first_diff.
        base: String, accepts 'common' and 'first_diff', the latter
            indicating that later_diff represents an edit of first_diff.
    Returns:
        The merged diff string with both diffs applied.
    """
    # Get splits of the original version of the first diff
    # at all edit points.
    first_diff_lines = first_diff.splitlines()
    first_diff_splits = []
    partial_original = ""
    reverse_partial_original = restore(first_diff, version="original")
    for i, line in enumerate(first_diff_lines):
        first_diff_splits.append((partial_original, reverse_partial_original))
        if ((base == "common" and not line.startswith("+"))
                or (base == "first_diff" and not line.startswith("-"))):
            partial_original += line[6:]
            reverse_partial_original = reverse_partial_original[len(line[6:]):]

    # Get splits of the original version of the later diff
    # at the insertion points and indices at the deletion points.
    later_diff_lines = later_diff.splitlines()
    later_diff_insertion_splits, later_diff_insertions = [], []
    partial_original = ""
    reverse_partial_original = restore(later_diff, version="original")
    original_text_index_offset = 0
    later_diff_index_dict = {}
    for i, line in enumerate(later_diff_lines):
        if line.startswith("-"):
            later_diff_index_dict.update({
                i:
                [original_text_index_offset + j for j in range(6, len(line))]
            })
        if line.startswith("+"):
            later_diff_insertion_splits.append(
                (partial_original, reverse_partial_original))
            later_diff_insertions.append(line)
        else:
            partial_original += line[6:]
            reverse_partial_original = reverse_partial_original[len(line) - 6:]
            original_text_index_offset += len(line) - 6

    # Merge insertions of the later diff into the first diff
    merged_diff_lines = first_diff_lines
    index_offset = 0
    for (partial1, partial2), line in zip(later_diff_insertion_splits,
                                          later_diff_insertions):
        if line.startswith("+") and base == "first_diff":
            line = "+" + line  # To distinguish later_diff insertions
        match_index, min_offset = min(enumerate(
            len(partial1) - len(first_diff_p)
            for first_diff_p, first_diff_pr in first_diff_splits
            if first_diff_p in partial1),
                                      key=lambda pair: pair[1])
        if min_offset == 0:
            merged_diff_lines.insert(match_index + index_offset, line)
            index_offset += 1
        elif min_offset == len(first_diff_lines[match_index]) - 6:
            merged_diff_lines.insert(match_index + 1 + index_offset, line)
            index_offset += 1
        elif 0 < min_offset < len(first_diff_lines[match_index]) - 6:
            if first_diff_lines[match_index].startswith("+"):
                if base == "common":
                    raise DiffComputationError(
                        "An error was encountered while merging two diffs.")
                elif base == "first_diff":
                    part1 = first_diff_lines[match_index][:min_offset]
                    if part1[-1] != " ":
                        part1 += " "
                    if first_diff_lines[match_index][min_offset:].startswith(
                            " "):
                        part2 = "+    " + first_diff_lines[match_index][
                            min_offset:]
                    else:
                        part2 = "+     " + first_diff_lines[match_index][
                            min_offset:]
                    del merged_diff_lines[match_index + index_offset]
                    merged_diff_lines.insert(match_index + index_offset, part1)
                    merged_diff_lines.insert(match_index + 1 + index_offset,
                                             line)
                    merged_diff_lines.insert(match_index + 2 + index_offset,
                                             part2)
                    index_offset += 2
                else:
                    raise InputError("Invalid argument(s) provided.",
                                     message="Invalid data provided.",
                                     inputs={"base": base})
            elif first_diff_lines[match_index].startswith("-"):
                if base == "first_diff":
                    raise DiffComputationError(
                        "An error was encountered while merging two diffs.")
                else:
                    merged_diff_lines.insert(match_index + 1 + index_offset,
                                             line)
                    index_offset += 1
            else:
                part1 = first_diff_lines[match_index][:min_offset]
                if part1[-1] != " ":
                    part1 += " "
                if first_diff_lines[match_index][min_offset:].startswith(" "):
                    part2 = "     " + first_diff_lines[match_index][min_offset:]
                else:
                    part2 = "      " + first_diff_lines[match_index][
                        min_offset:]
                del merged_diff_lines[match_index + index_offset]
                merged_diff_lines.insert(match_index + index_offset, part1)
                merged_diff_lines.insert(match_index + 1 + index_offset, line)
                merged_diff_lines.insert(match_index + 2 + index_offset, part2)
                index_offset += 2
        else:
            print("First diff:")
            print(first_diff)
            print("\nLater diff:")
            print(later_diff, "\n")
            print(later_diff.replace(" ", "X"))
            print(merged_diff_lines, "\n")
            print("First diff lines: ", first_diff_lines, "\n")
            print(later_diff_lines, "\n")
            print("First diff splits: ", first_diff_splits, "\n")
            print(len(first_diff_lines[match_index]), "\n")
            print("Later diff insertions: ", later_diff_insertions, "\n")
            print(later_diff_insertion_splits, "\n")
            print("X" + partial1 + "X", "\n")
            print(partial2, "\n")
            print(line, "\n")
            print(match_index, min_offset, "\n")
            raise DiffComputationError(
                "An error was encountered while merging two diffs.")

    # Merge deletions of the later diff into the first diff
    original_text_index_offset = 0
    merged_diff_index_dict = {}
    for i, line in enumerate(merged_diff_lines):
        if (not line.startswith("+")
                or (line.startswith("+ ") and base == "first_diff")):
            merged_diff_index_dict.update({
                original_text_index_offset + j: (i, j)
                for j in range(6, len(line))
            })
            original_text_index_offset += len(line) - 6
    index_offset = 0
    for i, line in enumerate(later_diff_lines):
        if line.startswith("-"):
            original_indices = later_diff_index_dict[i]
            pairs = [merged_diff_index_dict[j] for j in original_indices]
            first_line_index, start_subindex = min(pairs)
            last_line_index, finish_subindex = max(pairs)
            first_line_index += index_offset
            start_subindex += index_offset
            last_line_index += index_offset
            finish_subindex += index_offset
            for j in range(first_line_index, last_line_index + 1):
                merged_line = merged_diff_lines[j]
                if j == first_line_index:
                    if (merged_line.startswith(" ")
                            or (merged_line.startswith("+ ")
                                and base == "first_diff")):
                        same_part = merged_line[:start_subindex]
                        if not same_part.endswith(" "):
                            same_part += " "
                        deleted_part = merged_line[start_subindex:]
                        if deleted_part.startswith(" "):
                            deleted_part = "-    " + deleted_part
                        else:
                            deleted_part = "-     " + deleted_part
                        del merged_diff_lines[j]
                        merged_diff_lines.insert(j, same_part)
                        merged_diff_lines.insert(j + 1, deleted_part)
                elif j == last_line_index:
                    if (merged_line.startswith(" ")
                            or (merged_line.startswith("+ ")
                                and base == "first_diff")):
                        same_part = merged_line[finish_subindex + 1:]
                        if same_part.startswith(" "):
                            if base == "common":
                                same_part = "     " + same_part
                            else:
                                same_part = "+    " + same_part
                        else:
                            if base == "common":
                                same_part = "      " + same_part
                            else:
                                same_part = "+     " + same_part
                        deleted_part = merged_line[:finish_subindex + 1]
                        deleted_part = "-" + deleted_part[1:]
                        if not deleted_part.endswith(" "):
                            deleted_part += " "
                        del merged_diff_lines[j]
                        merged_diff_lines.insert(j, deleted_part)
                        merged_diff_lines.insert(j + 1, same_part)
                else:
                    if (merged_line.startswith(" ")
                            or (merged_line.startswith("+ ")
                                and base == "first_diff")):
                        merged_diff_lines[j] = "-" + merged_diff_lines[j][1:]

    # If applicable, recombine insertions.
    if base == "first_diff":
        previous_line = ""
        new_line = ""
        splintered_merged_diff_lines = merged_diff_lines
        merged_diff_lines = []
        for i, line in enumerate(splintered_merged_diff_lines):
            if previous_line.startswith("+") and line.startswith("+"):
                if line.startswith("++"):
                    new_line += line[7:]
                else:
                    new_line += line[6:]
                if i == len(splintered_merged_diff_lines) - 1:
                    merged_diff_lines.append(new_line)
            elif not previous_line.startswith("+") and line.startswith("+"):
                new_line = line
                if i == len(splintered_merged_diff_lines) - 1:
                    merged_diff_lines.append(new_line)
            else:
                merged_diff_lines.append(new_line)
                new_line = line

    merged_diff_lines = [line for line in merged_diff_lines if line != ""]
    return "\n".join(merged_diff_lines).replace("\n++     ", "\n+     ")
Esempio n. 23
0
    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