From 257b30afd69e7cc709fc1ec918810fd786afb890 Mon Sep 17 00:00:00 2001 From: Engine Bai Date: Sun, 26 Mar 2017 10:50:26 +0800 Subject: [PATCH 1/5] Add medium client sdk module and unittest for this module --- pymedium/medium.py | 44 ++++++++++++++++++++++++++++++++++++++++++++ tests/test_medium.py | 23 +++++++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 pymedium/medium.py create mode 100644 tests/test_medium.py diff --git a/pymedium/medium.py b/pymedium/medium.py new file mode 100644 index 0000000..ec538e7 --- /dev/null +++ b/pymedium/medium.py @@ -0,0 +1,44 @@ +#!/usr/bin/python3 +# -*- encoding: utf-8 -*- +import json + +import requests +from pymedium.parser import parse_user, parse_publication, parse_post, parse_post_detail +from pymedium.constant import ROOT_URL, HTML_PARSER, ACCEPT_HEADER, ESCAPE_CHARACTERS + + +class Medium(object): + def __init__(self): + pass + + def get_user_profile(self, username): + url = "{}@{}/latest".format(ROOT_URL, username) + return self._send_request(url, parse_user) + + def get_publication_profile(self, publication_name): + url = "{}{}/latest".format(ROOT_URL, publication_name) + return self._send_request(url, parse_publication) + + def get_user_posts(self, username): + pass + + def get_publication_posts(self, publication): + pass + + def get_top_posts(self): + pass + + def search_posts_by_tag(self, tag, sort): + pass + + def parse_post_content(self, url): + pass + + @staticmethod + def _send_request(url, parse_function): + req = requests.get(url, headers=ACCEPT_HEADER) + print(url, req.status_code) + if req.status_code == requests.codes.ok: + return parse_function(json.loads(req.text.replace(ESCAPE_CHARACTERS, "").strip())) + else: + return None \ No newline at end of file diff --git a/tests/test_medium.py b/tests/test_medium.py new file mode 100644 index 0000000..9155502 --- /dev/null +++ b/tests/test_medium.py @@ -0,0 +1,23 @@ +#!/usr/bin/python3 +# -*- coding: utf8 -*- +import string +import unittest +import random + +from pymedium.medium import Medium + +__author__ = "Engine Bai" + +class TestMedium(unittest.TestCase): + def setUp(self): + self.medium = Medium() + + def test_get_user(self): + self.assertIsNotNone(self.medium.get_user_profile("enginebai")) + self.assertIsNone(self.medium.get_user_profile( + "".join(random.choice(string.ascii_lowercase + string.digits) for _ in range(6)))) + self.assertIsNotNone(self.medium.get_publication_profile("dualcores-studio")) + + +if __name__ == "__main__": + unittest.main() From 54a1b9c855d103cd4c54012f4e2c59c85d03f18c Mon Sep 17 00:00:00 2001 From: Engine Bai Date: Sun, 26 Mar 2017 11:10:22 +0800 Subject: [PATCH 2/5] Add post methods to medium client module --- pymedium/medium.py | 29 +++++++++++++++++++---------- pymedium/model.py | 5 +++++ tests/test_medium.py | 17 ++++++++++++++--- 3 files changed, 38 insertions(+), 13 deletions(-) diff --git a/pymedium/medium.py b/pymedium/medium.py index ec538e7..54fd8a0 100644 --- a/pymedium/medium.py +++ b/pymedium/medium.py @@ -4,7 +4,8 @@ import requests from pymedium.parser import parse_user, parse_publication, parse_post, parse_post_detail -from pymedium.constant import ROOT_URL, HTML_PARSER, ACCEPT_HEADER, ESCAPE_CHARACTERS +from pymedium.constant import ROOT_URL, HTML_PARSER, ACCEPT_HEADER, ESCAPE_CHARACTERS, COUNT +from pymedium.model import Sort class Medium(object): @@ -19,17 +20,21 @@ def get_publication_profile(self, publication_name): url = "{}{}/latest".format(ROOT_URL, publication_name) return self._send_request(url, parse_publication) - def get_user_posts(self, username): - pass + def get_user_posts(self, username, n=COUNT): + return self._send_post_request(ROOT_URL + "@{0}/latest?limit={count}".format(username, count=n)) - def get_publication_posts(self, publication): - pass + def get_publication_posts(self, publication_name, n=COUNT): + return self._send_post_request(ROOT_URL + "{0}/latest?limit={count}".format(publication_name, count=n)) - def get_top_posts(self): - pass + def get_top_posts(self, n=COUNT): + return self._send_post_request(ROOT_URL + "browse/top?limit={count}".format(count=n)) - def search_posts_by_tag(self, tag, sort): - pass + def get_posts_by_tag(self, tag, n=COUNT, sort=Sort.TOP): + url = "{}tag/{tag}".format(ROOT_URL, tag=tag) + if sort == Sort.LATEST: + url += "/latest" + url += "?limit={}".format(n) + return self._send_post_request(url) def parse_post_content(self, url): pass @@ -41,4 +46,8 @@ def _send_request(url, parse_function): if req.status_code == requests.codes.ok: return parse_function(json.loads(req.text.replace(ESCAPE_CHARACTERS, "").strip())) else: - return None \ No newline at end of file + return None + + @staticmethod + def _send_post_request(url): + return Medium._send_request(url, parse_post) \ No newline at end of file diff --git a/pymedium/model.py b/pymedium/model.py index 0bf5122..43df98d 100644 --- a/pymedium/model.py +++ b/pymedium/model.py @@ -346,6 +346,11 @@ class OutputFormat(Enum): MARKDOWN = "md" +class Sort(Enum): + TOP = "top" + LATEST = "latest" + + def to_dict(model): return dict((get_key(key), value) for key, value in model.__dict__.items() diff --git a/tests/test_medium.py b/tests/test_medium.py index 9155502..84932f9 100644 --- a/tests/test_medium.py +++ b/tests/test_medium.py @@ -5,6 +5,7 @@ import random from pymedium.medium import Medium +from pymedium.model import Sort __author__ = "Engine Bai" @@ -12,11 +13,21 @@ class TestMedium(unittest.TestCase): def setUp(self): self.medium = Medium() - def test_get_user(self): - self.assertIsNotNone(self.medium.get_user_profile("enginebai")) + def test_user(self): + user = "enginebai" + self.assertIsNotNone(self.medium.get_user_profile(user)) self.assertIsNone(self.medium.get_user_profile( "".join(random.choice(string.ascii_lowercase + string.digits) for _ in range(6)))) - self.assertIsNotNone(self.medium.get_publication_profile("dualcores-studio")) + + def test_publication(self): + publication = "dualcores-studio" + self.assertIsNotNone(self.medium.get_publication_profile(publication)) + self.assertIsNotNone(self.medium.get_publication_posts(publication)) + + def test_public_posts(self): + self.assertIsNotNone(self.medium.get_top_posts()) + self.assertIsNotNone(self.medium.get_posts_by_tag("android")) + self.assertIsNotNone(self.medium.get_posts_by_tag("android", sort=Sort.LATEST)) if __name__ == "__main__": From db4e60e3ce48c04952a8f8231f9bf252665a008f Mon Sep 17 00:00:00 2001 From: Engine Bai Date: Mon, 27 Mar 2017 18:16:14 +0800 Subject: [PATCH 3/5] Add str and repr method to model; add python sdk link to README --- README.md | 2 + pymedium/api.py | 6 +- pymedium/medium.py | 8 +-- pymedium/model.py | 31 ++++++++++ pymedium/parser.py | 61 +++++++++++++------ requirements.txt | 1 - ...t_regression.py => test_api_regression.py} | 0 7 files changed, 81 insertions(+), 28 deletions(-) rename tests/{test_regression.py => test_api_regression.py} (100%) diff --git a/README.md b/README.md index 80a2808..a544982 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,8 @@ *PyMedium* is an unofficial Medium API written in python flask. It provides developers to access to user, post list and detail information from [Medium]( https://medium.com/) website. This is a read-only API to access public information from Medium, you can customize this API to fit your requirements and deploy on your own server. +Now we provide another way: python client SDK to let you integrate into your application. More detail go to [wiki page](https://github.com/enginebai/PyMedium/wiki/Python-SDK). + ## Installation Before running PyMedium API, you have to clone the code from this repository, install requirements at first. diff --git a/pymedium/api.py b/pymedium/api.py index 69265d8..9d05f0a 100644 --- a/pymedium/api.py +++ b/pymedium/api.py @@ -5,8 +5,8 @@ import requests from flask import Flask, jsonify, Response, request from selenium import webdriver -from pymedium.parser import parse_user, parse_publication, parse_post, parse_post_detail -from pymedium.model import OutputFormat +from .parser import parse_user, parse_publication, parse_post, parse_post_detail +from .model import OutputFormat import pymedium.constant as const app = Flask(__name__) @@ -55,7 +55,7 @@ def send_request(url, headers=const.ACCEPT_HEADER, param=None, parse_function=No if req.status_code == requests.codes.ok: if parse_function is None: parse_function = parse_post - model_dict = parse_function(json.loads(req.text.replace(const.ESCAPE_CHARACTERS, "").strip())) + model_dict = parse_function(json.loads(req.text.replace(const.ESCAPE_CHARACTERS, "").strip()), return_dict=True) return jsonify(model_dict) else: return Response(status=req.status_code) diff --git a/pymedium/medium.py b/pymedium/medium.py index 54fd8a0..dbc9153 100644 --- a/pymedium/medium.py +++ b/pymedium/medium.py @@ -3,9 +3,9 @@ import json import requests -from pymedium.parser import parse_user, parse_publication, parse_post, parse_post_detail -from pymedium.constant import ROOT_URL, HTML_PARSER, ACCEPT_HEADER, ESCAPE_CHARACTERS, COUNT -from pymedium.model import Sort +from .parser import parse_user, parse_publication, parse_post, parse_post_detail +from .constant import ROOT_URL, ACCEPT_HEADER, ESCAPE_CHARACTERS, COUNT +from .model import Sort class Medium(object): @@ -50,4 +50,4 @@ def _send_request(url, parse_function): @staticmethod def _send_post_request(url): - return Medium._send_request(url, parse_post) \ No newline at end of file + return Medium._send_request(url, parse_post) diff --git a/pymedium/model.py b/pymedium/model.py index 43df98d..d044367 100644 --- a/pymedium/model.py +++ b/pymedium/model.py @@ -97,6 +97,12 @@ def author_tags(self): def author_tags(self, tags): self._author_tags = tags + def __str__(self, *args, **kwargs): + return str(to_dict(self)) + + def __repr__(self, *args, **kwargs): + return str(to_dict(self)) + class Post: def __init__(self, post_id): @@ -190,6 +196,12 @@ def post_tags(self): def post_tags(self, tags): self._post_tags = tags + def __str__(self, *args, **kwargs): + return str(to_dict(self)) + + def __repr__(self, *args, **kwargs): + return str(to_dict(self)) + class Publication: def __init__(self, publication_id): @@ -267,6 +279,12 @@ def post_count(self): def post_count(self, count): self._post_count = count + def __str__(self, *args, **kwargs): + return str(to_dict(self)) + + def __repr__(self, *args, **kwargs): + return str(to_dict(self)) + class Tag: @property @@ -309,6 +327,12 @@ def image(self): def image(self, image): self._image = image + def __str__(self, *args, **kwargs): + return str(to_dict(self)) + + def __repr__(self, *args, **kwargs): + return str(to_dict(self)) + class Image: def __init__(self, image_id): @@ -338,6 +362,12 @@ def original_width(self, width): # def url(self, url): # self._url = url + def __str__(self, *args, **kwargs): + return str(to_dict(self)) + + def __repr__(self, *args, **kwargs): + return str(to_dict(self)) + class OutputFormat(Enum): PLAIN_TEXT = "text" @@ -356,5 +386,6 @@ def to_dict(model): for key, value in model.__dict__.items() if not callable(value) and not key.startswith("__")) + def get_key(key): return key.replace("_", "", 1) if key.startswith("_") else key \ No newline at end of file diff --git a/pymedium/parser.py b/pymedium/parser.py index 47ef4c8..da8457d 100644 --- a/pymedium/parser.py +++ b/pymedium/parser.py @@ -6,13 +6,13 @@ import requests from bs4 import BeautifulSoup -from pymedium.model import User, Post, Publication, Tag, Image, OutputFormat, to_dict -from pymedium.constant import ROOT_URL, HTML_PARSER +from .model import User, Post, Publication, Tag, Image, OutputFormat, to_dict +from .constant import ROOT_URL, HTML_PARSER __author__ = 'enginebai' -def parse_user(payload): +def parse_user(payload, return_dict=False): user_dict = payload["payload"]["user"] user_id = user_dict["userId"] user = User(user_id) @@ -27,15 +27,15 @@ def parse_user(payload): ref_dict = payload["payload"]["references"] # interest_tags = user_meta_dict["interestTags"] - # user.interest_tags = parse_tags(interest_tags) + # user.interest_tags = parse_tags(interest_tags, return_dict) # author_tags = user_meta_dict["authorTags"] - # user.author_tags = parse_tags(author_tags) + # user.author_tags = parse_tags(author_tags, return_dict) publication_ids = ref_dict["Collection"] if publication_ids is not None and len(publication_ids.keys()) > 0: publication_list = [] for pub_id in publication_ids.keys(): - publication = parse_publication(payload, pub_id) + publication = parse_publication(payload, pub_id, return_dict) publication_list.append(publication) if len(publication_list) > 0: user.publications = publication_list @@ -54,10 +54,13 @@ def parse_user(payload): user.following_count = following_count user.followedby_count = followby_count - return to_dict(user) + if return_dict: + return to_dict(user) + else: + return user -def parse_publication(payload, pub_id=None): +def parse_publication(payload, pub_id=None, return_dict=False): if pub_id is None: pub_id = payload["payload"]["collection"]["id"] publication_dict = payload["payload"]["references"]["Collection"][pub_id] @@ -66,11 +69,11 @@ def parse_publication(payload, pub_id=None): publication.description = publication_dict["description"] publication.creator_user_id = publication_dict["creatorId"] image_dict = publication_dict["image"] - image = parse_images(image_dict) + image = parse_images(image_dict, return_dict) if image is not None: publication.image = image logo_dict = publication_dict["logo"] - logo = parse_images(logo_dict) + logo = parse_images(logo_dict, return_dict) if logo is not None: publication.logo = logo publication.follower_count = publication_dict["metadata"]["followerCount"] @@ -81,10 +84,13 @@ def parse_publication(payload, pub_id=None): else: publication.url = ROOT_URL + publication_dict["slug"] publication.name = publication_dict["slug"] - return to_dict(publication) + if return_dict: + return to_dict(publication) + else: + return publication -def parse_post(payload): +def parse_post(payload, return_dict=False): # get the different parsing keys post_detail_parsing_keys = ("payload", "references", "Post") if post_detail_parsing_keys is None: @@ -93,6 +99,12 @@ def parse_post(payload): for key in post_detail_parsing_keys: post_list_payload = post_list_payload.get(key) + if post_list_payload is None: + post_detail_parsing_keys = ("payload", "posts") + post_list_payload = payload + for key in post_detail_parsing_keys: + post_list_payload = post_list_payload.get(key) + def parse_post_dict(post_dict, post_id=None): if post_id is None: post_id = post_dict["id"] @@ -129,7 +141,7 @@ def parse_post_dict(post_dict, post_id=None): image_count = virtual_dict["imageCount"] preview_image = virtual_dict["previewImage"] # post_tags = virtual_dict["tags"] - # post.post_tags = parse_tags(post_tags) + # post.post_tags = parse_tags(post_tags, return_dict) # post.unique_slug = unique_slug post.title = title @@ -140,17 +152,20 @@ def parse_post_dict(post_dict, post_id=None): post.read_time = read_time post.word_count = word_count post.image_count = image_count - image = parse_images(preview_image) + image = parse_images(preview_image, return_dict) if image is not None: post.preview_image = image # print("{id}, {title}".format(id=post_id, title=title)) # print("{recommend}, {response}, {read}".format( # recommend=recommend_count, response=response_count, read=read_time)) - return to_dict(post) + if return_dict: + return to_dict(post) + else: + return post post_list = [] - # print(post_list_payload) + print(post_list_payload) # payload -> references -> Post if type(post_list_payload) is dict: for post_id in post_list_payload.keys(): @@ -164,7 +179,7 @@ def parse_post_dict(post_dict, post_id=None): return post_list -def parse_tags(tags_list_dict): +def parse_tags(tags_list_dict, return_dict=False): if tags_list_dict is not None and len(tags_list_dict) > 0: tags_list = [] for tag_dict in tags_list_dict: @@ -175,11 +190,14 @@ def parse_tags(tags_list_dict): metadata_dict = tag_dict["metadata"] if metadata_dict is not None: tag.follower_count = metadata_dict["followerCount"] - tags_list.append(to_dict(tag)) + if return_dict: + tags_list.append(to_dict(tag)) + else: + tags_list.append(tag) return tags_list -def parse_images(image_dict): +def parse_images(image_dict, return_dict=False): if image_dict is not None: image_id = image_dict["imageId"] if "imageId" in image_dict else image_dict["id"] if image_id: @@ -191,7 +209,10 @@ def parse_images(image_dict): # .format(width=image.original_width, # height=image.original_height, # id=image.image_id) - return to_dict(image) + if return_dict: + return to_dict(image) + else: + return image else: return None diff --git a/requirements.txt b/requirements.txt index 9b67658..eba6d91 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,6 @@ appdirs==1.4.0 beautifulsoup4==4.5.1 bs4==0.0.1 -click==6.7 Flask==0.12 itsdangerous==0.24 Jinja2==2.9.5 diff --git a/tests/test_regression.py b/tests/test_api_regression.py similarity index 100% rename from tests/test_regression.py rename to tests/test_api_regression.py From a0ca9076e45ae60ab1f918145a3bccee5178614c Mon Sep 17 00:00:00 2001 From: enginebai Date: Sat, 13 May 2017 14:44:42 +0800 Subject: [PATCH 4/5] Fix setup dependencies problem; fix api crash --- pymedium/api.py | 4 ++-- pymedium/medium.py | 6 +++--- pymedium/parser.py | 7 ++++--- setup.py | 12 +++++++++--- tests/test_medium.py | 1 + 5 files changed, 19 insertions(+), 11 deletions(-) diff --git a/pymedium/api.py b/pymedium/api.py index 9d05f0a..4b876a3 100644 --- a/pymedium/api.py +++ b/pymedium/api.py @@ -5,8 +5,8 @@ import requests from flask import Flask, jsonify, Response, request from selenium import webdriver -from .parser import parse_user, parse_publication, parse_post, parse_post_detail -from .model import OutputFormat +from pymedium.parser import parse_user, parse_publication, parse_post, parse_post_detail +from pymedium.model import OutputFormat import pymedium.constant as const app = Flask(__name__) diff --git a/pymedium/medium.py b/pymedium/medium.py index dbc9153..8ae7cc9 100644 --- a/pymedium/medium.py +++ b/pymedium/medium.py @@ -3,9 +3,9 @@ import json import requests -from .parser import parse_user, parse_publication, parse_post, parse_post_detail -from .constant import ROOT_URL, ACCEPT_HEADER, ESCAPE_CHARACTERS, COUNT -from .model import Sort +from pymedium.parser import parse_user, parse_publication, parse_post, parse_post_detail +from pymedium.constant import ROOT_URL, ACCEPT_HEADER, ESCAPE_CHARACTERS, COUNT +from pymedium.model import Sort class Medium(object): diff --git a/pymedium/parser.py b/pymedium/parser.py index da8457d..03ae8af 100644 --- a/pymedium/parser.py +++ b/pymedium/parser.py @@ -6,8 +6,8 @@ import requests from bs4 import BeautifulSoup -from .model import User, Post, Publication, Tag, Image, OutputFormat, to_dict -from .constant import ROOT_URL, HTML_PARSER +from pymedium.model import User, Post, Publication, Tag, Image, OutputFormat, to_dict +from pymedium.constant import ROOT_URL, HTML_PARSER __author__ = 'enginebai' @@ -77,7 +77,8 @@ def parse_publication(payload, pub_id=None, return_dict=False): if logo is not None: publication.logo = logo publication.follower_count = publication_dict["metadata"]["followerCount"] - publication.post_count = publication_dict["metadata"]["postCount"] + if "postCount" in publication_dict["metadata"]: + publication.post_count = publication_dict["metadata"]["postCount"] if "domain" in publication_dict: publication.url = "http://" + publication_dict["domain"] diff --git a/setup.py b/setup.py index 1f0dad1..2c7beb0 100644 --- a/setup.py +++ b/setup.py @@ -1,16 +1,22 @@ #!/usr/bin/python # -*- encoding: utf-8 -*- -from distutils.core import setup +from setuptools import setup setup( name='PyMedium', - version='1.0.0', - packages=['PyMedium', ], + version='1.0.3', + packages=['pymedium', ], license='The MIT License (MIT) Copyright © 2017 Engine Bai.', description='PyMedium - Unofficial Medium API', long_description=open('README', 'r').read(), author='Engine Bai', author_email='enginebai@gmail.com', url='https://github.com/enginebai/PyMedium', + install_requires=[ + 'flask', + 'bs4', + 'requests', + 'selenium' + ], ) diff --git a/tests/test_medium.py b/tests/test_medium.py index 84932f9..83568b8 100644 --- a/tests/test_medium.py +++ b/tests/test_medium.py @@ -9,6 +9,7 @@ __author__ = "Engine Bai" + class TestMedium(unittest.TestCase): def setUp(self): self.medium = Medium() From 3b5afdbcad827c9c977c7efcb78214e6076ec393 Mon Sep 17 00:00:00 2001 From: enginebai Date: Sun, 14 May 2017 09:46:01 +0800 Subject: [PATCH 5/5] Replace Selenium to use Requests to get post content --- pymedium/api.py | 4 +--- pymedium/parser.py | 11 ++++------- setup.py | 3 +-- tests/test_api_regression.py | 4 ++-- 4 files changed, 8 insertions(+), 14 deletions(-) diff --git a/pymedium/api.py b/pymedium/api.py index 4b876a3..2fbff0f 100644 --- a/pymedium/api.py +++ b/pymedium/api.py @@ -4,13 +4,11 @@ import requests from flask import Flask, jsonify, Response, request -from selenium import webdriver from pymedium.parser import parse_user, parse_publication, parse_post, parse_post_detail from pymedium.model import OutputFormat import pymedium.constant as const app = Flask(__name__) -driver = webdriver.Chrome("driver/chromedriver") @app.route("/", methods=["GET"]) @@ -77,7 +75,7 @@ def get_post(): if not output_format: output_format = OutputFormat.PLAIN_TEXT.value if url: - detail_str = parse_post_detail(url, output_format, driver) + detail_str = parse_post_detail(url, output_format) status_code = 200 mime_type = "text/html" if output_format == OutputFormat.JSON.value: diff --git a/pymedium/parser.py b/pymedium/parser.py index 03ae8af..44487c8 100644 --- a/pymedium/parser.py +++ b/pymedium/parser.py @@ -218,7 +218,7 @@ def parse_images(image_dict, return_dict=False): return None -def parse_post_detail(post_url, output_format, driver): +def parse_post_detail(post_url, output_format): # driver = webdriver.Remote(desired_capabilities=DesiredCapabilities.CHROME) # for json format, just return medium json response if output_format == OutputFormat.JSON.value: @@ -228,17 +228,14 @@ def parse_post_detail(post_url, output_format, driver): else: return None else: - # for else formats, use Selenium to render page to get actual content and parse it - driver.get(post_url) - content_elements = driver.find_element_by_class_name("postArticle-content") - inner_html = BeautifulSoup(content_elements.get_attribute("innerHTML"), HTML_PARSER) + inner_html = BeautifulSoup(requests.get(post_url).text, HTML_PARSER) content_tags = inner_html.find_all() response = "" if output_format == OutputFormat.MARKDOWN.value: for i in range(0, len(content_tags)): tag = content_tags[i] - md = to_markdown(tag, driver) + md = to_markdown(tag) if md is not None and md: response += md + "\n" elif output_format == OutputFormat.HTML.value: @@ -256,7 +253,7 @@ def strip_space(text, trim_space=True): return text -def to_markdown(medium_tag, driver): +def to_markdown(medium_tag): text = strip_space(medium_tag.text) if medium_tag.name == 'h3': return '\n## {}'.format(text) diff --git a/setup.py b/setup.py index 2c7beb0..d2b275b 100644 --- a/setup.py +++ b/setup.py @@ -16,7 +16,6 @@ install_requires=[ 'flask', 'bs4', - 'requests', - 'selenium' + 'requests' ], ) diff --git a/tests/test_api_regression.py b/tests/test_api_regression.py index 1c6e97f..5dc68fa 100644 --- a/tests/test_api_regression.py +++ b/tests/test_api_regression.py @@ -12,8 +12,8 @@ class RegressionTest(unittest.TestCase): def setUp(self): self.users = ( - "sitapati", "enginebai", "101", "mobiscroll", "richard.yang.uw", "tzhongg", "jon.moore", "JonDeng", - "waymo", "quincylarson", "benjaminhardy", "jsaito", "lindacaroll", "jasonfried") + "sitapati", "enginebai", "101", "mobiscroll", "richard.yang.uw", "tzhongg", "jon.moore", "JonDeng", + "waymo", "quincylarson", "benjaminhardy", "jsaito", "lindacaroll", "jasonfried") def test_user_api(self): for user in self.users: