From 96ac7c343b3af54bac4b741d78df3eb09c413f6a Mon Sep 17 00:00:00 2001 From: pcain-usgs <pcain@usgs.gov> Date: Fri, 12 Feb 2021 17:15:21 -0700 Subject: [PATCH 01/12] Implement metadata factory for requests --- geomagio/metadata/MetadataFactory.py | 81 ++++++++++++++++++++++++++++ geomagio/metadata/__init__.py | 3 +- 2 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 geomagio/metadata/MetadataFactory.py diff --git a/geomagio/metadata/MetadataFactory.py b/geomagio/metadata/MetadataFactory.py new file mode 100644 index 000000000..31578aa0c --- /dev/null +++ b/geomagio/metadata/MetadataFactory.py @@ -0,0 +1,81 @@ +import os +import json +import requests +import urllib +from typing import Dict, List, Optional + +from obspy import UTCDateTime +from pydantic import parse_obj_as + +from ..residual import Reading +from .Metadata import Metadata +from .MetadataCategory import MetadataCategory + +HOST = os.getenv("EDGE_HOST", "127.0.0.1:8000") + +class MetadataFactory(object): + def __init__(self, url:str = None): + self.url = url or f"http://{HOST}/ws/secure/metadata" + + def get_metadata( + self, + category: Optional[MetadataCategory] = None, + starttime: Optional[UTCDateTime] = None, + endtime: Optional[UTCDateTime] = None, + created_after: Optional[UTCDateTime] = None, + created_before: Optional[UTCDateTime] = None, + network: Optional[str] = None, + station: Optional[str] = None, + channel: Optional[str] = None, + location: Optional[str] = None, + data_valid: Optional[bool] = None, + metadata_valid: Optional[bool] = True, + # returns interior base models from metadata.metadata + return_objects: str = False, + ) -> List[Metadata]: + args = parse_params(params=locals()) + response = web_request(url=f"{self.url}?{args}") + metadata = self.format_metadata(data=response, return_objects=return_objects) + return metadata + + def put_metadata(self): + raise NotImplementedError + + def format_metadata(self, data: Dict, return_objects: bool = False): + # formats responses as Metadata objects + data = parse_obj_as(List[Metadata], data) + if not return_objects: + return data + formatted_metadata = [] + for metadata in data: + category = metadata.category + if category == MetadataCategory.ADJUSTED_MATRIX: + raise NotImplementedError + elif category == MetadataCategory.FLAG: + raise NotImplementedError + elif category == MetadataCategory.READING: + formatted_metadata.append(parse_obj_as(Reading, metadata.metadata)) + # observatory and instrument metadata do not have a confining object other than metadata + return formatted_metadata + + +def web_request(url:str) -> Dict: + client_id=os.getenv("OPENID_CLIENT_ID") + client_secret=os.getenv("OPENID_CLIENT_SECRET") + response = requests.get(url, data={'grant_type' : 'client_credentials'}, auth = (client_id, client_secret)) + metadata = json.loads(response.text) + return metadata + +def parse_params(params: Dict): + data = {} + for param in params.keys(): + if params[param] is not None and param != "self": + p = params[param] + # convert times to strings + if type(p) == UTCDateTime: + p=p.isoformat() + data[param] = p + + return urllib.parse.urlencode( + data + ) \ No newline at end of file diff --git a/geomagio/metadata/__init__.py b/geomagio/metadata/__init__.py index 7502db024..f1c6be68b 100644 --- a/geomagio/metadata/__init__.py +++ b/geomagio/metadata/__init__.py @@ -1,5 +1,6 @@ from .Metadata import Metadata from .MetadataCategory import MetadataCategory +from .MetadataFactory import MetadataFactory -__all__ = ["Metadata", "MetadataCategory"] +__all__ = ["Metadata", "MetadataCategory", "MetadataFactory"] -- GitLab From d10fa1b84ddaff0f87628bd4088b27920607a273 Mon Sep 17 00:00:00 2001 From: pcain-usgs <pcain@usgs.gov> Date: Tue, 16 Feb 2021 11:16:40 -0700 Subject: [PATCH 02/12] factory entrypoint for requests --- geomagio/geomagioapi/__init__.py | 3 + geomagio/geomagioapi/metadata.py | 54 ++++++++++++++++ geomagio/metadata/MetadataFactory.py | 93 ++++++++++++---------------- geomagio/processing/__init__.py | 2 +- setup.py | 1 + 5 files changed, 99 insertions(+), 54 deletions(-) create mode 100644 geomagio/geomagioapi/__init__.py create mode 100644 geomagio/geomagioapi/metadata.py diff --git a/geomagio/geomagioapi/__init__.py b/geomagio/geomagioapi/__init__.py new file mode 100644 index 000000000..20b3da7f7 --- /dev/null +++ b/geomagio/geomagioapi/__init__.py @@ -0,0 +1,3 @@ +from .metadata import client + +__all__ = ["client"] diff --git a/geomagio/geomagioapi/metadata.py b/geomagio/geomagioapi/metadata.py new file mode 100644 index 000000000..9d56037c7 --- /dev/null +++ b/geomagio/geomagioapi/metadata.py @@ -0,0 +1,54 @@ +import os +from typing import Literal, Optional + +from obspy import UTCDateTime +import typer + +from ..api.secure.MetadataQuery import MetadataQuery +from ..metadata import MetadataCategory, MetadataFactory + + +def main(): + typer.run(client) + + +def client( + action: str, + url: str = "http://{}/ws/secure/metadata".format( + os.getenv("EDGE_HOST", "127.0.0.1:8000") + ), + category: Optional[MetadataCategory] = None, + starttime: Optional[str] = None, + endtime: Optional[str] = None, + created_after: Optional[str] = None, + created_before: Optional[str] = None, + network: Optional[str] = None, + station: Optional[str] = None, + channel: Optional[str] = None, + location: Optional[str] = None, + data_valid: Optional[bool] = None, + metadata_valid: Optional[bool] = True, +): + query = MetadataQuery( + category=category, + starttime=UTCDateTime(starttime) if starttime else None, + endtime=UTCDateTime(endtime) if endtime else None, + created_after=UTCDateTime(created_after) if created_after else None, + created_before=UTCDateTime(created_before) if created_before else None, + network=network, + station=station, + channel=channel, + location=location, + data_valid=data_valid, + metadata_valid=metadata_valid, + ) + factory = MetadataFactory(url=url) + if action == "delete": + factory.delete_metadata(query=query) + elif action == "get": + metadata = factory.get_metadata(query=query) + if action == "post": + factory.post_metadata(query=query) + if action == "update": + factory.update_metadata(query=query) + return metadata diff --git a/geomagio/metadata/MetadataFactory.py b/geomagio/metadata/MetadataFactory.py index 31578aa0c..dc3fefe09 100644 --- a/geomagio/metadata/MetadataFactory.py +++ b/geomagio/metadata/MetadataFactory.py @@ -7,75 +7,62 @@ from typing import Dict, List, Optional from obspy import UTCDateTime from pydantic import parse_obj_as +from ..api.secure import MetadataQuery from ..residual import Reading from .Metadata import Metadata from .MetadataCategory import MetadataCategory -HOST = os.getenv("EDGE_HOST", "127.0.0.1:8000") class MetadataFactory(object): - def __init__(self, url:str = None): - self.url = url or f"http://{HOST}/ws/secure/metadata" - - def get_metadata( + def __init__( self, - category: Optional[MetadataCategory] = None, - starttime: Optional[UTCDateTime] = None, - endtime: Optional[UTCDateTime] = None, - created_after: Optional[UTCDateTime] = None, - created_before: Optional[UTCDateTime] = None, - network: Optional[str] = None, - station: Optional[str] = None, - channel: Optional[str] = None, - location: Optional[str] = None, - data_valid: Optional[bool] = None, - metadata_valid: Optional[bool] = True, - # returns interior base models from metadata.metadata - return_objects: str = False, - ) -> List[Metadata]: - args = parse_params(params=locals()) + url: str = "http://{}/ws/secure/metadata".format( + os.getenv("EDGE_HOST", "127.0.0.1:8000") + ), + ): + self.url = url + + def delete_metadata(self, query: MetadataQuery): + raise NotImplementedError + + def format_metadata(self, data: Dict): + # formats responses as Metadata objects + return parse_obj_as(List[Metadata], data) + + def get_metadata(self, query: MetadataQuery) -> List[Metadata]: + args = parse_params(query=query) response = web_request(url=f"{self.url}?{args}") - metadata = self.format_metadata(data=response, return_objects=return_objects) + metadata = self.format_metadata(data=response) return metadata - def put_metadata(self): + def post_metadata(self, query: MetadataQuery): raise NotImplementedError - def format_metadata(self, data: Dict, return_objects: bool = False): - # formats responses as Metadata objects - data = parse_obj_as(List[Metadata], data) - if not return_objects: - return data - formatted_metadata = [] - for metadata in data: - category = metadata.category - if category == MetadataCategory.ADJUSTED_MATRIX: - raise NotImplementedError - elif category == MetadataCategory.FLAG: - raise NotImplementedError - elif category == MetadataCategory.READING: - formatted_metadata.append(parse_obj_as(Reading, metadata.metadata)) - # observatory and instrument metadata do not have a confining object other than metadata - return formatted_metadata + def update_metadata(self, query: MetadataQuery): + raise NotImplementedError -def web_request(url:str) -> Dict: - client_id=os.getenv("OPENID_CLIENT_ID") - client_secret=os.getenv("OPENID_CLIENT_SECRET") - response = requests.get(url, data={'grant_type' : 'client_credentials'}, auth = (client_id, client_secret)) +def web_request(url: str) -> Dict: + client_id = os.getenv("OPENID_CLIENT_ID") + client_secret = os.getenv("OPENID_CLIENT_SECRET") + response = requests.get( + url, data={"grant_type": "client_credentials"}, auth=(client_id, client_secret) + ) metadata = json.loads(response.text) return metadata -def parse_params(params: Dict): + +def parse_params(query: MetadataQuery): + d = query.dict() data = {} - for param in params.keys(): - if params[param] is not None and param != "self": - p = params[param] - # convert times to strings - if type(p) == UTCDateTime: - p=p.isoformat() - data[param] = p + for key in d.keys(): + if d[key] is None: + continue + # convert times to strings + if type(d[key]) == UTCDateTime: + d[key] = d[key].isoformat() + if key == "category": + d[key] = d[key].value + data[key] = d[key] - return urllib.parse.urlencode( - data - ) \ No newline at end of file + return urllib.parse.urlencode(data) diff --git a/geomagio/processing/__init__.py b/geomagio/processing/__init__.py index b17686a9e..be97cfbd5 100644 --- a/geomagio/processing/__init__.py +++ b/geomagio/processing/__init__.py @@ -17,7 +17,7 @@ __all__ = [ "obsrio_minute", "obsrio_second", "obsrio_temperatures", - "obsrid_tenhertz", + "obsrio_tenhertz", "rotate", "sqdist_minute", ] diff --git a/setup.py b/setup.py index 6216d647e..8009d4f78 100644 --- a/setup.py +++ b/setup.py @@ -28,6 +28,7 @@ setuptools.setup( "magproc-prepfiles=geomagio.processing.magproc:main", "generate-matrix=geomagio.processing.adjusted:main", "obsrio-filter=geomagio.processing.obsrio:main", + "metadata-client=geomagio.geomagioapi.metadata:main", ], }, ) -- GitLab From 8da741190ebace9d6d66a3fb1c8898d2c28da21f Mon Sep 17 00:00:00 2001 From: pcain-usgs <pcain@usgs.gov> Date: Wed, 17 Feb 2021 15:11:36 -0700 Subject: [PATCH 03/12] Gitlab token for delete, post, and update --- geomagio/api/secure/login.py | 46 +++++++++++++- geomagio/api/secure/metadata.py | 2 +- geomagio/geomagioapi/metadata.py | 41 ++++++++++--- geomagio/metadata/MetadataFactory.py | 92 +++++++++++++++------------- 4 files changed, 128 insertions(+), 53 deletions(-) diff --git a/geomagio/api/secure/login.py b/geomagio/api/secure/login.py index c3407d931..92246b504 100644 --- a/geomagio/api/secure/login.py +++ b/geomagio/api/secure/login.py @@ -33,8 +33,10 @@ Usage: /logout - logout current user /user - access current user information as json """ +import json import logging import os +import requests from typing import Callable, List, Optional from authlib.integrations.starlette_client import OAuth @@ -56,17 +58,59 @@ class User(BaseModel): async def current_user(request: Request) -> Optional[User]: - """Get logged in user, or None if not logged in. + """Get user information from gitlab access token or session(if currently logged in). + Returns none if access token is not vald or user is not logged in. Usage example: user: Optional[User] = Depends(current_user) """ + if "Authorization" in request.headers: + return get_api_user(token=request.headers["Authorization"]) if "user" in request.session: return User(**request.session["user"]) return None +def get_api_user(token: str) -> Optional[User]: + url = os.getenv("GITLAB_API_URL") + header = {"PRIVATE-TOKEN": token} + # request user information from gitlab api with access token + user_response = requests.get( + f"{url}/user", + headers=header, + ) + userinfo = json.loads(user_response.content) + try: + user = User( + email=userinfo["email"], + sub=userinfo["id"], + name=userinfo["name"], + nickname=userinfo["username"], + picture=userinfo["avatar_url"], + ) + except KeyError: + logging.info("Invalid token") + return None + # use valid token to retrieve user's groups + user.groups = get_groups_api( + groups_response=requests.get( + f"{url}/groups", + headers=header, + ) + ) + + return user + + +def get_groups_api(groups_response: requests.Response) -> List[str]: + """returns a user's groups via gitlab api""" + groups = json.loads(groups_response.content) + group_urls = [g["web_url"] for g in groups] + group_names = [url[url.find("ghsc") :] for url in group_urls] + return group_names + + def require_user( allowed_groups: List[str] = None, ) -> Callable[[Request, User], User]: diff --git a/geomagio/api/secure/metadata.py b/geomagio/api/secure/metadata.py index 710128d1a..227ce65d0 100644 --- a/geomagio/api/secure/metadata.py +++ b/geomagio/api/secure/metadata.py @@ -41,7 +41,7 @@ async def create_metadata( @router.delete("/metadata/{id}") async def delete_metadata( - id: int, user: User = Depends(require_user(os.getenv("ADMIN_GROUP", "admin"))) + id: int, user: User = Depends(require_user([os.getenv("ADMIN_GROUP", "admin")])) ): await metadata_table.delete_metadata(id) diff --git a/geomagio/geomagioapi/metadata.py b/geomagio/geomagioapi/metadata.py index 9d56037c7..a33317e6f 100644 --- a/geomagio/geomagioapi/metadata.py +++ b/geomagio/geomagioapi/metadata.py @@ -1,5 +1,6 @@ +import json import os -from typing import Literal, Optional +from typing import Dict, Optional from obspy import UTCDateTime import typer @@ -17,6 +18,10 @@ def client( url: str = "http://{}/ws/secure/metadata".format( os.getenv("EDGE_HOST", "127.0.0.1:8000") ), + id: Optional[int] = typer.Option( + None, + help="Database id required for deleting and updating metadata. NOTE: Metadata requests by id ignore additional parameters", + ), category: Optional[MetadataCategory] = None, starttime: Optional[str] = None, endtime: Optional[str] = None, @@ -26,10 +31,18 @@ def client( station: Optional[str] = None, channel: Optional[str] = None, location: Optional[str] = None, - data_valid: Optional[bool] = None, + data_valid: Optional[bool] = True, metadata_valid: Optional[bool] = True, + input_file: Optional[str] = typer.Option( + None, + help="JSON formatted file containing non-shared metadata information", + ), + token: Optional[str] = typer.Option( + os.getenv("GITLAB_API_TOKEN"), help="Gitlab account access token" + ), ): query = MetadataQuery( + id=id, category=category, starttime=UTCDateTime(starttime) if starttime else None, endtime=UTCDateTime(endtime) if endtime else None, @@ -42,13 +55,21 @@ def client( data_valid=data_valid, metadata_valid=metadata_valid, ) - factory = MetadataFactory(url=url) + factory = MetadataFactory(url=url, token=token) if action == "delete": - factory.delete_metadata(query=query) + response = factory.delete_metadata(id=query.id) elif action == "get": - metadata = factory.get_metadata(query=query) - if action == "post": - factory.post_metadata(query=query) - if action == "update": - factory.update_metadata(query=query) - return metadata + response = factory.get_metadata(query=query) + elif action in ["post", "update"]: + try: + with open(input_file, "r") as file: + data = json.load(file) + except (FileNotFoundError, TypeError): + raise ValueError("Input file invalid or not provided") + if action == "post": + response = factory.post_metadata(query=query, data=data) + elif action == "update": + response = factory.update_metadata(id=query.id, query=query, data=data) + else: + raise ValueError("Invalid action") + return response diff --git a/geomagio/metadata/MetadataFactory.py b/geomagio/metadata/MetadataFactory.py index dc3fefe09..21c9e003f 100644 --- a/geomagio/metadata/MetadataFactory.py +++ b/geomagio/metadata/MetadataFactory.py @@ -1,16 +1,14 @@ -import os import json +from json.decoder import JSONDecodeError +import os import requests -import urllib from typing import Dict, List, Optional +import urllib from obspy import UTCDateTime -from pydantic import parse_obj_as -from ..api.secure import MetadataQuery -from ..residual import Reading +from ..api.secure.MetadataQuery import MetadataQuery from .Metadata import Metadata -from .MetadataCategory import MetadataCategory class MetadataFactory(object): @@ -19,50 +17,62 @@ class MetadataFactory(object): url: str = "http://{}/ws/secure/metadata".format( os.getenv("EDGE_HOST", "127.0.0.1:8000") ), + token: str = os.getenv("GITLAB_API_TOKEN"), ): self.url = url + self.token = token + self.header = {"Authorization": self.token} if token else None - def delete_metadata(self, query: MetadataQuery): - raise NotImplementedError - - def format_metadata(self, data: Dict): - # formats responses as Metadata objects - return parse_obj_as(List[Metadata], data) + def delete_metadata(self, id: int) -> Dict: + response = requests.delete(url=f"{self.url}/{id}", headers=self.header) + return response - def get_metadata(self, query: MetadataQuery) -> List[Metadata]: + def get_metadata(self, query: MetadataQuery) -> List[Dict]: args = parse_params(query=query) - response = web_request(url=f"{self.url}?{args}") - metadata = self.format_metadata(data=response) - return metadata + raw_response = requests.get(url=f"{self.url}{args}", headers=self.header) + try: + response = json.loads(raw_response.content) + except JSONDecodeError: + raise ValueError("Data not found") + return response - def post_metadata(self, query: MetadataQuery): - raise NotImplementedError + def post_metadata( + self, query: MetadataQuery, data: Optional[Dict] = {} + ) -> requests.Response: + metadata = parse_metadata(query=query, data=data) + response = requests.post(url=self.url, data=metadata, headers=self.header) + return response - def update_metadata(self, query: MetadataQuery): - raise NotImplementedError + def update_metadata( + self, id: int, query: MetadataQuery, data: Optional[Dict] = {} + ) -> requests.Response: + metadata = parse_metadata(query=query, data=data) + response = requests.put( + url=f"{self.url}/{query.id}", data=metadata, headers=self.header + ) + return response -def web_request(url: str) -> Dict: - client_id = os.getenv("OPENID_CLIENT_ID") - client_secret = os.getenv("OPENID_CLIENT_SECRET") - response = requests.get( - url, data={"grant_type": "client_credentials"}, auth=(client_id, client_secret) - ) - metadata = json.loads(response.text) - return metadata +def parse_metadata(query: MetadataQuery, data: Optional[Dict] = {}) -> str: + metadata = Metadata(**query.dict()) + metadata.metadata = data + return metadata.json() -def parse_params(query: MetadataQuery): - d = query.dict() - data = {} - for key in d.keys(): - if d[key] is None: - continue - # convert times to strings - if type(d[key]) == UTCDateTime: - d[key] = d[key].isoformat() - if key == "category": - d[key] = d[key].value - data[key] = d[key] +def parse_params(query: MetadataQuery) -> str: + query = query.dict() + args = {} + for key in query.keys(): + element = query[key] + if element is not None: + # convert times to strings + if type(element) == UTCDateTime: + element = element.isoformat() + # get string value of metadata category + if key == "category": + element = element.value + elif key == "id": + return f"/{element}" + args[key] = element - return urllib.parse.urlencode(data) + return f"?{urllib.parse.urlencode(args)}" -- GitLab From 4b3c6abb56470f83d4d050ed046017d506e2648b Mon Sep 17 00:00:00 2001 From: pcain-usgs <pcain@usgs.gov> Date: Tue, 2 Mar 2021 14:26:47 -0700 Subject: [PATCH 04/12] Change login logic, rename package, move factory, rename post method/action --- geomagio/api/secure/login.py | 34 ++++++------- .../MetadataFactory.py | 47 ++++++++--------- geomagio/apiclient/__init__.py | 4 ++ .../{geomagioapi => apiclient}/metadata.py | 50 ++++++++++++------- geomagio/geomagioapi/__init__.py | 3 -- geomagio/metadata/__init__.py | 3 +- setup.py | 2 +- 7 files changed, 71 insertions(+), 72 deletions(-) rename geomagio/{metadata => apiclient}/MetadataFactory.py (51%) create mode 100644 geomagio/apiclient/__init__.py rename geomagio/{geomagioapi => apiclient}/metadata.py (55%) delete mode 100644 geomagio/geomagioapi/__init__.py diff --git a/geomagio/api/secure/login.py b/geomagio/api/secure/login.py index 92246b504..b687dec3b 100644 --- a/geomagio/api/secure/login.py +++ b/geomagio/api/secure/login.py @@ -33,7 +33,6 @@ Usage: /logout - logout current user /user - access current user information as json """ -import json import logging import os import requests @@ -65,10 +64,15 @@ async def current_user(request: Request) -> Optional[User]: user: Optional[User] = Depends(current_user) """ - if "Authorization" in request.headers: - return get_api_user(token=request.headers["Authorization"]) if "user" in request.session: return User(**request.session["user"]) + if "apiuser" in request.session: + return User(**request.session["apiuser"]) + if "Authorization" in request.headers: + user = get_api_user(token=request.headers["Authorization"]) + if user is not None: + request.session["apiuser"] = user.dict() + return user return None @@ -76,11 +80,11 @@ def get_api_user(token: str) -> Optional[User]: url = os.getenv("GITLAB_API_URL") header = {"PRIVATE-TOKEN": token} # request user information from gitlab api with access token - user_response = requests.get( + userinfo_response = requests.get( f"{url}/user", headers=header, ) - userinfo = json.loads(user_response.content) + userinfo = userinfo_response.json() try: user = User( email=userinfo["email"], @@ -90,27 +94,17 @@ def get_api_user(token: str) -> Optional[User]: picture=userinfo["avatar_url"], ) except KeyError: - logging.info("Invalid token") + logging.info(f"Invalid token: {userinfo_response.status_code}") return None # use valid token to retrieve user's groups - user.groups = get_groups_api( - groups_response=requests.get( - f"{url}/groups", - headers=header, - ) + groups_response = requests.get( + f"{url}/groups", + headers=header, ) - + user.groups = [g["full_path"] for g in groups_response.json()] return user -def get_groups_api(groups_response: requests.Response) -> List[str]: - """returns a user's groups via gitlab api""" - groups = json.loads(groups_response.content) - group_urls = [g["web_url"] for g in groups] - group_names = [url[url.find("ghsc") :] for url in group_urls] - return group_names - - def require_user( allowed_groups: List[str] = None, ) -> Callable[[Request, User], User]: diff --git a/geomagio/metadata/MetadataFactory.py b/geomagio/apiclient/MetadataFactory.py similarity index 51% rename from geomagio/metadata/MetadataFactory.py rename to geomagio/apiclient/MetadataFactory.py index 21c9e003f..64c43faae 100644 --- a/geomagio/metadata/MetadataFactory.py +++ b/geomagio/apiclient/MetadataFactory.py @@ -1,14 +1,13 @@ -import json -from json.decoder import JSONDecodeError import os import requests -from typing import Dict, List, Optional +from typing import Dict, List, Union import urllib from obspy import UTCDateTime +from pydantic import parse_obj_as from ..api.secure.MetadataQuery import MetadataQuery -from .Metadata import Metadata +from ..metadata import Metadata class MetadataFactory(object): @@ -23,42 +22,36 @@ class MetadataFactory(object): self.token = token self.header = {"Authorization": self.token} if token else None - def delete_metadata(self, id: int) -> Dict: - response = requests.delete(url=f"{self.url}/{id}", headers=self.header) + def delete_metadata(self, metadata: Metadata) -> Dict: + response = requests.delete(url=f"{self.url}/{metadata.id}", headers=self.header) return response - def get_metadata(self, query: MetadataQuery) -> List[Dict]: + def get_metadata(self, query: MetadataQuery) -> Union[List[Metadata], Metadata]: args = parse_params(query=query) - raw_response = requests.get(url=f"{self.url}{args}", headers=self.header) + responses = requests.get(url=f"{self.url}{args}", headers=self.header) try: - response = json.loads(raw_response.content) - except JSONDecodeError: + metadata = parse_obj_as(Union[List[Metadata], Metadata], responses.json()) + except: raise ValueError("Data not found") - return response + return metadata - def post_metadata( - self, query: MetadataQuery, data: Optional[Dict] = {} - ) -> requests.Response: - metadata = parse_metadata(query=query, data=data) - response = requests.post(url=self.url, data=metadata, headers=self.header) + def create_metadata(self, metadata: Metadata) -> requests.Response: + response = requests.post( + url=self.url, data=metadata.json(), headers=self.header + ) return response - def update_metadata( - self, id: int, query: MetadataQuery, data: Optional[Dict] = {} - ) -> requests.Response: - metadata = parse_metadata(query=query, data=data) + def update_metadata(self, metadata: Metadata) -> requests.Response: + if metadata.metadata is None: + metadata.metadata = self.get_metadata( + query=MetadataQuery(**metadata.dict()) + ).metadata response = requests.put( - url=f"{self.url}/{query.id}", data=metadata, headers=self.header + url=f"{self.url}/{metadata.id}", data=metadata.json(), headers=self.header ) return response -def parse_metadata(query: MetadataQuery, data: Optional[Dict] = {}) -> str: - metadata = Metadata(**query.dict()) - metadata.metadata = data - return metadata.json() - - def parse_params(query: MetadataQuery) -> str: query = query.dict() args = {} diff --git a/geomagio/apiclient/__init__.py b/geomagio/apiclient/__init__.py new file mode 100644 index 000000000..a2e83d717 --- /dev/null +++ b/geomagio/apiclient/__init__.py @@ -0,0 +1,4 @@ +from .metadata import client +from .MetadataFactory import MetadataFactory + +__all__ = ["client", "MetadataFactory"] diff --git a/geomagio/geomagioapi/metadata.py b/geomagio/apiclient/metadata.py similarity index 55% rename from geomagio/geomagioapi/metadata.py rename to geomagio/apiclient/metadata.py index a33317e6f..65ea91eed 100644 --- a/geomagio/geomagioapi/metadata.py +++ b/geomagio/apiclient/metadata.py @@ -1,12 +1,13 @@ import json import os -from typing import Dict, Optional +from typing import Optional from obspy import UTCDateTime import typer from ..api.secure.MetadataQuery import MetadataQuery -from ..metadata import MetadataCategory, MetadataFactory +from ..metadata import Metadata, MetadataCategory +from .MetadataFactory import MetadataFactory def main(): @@ -16,12 +17,9 @@ def main(): def client( action: str, url: str = "http://{}/ws/secure/metadata".format( - os.getenv("EDGE_HOST", "127.0.0.1:8000") - ), - id: Optional[int] = typer.Option( - None, - help="Database id required for deleting and updating metadata. NOTE: Metadata requests by id ignore additional parameters", + os.getenv("GEOMAG_API_HOST", "127.0.0.1:8000") ), + id: Optional[int] = None, category: Optional[MetadataCategory] = None, starttime: Optional[str] = None, endtime: Optional[str] = None, @@ -37,11 +35,8 @@ def client( None, help="JSON formatted file containing non-shared metadata information", ), - token: Optional[str] = typer.Option( - os.getenv("GITLAB_API_TOKEN"), help="Gitlab account access token" - ), ): - query = MetadataQuery( + metadata = Metadata( id=id, category=category, starttime=UTCDateTime(starttime) if starttime else None, @@ -55,21 +50,38 @@ def client( data_valid=data_valid, metadata_valid=metadata_valid, ) - factory = MetadataFactory(url=url, token=token) + + factory = MetadataFactory(url=url, token=os.getenv("GITLAB_API_TOKEN")) if action == "delete": - response = factory.delete_metadata(id=query.id) + response = factory.delete_metadata(metadata=metadata) elif action == "get": - response = factory.get_metadata(query=query) - elif action in ["post", "update"]: + response = factory.get_metadata( + query=MetadataQuery( + id=id, + category=category, + starttime=UTCDateTime(starttime) if starttime else None, + endtime=UTCDateTime(endtime) if endtime else None, + created_after=UTCDateTime(created_after) if created_after else None, + created_before=UTCDateTime(created_before) if created_before else None, + network=network, + station=station, + channel=channel, + location=location, + data_valid=data_valid, + metadata_valid=metadata_valid, + ) + ) + elif action in ["create", "update"]: try: with open(input_file, "r") as file: data = json.load(file) except (FileNotFoundError, TypeError): - raise ValueError("Input file invalid or not provided") - if action == "post": - response = factory.post_metadata(query=query, data=data) + data = None + metadata.metadata = data + if action == "create": + response = factory.create_metadata(metadata=metadata) elif action == "update": - response = factory.update_metadata(id=query.id, query=query, data=data) + response = factory.update_metadata(metadata=metadata) else: raise ValueError("Invalid action") return response diff --git a/geomagio/geomagioapi/__init__.py b/geomagio/geomagioapi/__init__.py deleted file mode 100644 index 20b3da7f7..000000000 --- a/geomagio/geomagioapi/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .metadata import client - -__all__ = ["client"] diff --git a/geomagio/metadata/__init__.py b/geomagio/metadata/__init__.py index f1c6be68b..7502db024 100644 --- a/geomagio/metadata/__init__.py +++ b/geomagio/metadata/__init__.py @@ -1,6 +1,5 @@ from .Metadata import Metadata from .MetadataCategory import MetadataCategory -from .MetadataFactory import MetadataFactory -__all__ = ["Metadata", "MetadataCategory", "MetadataFactory"] +__all__ = ["Metadata", "MetadataCategory"] diff --git a/setup.py b/setup.py index 8009d4f78..f7b05c319 100644 --- a/setup.py +++ b/setup.py @@ -28,7 +28,7 @@ setuptools.setup( "magproc-prepfiles=geomagio.processing.magproc:main", "generate-matrix=geomagio.processing.adjusted:main", "obsrio-filter=geomagio.processing.obsrio:main", - "metadata-client=geomagio.geomagioapi.metadata:main", + "geomag-apiclient=geomagio.apiclient.metadata:main", ], }, ) -- GitLab From 704ba79d96212e1cbdbbf1c1a3ecdc044822c275 Mon Sep 17 00:00:00 2001 From: pcain-usgs <pcain@usgs.gov> Date: Wed, 10 Mar 2021 11:48:00 -0700 Subject: [PATCH 05/12] Change hostname, request.get with params, null metadata, typer option --- geomagio/apiclient/MetadataFactory.py | 15 +++++++-------- geomagio/apiclient/metadata.py | 4 +++- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/geomagio/apiclient/MetadataFactory.py b/geomagio/apiclient/MetadataFactory.py index 64c43faae..adfedcfef 100644 --- a/geomagio/apiclient/MetadataFactory.py +++ b/geomagio/apiclient/MetadataFactory.py @@ -14,7 +14,7 @@ class MetadataFactory(object): def __init__( self, url: str = "http://{}/ws/secure/metadata".format( - os.getenv("EDGE_HOST", "127.0.0.1:8000") + os.getenv("GEOMAG_API_HOST", "127.0.0.1:8000") ), token: str = os.getenv("GITLAB_API_TOKEN"), ): @@ -28,7 +28,10 @@ class MetadataFactory(object): def get_metadata(self, query: MetadataQuery) -> Union[List[Metadata], Metadata]: args = parse_params(query=query) - responses = requests.get(url=f"{self.url}{args}", headers=self.header) + if "id" in args: + self.url = f"{self.url}/{args['id']}" + args = {} + responses = requests.get(url=self.url, params=args, headers=self.header) try: metadata = parse_obj_as(Union[List[Metadata], Metadata], responses.json()) except: @@ -42,10 +45,6 @@ class MetadataFactory(object): return response def update_metadata(self, metadata: Metadata) -> requests.Response: - if metadata.metadata is None: - metadata.metadata = self.get_metadata( - query=MetadataQuery(**metadata.dict()) - ).metadata response = requests.put( url=f"{self.url}/{metadata.id}", data=metadata.json(), headers=self.header ) @@ -65,7 +64,7 @@ def parse_params(query: MetadataQuery) -> str: if key == "category": element = element.value elif key == "id": - return f"/{element}" + return {"id": element} args[key] = element - return f"?{urllib.parse.urlencode(args)}" + return args diff --git a/geomagio/apiclient/metadata.py b/geomagio/apiclient/metadata.py index 65ea91eed..535c881c0 100644 --- a/geomagio/apiclient/metadata.py +++ b/geomagio/apiclient/metadata.py @@ -15,7 +15,9 @@ def main(): def client( - action: str, + action: str = typer.Option( + default="get", help="get(default), delete, create, or update" + ), url: str = "http://{}/ws/secure/metadata".format( os.getenv("GEOMAG_API_HOST", "127.0.0.1:8000") ), -- GitLab From 684271e4bbf4e1c564b1eb8c99b434a3d829a3ee Mon Sep 17 00:00:00 2001 From: pcain-usgs <pcain@usgs.gov> Date: Thu, 11 Mar 2021 09:51:54 -0700 Subject: [PATCH 06/12] Create typer commands --- geomagio/api/secure/MetadataQuery.py | 5 +- geomagio/apiclient/__init__.py | 4 +- geomagio/apiclient/metadata.py | 146 +++++++++++++++++++-------- 3 files changed, 111 insertions(+), 44 deletions(-) diff --git a/geomagio/api/secure/MetadataQuery.py b/geomagio/api/secure/MetadataQuery.py index 6b30699d9..f414dd478 100644 --- a/geomagio/api/secure/MetadataQuery.py +++ b/geomagio/api/secure/MetadataQuery.py @@ -2,6 +2,7 @@ from datetime import timezone from obspy import UTCDateTime from pydantic import BaseModel +from typing import Optional from ...metadata import MetadataCategory from ... import pydantic_utcdatetime @@ -18,8 +19,8 @@ class MetadataQuery(BaseModel): station: str = None channel: str = None location: str = None - data_valid: bool = None - metadata_valid: bool = True + data_valid: Optional[bool] = None + metadata_valid: Optional[bool] = None def datetime_dict(self, **kwargs): values = self.dict(**kwargs) diff --git a/geomagio/apiclient/__init__.py b/geomagio/apiclient/__init__.py index a2e83d717..53bb45e4c 100644 --- a/geomagio/apiclient/__init__.py +++ b/geomagio/apiclient/__init__.py @@ -1,4 +1,4 @@ -from .metadata import client +from .metadata import app from .MetadataFactory import MetadataFactory -__all__ = ["client", "MetadataFactory"] +__all__ = ["app", "MetadataFactory"] diff --git a/geomagio/apiclient/metadata.py b/geomagio/apiclient/metadata.py index 535c881c0..fae26289b 100644 --- a/geomagio/apiclient/metadata.py +++ b/geomagio/apiclient/metadata.py @@ -1,6 +1,6 @@ import json import os -from typing import Optional +from typing import Dict, Optional from obspy import UTCDateTime import typer @@ -10,14 +10,20 @@ from ..metadata import Metadata, MetadataCategory from .MetadataFactory import MetadataFactory -def main(): - typer.run(client) +def load_metadata(input_file) -> Optional[Dict]: + try: + with open(input_file, "r") as file: + data = json.load(file) + return data + except (FileNotFoundError, TypeError): + return None -def client( - action: str = typer.Option( - default="get", help="get(default), delete, create, or update" - ), +app = typer.Typer() + + +@app.command() +def create( url: str = "http://{}/ws/secure/metadata".format( os.getenv("GEOMAG_API_HOST", "127.0.0.1:8000") ), @@ -52,38 +58,98 @@ def client( data_valid=data_valid, metadata_valid=metadata_valid, ) + metadata.metadata = load_metadata(input_file=input_file) + response = MetadataFactory(url=url).create_metadata(metadata=metadata) + return response + + +@app.command() +def delete( + id: int, + url: str = "http://{}/ws/secure/metadata".format( + os.getenv("GEOMAG_API_HOST", "127.0.0.1:8000") + ), +): + response = MetadataFactory(url=url).delete_metadata(metadata=Metadata(id=id)) + return response + + +@app.command() +def get( + url: str = "http://{}/ws/secure/metadata".format( + os.getenv("GEOMAG_API_HOST", "127.0.0.1:8000") + ), + id: Optional[int] = None, + category: Optional[MetadataCategory] = None, + starttime: Optional[str] = None, + endtime: Optional[str] = None, + created_after: Optional[str] = None, + created_before: Optional[str] = None, + network: Optional[str] = None, + station: Optional[str] = None, + channel: Optional[str] = None, + location: Optional[str] = None, + data_valid: Optional[bool] = None, + metadata_valid: Optional[bool] = None, +): + query = MetadataQuery( + id=id, + category=category, + starttime=UTCDateTime(starttime) if starttime else None, + endtime=UTCDateTime(endtime) if endtime else None, + created_after=UTCDateTime(created_after) if created_after else None, + created_before=UTCDateTime(created_before) if created_before else None, + network=network, + station=station, + channel=channel, + location=location, + data_valid=data_valid, + metadata_valid=metadata_valid, + ) + metadata = MetadataFactory(url=url).get_metadata(query=query) + return metadata + - factory = MetadataFactory(url=url, token=os.getenv("GITLAB_API_TOKEN")) - if action == "delete": - response = factory.delete_metadata(metadata=metadata) - elif action == "get": - response = factory.get_metadata( - query=MetadataQuery( - id=id, - category=category, - starttime=UTCDateTime(starttime) if starttime else None, - endtime=UTCDateTime(endtime) if endtime else None, - created_after=UTCDateTime(created_after) if created_after else None, - created_before=UTCDateTime(created_before) if created_before else None, - network=network, - station=station, - channel=channel, - location=location, - data_valid=data_valid, - metadata_valid=metadata_valid, - ) - ) - elif action in ["create", "update"]: - try: - with open(input_file, "r") as file: - data = json.load(file) - except (FileNotFoundError, TypeError): - data = None - metadata.metadata = data - if action == "create": - response = factory.create_metadata(metadata=metadata) - elif action == "update": - response = factory.update_metadata(metadata=metadata) - else: - raise ValueError("Invalid action") +@app.command() +def update( + id: int, + url: str = "http://{}/ws/secure/metadata".format( + os.getenv("GEOMAG_API_HOST", "127.0.0.1:8000") + ), + category: Optional[MetadataCategory] = None, + starttime: Optional[str] = None, + endtime: Optional[str] = None, + created_after: Optional[str] = None, + created_before: Optional[str] = None, + network: Optional[str] = None, + station: Optional[str] = None, + channel: Optional[str] = None, + location: Optional[str] = None, + data_valid: Optional[bool] = True, + metadata_valid: Optional[bool] = True, + input_file: Optional[str] = typer.Option( + None, + help="JSON formatted file containing non-shared metadata information", + ), +): + metadata = Metadata( + id=id, + category=category, + starttime=UTCDateTime(starttime) if starttime else None, + endtime=UTCDateTime(endtime) if endtime else None, + created_after=UTCDateTime(created_after) if created_after else None, + created_before=UTCDateTime(created_before) if created_before else None, + network=network, + station=station, + channel=channel, + location=location, + data_valid=data_valid, + metadata_valid=metadata_valid, + ) + metadata.metadata = load_metadata(input_file=input_file) + response = MetadataFactory(url=url).update_metadata(metadata=metadata) return response + + +def main(): + app() -- GitLab From 3f2a2be67fbe47a1773de758021432b64991d0b3 Mon Sep 17 00:00:00 2001 From: pcain-usgs <pcain@usgs.gov> Date: Mon, 15 Mar 2021 07:47:01 -0600 Subject: [PATCH 07/12] Address comments --- geomagio/apiclient/MetadataFactory.py | 2 + geomagio/apiclient/metadata.py | 193 +++++++++++++++++--------- setup.py | 4 +- 3 files changed, 133 insertions(+), 66 deletions(-) diff --git a/geomagio/apiclient/MetadataFactory.py b/geomagio/apiclient/MetadataFactory.py index adfedcfef..8ffaead94 100644 --- a/geomagio/apiclient/MetadataFactory.py +++ b/geomagio/apiclient/MetadataFactory.py @@ -36,6 +36,8 @@ class MetadataFactory(object): metadata = parse_obj_as(Union[List[Metadata], Metadata], responses.json()) except: raise ValueError("Data not found") + if isinstance(metadata, Metadata): + metadata = [metadata] return metadata def create_metadata(self, metadata: Metadata) -> requests.Response: diff --git a/geomagio/apiclient/metadata.py b/geomagio/apiclient/metadata.py index fae26289b..cfed6e7c5 100644 --- a/geomagio/apiclient/metadata.py +++ b/geomagio/apiclient/metadata.py @@ -1,3 +1,4 @@ +import sys import json import os from typing import Dict, Optional @@ -11,6 +12,11 @@ from .MetadataFactory import MetadataFactory def load_metadata(input_file) -> Optional[Dict]: + if input_file is None: + return None + if input_file == "-": + data = json.loads(sys.stdin.read()) + return data try: with open(input_file, "r") as file: data = json.load(file) @@ -27,50 +33,91 @@ def create( url: str = "http://{}/ws/secure/metadata".format( os.getenv("GEOMAG_API_HOST", "127.0.0.1:8000") ), - id: Optional[int] = None, - category: Optional[MetadataCategory] = None, - starttime: Optional[str] = None, - endtime: Optional[str] = None, - created_after: Optional[str] = None, - created_before: Optional[str] = None, - network: Optional[str] = None, - station: Optional[str] = None, - channel: Optional[str] = None, - location: Optional[str] = None, - data_valid: Optional[bool] = True, - metadata_valid: Optional[bool] = True, - input_file: Optional[str] = typer.Option( + category: MetadataCategory = None, + channel: str = None, + created_after: str = None, + created_before: str = None, + data_valid: bool = True, + endtime: str = None, + id: int = None, + input_file: str = typer.Option( None, help="JSON formatted file containing non-shared metadata information", ), + location: str = None, + metadata_valid: bool = True, + network: str = None, + starttime: str = None, + station: str = None, + wrap: bool = True, ): - metadata = Metadata( - id=id, - category=category, - starttime=UTCDateTime(starttime) if starttime else None, - endtime=UTCDateTime(endtime) if endtime else None, - created_after=UTCDateTime(created_after) if created_after else None, - created_before=UTCDateTime(created_before) if created_before else None, - network=network, - station=station, - channel=channel, - location=location, - data_valid=data_valid, - metadata_valid=metadata_valid, - ) - metadata.metadata = load_metadata(input_file=input_file) + + if wrap == True: + metadata = Metadata( + id=id, + category=category, + starttime=UTCDateTime(starttime) if starttime else None, + endtime=UTCDateTime(endtime) if endtime else None, + created_after=UTCDateTime(created_after) if created_after else None, + created_before=UTCDateTime(created_before) if created_before else None, + network=network, + station=station, + channel=channel, + location=location, + data_valid=data_valid, + metadata_valid=metadata_valid, + ) + metadata.metadata = load_metadata(input_file=input_file) + elif wrap == False: + metadata_dict = load_metadata(input_file=input_file) + metadata = Metadata(**metadata_dict) response = MetadataFactory(url=url).create_metadata(metadata=metadata) + print(response.json()) return response @app.command() def delete( - id: int, url: str = "http://{}/ws/secure/metadata".format( os.getenv("GEOMAG_API_HOST", "127.0.0.1:8000") ), + category: MetadataCategory = None, + channel: str = None, + created_after: str = None, + created_before: str = None, + data_valid: bool = True, + endtime: str = None, + id: int = None, + input_file: str = typer.Option( + None, + help="JSON formatted file containing non-shared metadata information", + ), + location: str = None, + metadata_valid: bool = True, + network: str = None, + starttime: str = None, + station: str = None, ): - response = MetadataFactory(url=url).delete_metadata(metadata=Metadata(id=id)) + if input_file is not None: + metadata_dict = load_metadata(input_file=input_file) + metadata = Metadata(**metadata_dict) + else: + metadata = Metadata( + id=id, + category=category, + starttime=starttime, + endtime=endtime, + created_after=created_after, + created_before=created_before, + network=network, + station=station, + channel=channel, + location=location, + data_valid=data_valid, + metadata_valid=metadata_valid, + ) + response = MetadataFactory(url=url).delete_metadata(metadata=metadata) + print(response.json()) return response @@ -79,18 +126,19 @@ def get( url: str = "http://{}/ws/secure/metadata".format( os.getenv("GEOMAG_API_HOST", "127.0.0.1:8000") ), - id: Optional[int] = None, category: Optional[MetadataCategory] = None, - starttime: Optional[str] = None, - endtime: Optional[str] = None, + channel: Optional[str] = None, created_after: Optional[str] = None, created_before: Optional[str] = None, + data_valid: Optional[bool] = True, + endtime: Optional[str] = None, + id: Optional[int] = None, + location: Optional[str] = None, + metadata_valid: Optional[bool] = True, network: Optional[str] = None, + starttime: Optional[str] = None, station: Optional[str] = None, - channel: Optional[str] = None, - location: Optional[str] = None, - data_valid: Optional[bool] = None, - metadata_valid: Optional[bool] = None, + unwrap: bool = False, ): query = MetadataQuery( id=id, @@ -107,47 +155,64 @@ def get( metadata_valid=metadata_valid, ) metadata = MetadataFactory(url=url).get_metadata(query=query) + if unwrap: + data = [m.metadata for m in metadata] + if len(data) == 1: + print(json.dumps(data[0])) + return data[0] + print([json.dumps(d) for d in data]) + return data + if len(metadata) == 1: + print(metadata[0].json()) + return metadata[0] + print([m.json() for m in metadata]) return metadata @app.command() def update( - id: int, url: str = "http://{}/ws/secure/metadata".format( os.getenv("GEOMAG_API_HOST", "127.0.0.1:8000") ), - category: Optional[MetadataCategory] = None, - starttime: Optional[str] = None, - endtime: Optional[str] = None, - created_after: Optional[str] = None, - created_before: Optional[str] = None, - network: Optional[str] = None, - station: Optional[str] = None, - channel: Optional[str] = None, - location: Optional[str] = None, - data_valid: Optional[bool] = True, - metadata_valid: Optional[bool] = True, - input_file: Optional[str] = typer.Option( + category: MetadataCategory = None, + channel: str = None, + created_after: str = None, + created_before: str = None, + data_valid: bool = True, + endtime: str = None, + id: int = None, + input_file: str = typer.Option( None, help="JSON formatted file containing non-shared metadata information", ), + location: str = None, + metadata_valid: bool = True, + network: str = None, + starttime: str = None, + station: str = None, ): - metadata = Metadata( - id=id, - category=category, - starttime=UTCDateTime(starttime) if starttime else None, - endtime=UTCDateTime(endtime) if endtime else None, - created_after=UTCDateTime(created_after) if created_after else None, - created_before=UTCDateTime(created_before) if created_before else None, - network=network, - station=station, - channel=channel, - location=location, - data_valid=data_valid, - metadata_valid=metadata_valid, - ) + if input_file is not None: + metadata_dict = load_metadata(input_file=input_file) + metadata = Metadata(**metadata_dict) + + else: + metadata = Metadata( + id=id, + category=category, + starttime=UTCDateTime(starttime) if starttime else None, + endtime=UTCDateTime(endtime) if endtime else None, + created_after=UTCDateTime(created_after) if created_after else None, + created_before=UTCDateTime(created_before) if created_before else None, + network=network, + station=station, + channel=channel, + location=location, + data_valid=data_valid, + metadata_valid=metadata_valid, + ) metadata.metadata = load_metadata(input_file=input_file) response = MetadataFactory(url=url).update_metadata(metadata=metadata) + print(response.json()) return response diff --git a/setup.py b/setup.py index f7b05c319..a73fd11e7 100644 --- a/setup.py +++ b/setup.py @@ -25,10 +25,10 @@ setuptools.setup( use_pipfile=True, entry_points={ "console_scripts": [ - "magproc-prepfiles=geomagio.processing.magproc:main", "generate-matrix=geomagio.processing.adjusted:main", - "obsrio-filter=geomagio.processing.obsrio:main", "geomag-apiclient=geomagio.apiclient.metadata:main", + "magproc-prepfiles=geomagio.processing.magproc:main", + "obsrio-filter=geomagio.processing.obsrio:main", ], }, ) -- GitLab From 48037d19043e346d729684d1b565f3600969a60e Mon Sep 17 00:00:00 2001 From: pcain-usgs <pcain@usgs.gov> Date: Mon, 15 Mar 2021 15:31:35 -0600 Subject: [PATCH 08/12] Remove returns --- geomagio/apiclient/metadata.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/geomagio/apiclient/metadata.py b/geomagio/apiclient/metadata.py index cfed6e7c5..8d3af668a 100644 --- a/geomagio/apiclient/metadata.py +++ b/geomagio/apiclient/metadata.py @@ -73,7 +73,6 @@ def create( metadata = Metadata(**metadata_dict) response = MetadataFactory(url=url).create_metadata(metadata=metadata) print(response.json()) - return response @app.command() @@ -118,7 +117,6 @@ def delete( ) response = MetadataFactory(url=url).delete_metadata(metadata=metadata) print(response.json()) - return response @app.command() @@ -159,14 +157,10 @@ def get( data = [m.metadata for m in metadata] if len(data) == 1: print(json.dumps(data[0])) - return data[0] print([json.dumps(d) for d in data]) - return data if len(metadata) == 1: print(metadata[0].json()) - return metadata[0] print([m.json() for m in metadata]) - return metadata @app.command() @@ -213,7 +207,6 @@ def update( metadata.metadata = load_metadata(input_file=input_file) response = MetadataFactory(url=url).update_metadata(metadata=metadata) print(response.json()) - return response def main(): -- GitLab From 0126b04a3af7caa289e9211672952f075213d53b Mon Sep 17 00:00:00 2001 From: pcain-usgs <pcain@usgs.gov> Date: Tue, 16 Mar 2021 07:31:05 -0600 Subject: [PATCH 09/12] Clean up prints for get method --- geomagio/apiclient/metadata.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/geomagio/apiclient/metadata.py b/geomagio/apiclient/metadata.py index 8d3af668a..42551ad45 100644 --- a/geomagio/apiclient/metadata.py +++ b/geomagio/apiclient/metadata.py @@ -154,13 +154,13 @@ def get( ) metadata = MetadataFactory(url=url).get_metadata(query=query) if unwrap: - data = [m.metadata for m in metadata] - if len(data) == 1: - print(json.dumps(data[0])) - print([json.dumps(d) for d in data]) + metadata = [json.dumps(m.metadata) for m in metadata] + else: + metadata = [m.json() for m in metadata] if len(metadata) == 1: - print(metadata[0].json()) - print([m.json() for m in metadata]) + print(metadata[0]) + return + print(metadata) @app.command() -- GitLab From 1c4af072922f9644e1060b6812d780e609443575 Mon Sep 17 00:00:00 2001 From: pcain <pcain@usgs.gov> Date: Thu, 18 Mar 2021 16:05:19 -0600 Subject: [PATCH 10/12] Raise file errors, add getone parameter, change bool logic, update factory return types, remove parameters from delete/update --- debugger.py | 3 + geomagio/apiclient/MetadataFactory.py | 25 ++-- geomagio/apiclient/metadata.py | 158 ++++++++------------------ 3 files changed, 61 insertions(+), 125 deletions(-) create mode 100644 debugger.py diff --git a/debugger.py b/debugger.py new file mode 100644 index 000000000..f8e711b4b --- /dev/null +++ b/debugger.py @@ -0,0 +1,3 @@ +from geomagio.apiclient.metadata import create + +create(station="BOU", category="reading") \ No newline at end of file diff --git a/geomagio/apiclient/MetadataFactory.py b/geomagio/apiclient/MetadataFactory.py index 8ffaead94..1d8ded018 100644 --- a/geomagio/apiclient/MetadataFactory.py +++ b/geomagio/apiclient/MetadataFactory.py @@ -1,7 +1,6 @@ import os import requests -from typing import Dict, List, Union -import urllib +from typing import List, Union from obspy import UTCDateTime from pydantic import parse_obj_as @@ -22,35 +21,37 @@ class MetadataFactory(object): self.token = token self.header = {"Authorization": self.token} if token else None - def delete_metadata(self, metadata: Metadata) -> Dict: + def delete_metadata(self, metadata: Metadata) -> bool: response = requests.delete(url=f"{self.url}/{metadata.id}", headers=self.header) - return response + if response.status_code == 200: + return True + return False - def get_metadata(self, query: MetadataQuery) -> Union[List[Metadata], Metadata]: + def get_metadata(self, query: MetadataQuery) -> List[Metadata]: args = parse_params(query=query) if "id" in args: self.url = f"{self.url}/{args['id']}" args = {} - responses = requests.get(url=self.url, params=args, headers=self.header) + response = requests.get(url=self.url, params=args, headers=self.header) try: - metadata = parse_obj_as(Union[List[Metadata], Metadata], responses.json()) + metadata = parse_obj_as(Union[List[Metadata], Metadata], response.json()) except: - raise ValueError("Data not found") + return [] if isinstance(metadata, Metadata): metadata = [metadata] return metadata - def create_metadata(self, metadata: Metadata) -> requests.Response: + def create_metadata(self, metadata: Metadata) -> Metadata: response = requests.post( url=self.url, data=metadata.json(), headers=self.header ) - return response + return Metadata(**response.json()) - def update_metadata(self, metadata: Metadata) -> requests.Response: + def update_metadata(self, metadata: Metadata) -> Metadata: response = requests.put( url=f"{self.url}/{metadata.id}", data=metadata.json(), headers=self.header ) - return response + return Metadata(**response.json()) def parse_params(query: MetadataQuery) -> str: diff --git a/geomagio/apiclient/metadata.py b/geomagio/apiclient/metadata.py index 42551ad45..272e2cd87 100644 --- a/geomagio/apiclient/metadata.py +++ b/geomagio/apiclient/metadata.py @@ -11,18 +11,15 @@ from ..metadata import Metadata, MetadataCategory from .MetadataFactory import MetadataFactory -def load_metadata(input_file) -> Optional[Dict]: +def load_metadata(input_file: str) -> Optional[Dict]: if input_file is None: return None if input_file == "-": data = json.loads(sys.stdin.read()) return data - try: - with open(input_file, "r") as file: - data = json.load(file) - return data - except (FileNotFoundError, TypeError): - return None + with open(input_file, "r") as file: + data = json.load(file) + return data app = typer.Typer() @@ -40,10 +37,7 @@ def create( data_valid: bool = True, endtime: str = None, id: int = None, - input_file: str = typer.Option( - None, - help="JSON formatted file containing non-shared metadata information", - ), + input_file: str = None, location: str = None, metadata_valid: bool = True, network: str = None, @@ -51,72 +45,40 @@ def create( station: str = None, wrap: bool = True, ): - - if wrap == True: + input_metadata = load_metadata(input_file=input_file) + if not wrap: + metadata = Metadata(**input_metadata) + else: metadata = Metadata( - id=id, category=category, - starttime=UTCDateTime(starttime) if starttime else None, - endtime=UTCDateTime(endtime) if endtime else None, + channel=channel, created_after=UTCDateTime(created_after) if created_after else None, created_before=UTCDateTime(created_before) if created_before else None, - network=network, - station=station, - channel=channel, - location=location, data_valid=data_valid, + endtime=UTCDateTime(endtime) if endtime else None, + id=id, + location=location, + metadata = input_metadata["metadata"], metadata_valid=metadata_valid, + network=network, + starttime=UTCDateTime(starttime) if starttime else None, + station=station, ) - metadata.metadata = load_metadata(input_file=input_file) - elif wrap == False: - metadata_dict = load_metadata(input_file=input_file) - metadata = Metadata(**metadata_dict) - response = MetadataFactory(url=url).create_metadata(metadata=metadata) - print(response.json()) + metadata = MetadataFactory(url=url).create_metadata(metadata=metadata) + print(metadata.json()) @app.command() def delete( + input_file: str, url: str = "http://{}/ws/secure/metadata".format( os.getenv("GEOMAG_API_HOST", "127.0.0.1:8000") ), - category: MetadataCategory = None, - channel: str = None, - created_after: str = None, - created_before: str = None, - data_valid: bool = True, - endtime: str = None, - id: int = None, - input_file: str = typer.Option( - None, - help="JSON formatted file containing non-shared metadata information", - ), - location: str = None, - metadata_valid: bool = True, - network: str = None, - starttime: str = None, - station: str = None, ): - if input_file is not None: - metadata_dict = load_metadata(input_file=input_file) - metadata = Metadata(**metadata_dict) - else: - metadata = Metadata( - id=id, - category=category, - starttime=starttime, - endtime=endtime, - created_after=created_after, - created_before=created_before, - network=network, - station=station, - channel=channel, - location=location, - data_valid=data_valid, - metadata_valid=metadata_valid, - ) - response = MetadataFactory(url=url).delete_metadata(metadata=metadata) - print(response.json()) + metadata_dict = load_metadata(input_file=input_file) + metadata = Metadata(**metadata_dict) + deleted = MetadataFactory(url=url).delete_metadata(metadata=metadata) + print(deleted) @app.command() @@ -136,75 +98,45 @@ def get( network: Optional[str] = None, starttime: Optional[str] = None, station: Optional[str] = None, - unwrap: bool = False, + getone: bool = False, ): query = MetadataQuery( - id=id, category=category, - starttime=UTCDateTime(starttime) if starttime else None, - endtime=UTCDateTime(endtime) if endtime else None, + channel=channel, created_after=UTCDateTime(created_after) if created_after else None, created_before=UTCDateTime(created_before) if created_before else None, - network=network, - station=station, - channel=channel, - location=location, data_valid=data_valid, + endtime=UTCDateTime(endtime) if endtime else None, + id=id, + location=location, metadata_valid=metadata_valid, + network=network, + starttime=UTCDateTime(starttime) if starttime else None, + station=station, ) metadata = MetadataFactory(url=url).get_metadata(query=query) - if unwrap: - metadata = [json.dumps(m.metadata) for m in metadata] - else: - metadata = [m.json() for m in metadata] - if len(metadata) == 1: - print(metadata[0]) + if not metadata: + print([]) + return + + if getone: + if len(metadata) > 1: + raise ValueError("More than one matching record") + print(metadata[0].json()) return - print(metadata) + print([m.json() for m in metadata]) + @app.command() def update( + input_file: str, url: str = "http://{}/ws/secure/metadata".format( os.getenv("GEOMAG_API_HOST", "127.0.0.1:8000") ), - category: MetadataCategory = None, - channel: str = None, - created_after: str = None, - created_before: str = None, - data_valid: bool = True, - endtime: str = None, - id: int = None, - input_file: str = typer.Option( - None, - help="JSON formatted file containing non-shared metadata information", - ), - location: str = None, - metadata_valid: bool = True, - network: str = None, - starttime: str = None, - station: str = None, ): - if input_file is not None: - metadata_dict = load_metadata(input_file=input_file) - metadata = Metadata(**metadata_dict) - - else: - metadata = Metadata( - id=id, - category=category, - starttime=UTCDateTime(starttime) if starttime else None, - endtime=UTCDateTime(endtime) if endtime else None, - created_after=UTCDateTime(created_after) if created_after else None, - created_before=UTCDateTime(created_before) if created_before else None, - network=network, - station=station, - channel=channel, - location=location, - data_valid=data_valid, - metadata_valid=metadata_valid, - ) - metadata.metadata = load_metadata(input_file=input_file) + metadata_dict = load_metadata(input_file=input_file) + metadata = Metadata(**metadata_dict) response = MetadataFactory(url=url).update_metadata(metadata=metadata) print(response.json()) -- GitLab From f0da49c60eedaf58ede951cff4fb069d939333b8 Mon Sep 17 00:00:00 2001 From: pcain-usgs <pcain@usgs.gov> Date: Fri, 19 Mar 2021 09:41:16 -0600 Subject: [PATCH 11/12] Black reformat --- debugger.py | 2 +- geomagio/apiclient/metadata.py | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/debugger.py b/debugger.py index f8e711b4b..e62088d4b 100644 --- a/debugger.py +++ b/debugger.py @@ -1,3 +1,3 @@ from geomagio.apiclient.metadata import create -create(station="BOU", category="reading") \ No newline at end of file +create(station="BOU", category="reading") diff --git a/geomagio/apiclient/metadata.py b/geomagio/apiclient/metadata.py index 272e2cd87..0835d5ae0 100644 --- a/geomagio/apiclient/metadata.py +++ b/geomagio/apiclient/metadata.py @@ -58,7 +58,7 @@ def create( endtime=UTCDateTime(endtime) if endtime else None, id=id, location=location, - metadata = input_metadata["metadata"], + metadata=input_metadata["metadata"], metadata_valid=metadata_valid, network=network, starttime=UTCDateTime(starttime) if starttime else None, @@ -125,7 +125,6 @@ def get( print(metadata[0].json()) return print([m.json() for m in metadata]) - @app.command() -- GitLab From 970705c13c49cc62fbe43ded315bf810228d5e2b Mon Sep 17 00:00:00 2001 From: pcain-usgs <pcain@usgs.gov> Date: Tue, 23 Mar 2021 09:33:11 -0600 Subject: [PATCH 12/12] _get_header, get_metadata_by_id, allow exception for no data, move query --- debugger.py | 3 -- geomagio/api/secure/metadata.py | 5 +- geomagio/api/ws/metadata.py | 3 +- geomagio/apiclient/MetadataFactory.py | 46 +++++++++---------- geomagio/apiclient/metadata.py | 10 ++-- .../{api/secure => metadata}/MetadataQuery.py | 4 +- geomagio/metadata/__init__.py | 3 +- 7 files changed, 34 insertions(+), 40 deletions(-) delete mode 100644 debugger.py rename geomagio/{api/secure => metadata}/MetadataQuery.py (91%) diff --git a/debugger.py b/debugger.py deleted file mode 100644 index e62088d4b..000000000 --- a/debugger.py +++ /dev/null @@ -1,3 +0,0 @@ -from geomagio.apiclient.metadata import create - -create(station="BOU", category="reading") diff --git a/geomagio/api/secure/metadata.py b/geomagio/api/secure/metadata.py index 227ce65d0..e619c6636 100644 --- a/geomagio/api/secure/metadata.py +++ b/geomagio/api/secure/metadata.py @@ -19,11 +19,10 @@ from typing import List from fastapi import APIRouter, Body, Depends, Request, Response from obspy import UTCDateTime -from ...metadata import Metadata, MetadataCategory +from ...metadata import Metadata, MetadataCategory, MetadataQuery +from ... import pydantic_utcdatetime from ..db import metadata_table from .login import require_user, User -from .MetadataQuery import MetadataQuery -from ... import pydantic_utcdatetime # routes for login/logout router = APIRouter() diff --git a/geomagio/api/ws/metadata.py b/geomagio/api/ws/metadata.py index 9f4febeb9..655c33e59 100644 --- a/geomagio/api/ws/metadata.py +++ b/geomagio/api/ws/metadata.py @@ -3,8 +3,7 @@ from typing import List from fastapi import APIRouter from obspy import UTCDateTime -from ...metadata import Metadata, MetadataCategory -from ..secure.MetadataQuery import MetadataQuery +from ...metadata import Metadata, MetadataCategory, MetadataQuery from ..db import metadata_table router = APIRouter() diff --git a/geomagio/apiclient/MetadataFactory.py b/geomagio/apiclient/MetadataFactory.py index 1d8ded018..288901c68 100644 --- a/geomagio/apiclient/MetadataFactory.py +++ b/geomagio/apiclient/MetadataFactory.py @@ -5,8 +5,7 @@ from typing import List, Union from obspy import UTCDateTime from pydantic import parse_obj_as -from ..api.secure.MetadataQuery import MetadataQuery -from ..metadata import Metadata +from ..metadata import Metadata, MetadataQuery class MetadataFactory(object): @@ -19,7 +18,10 @@ class MetadataFactory(object): ): self.url = url self.token = token - self.header = {"Authorization": self.token} if token else None + self.header = self._get_headers() + + def _get_headers(self): + return {"Authorization": self.token} if self.token else None def delete_metadata(self, metadata: Metadata) -> bool: response = requests.delete(url=f"{self.url}/{metadata.id}", headers=self.header) @@ -28,19 +30,19 @@ class MetadataFactory(object): return False def get_metadata(self, query: MetadataQuery) -> List[Metadata]: - args = parse_params(query=query) - if "id" in args: - self.url = f"{self.url}/{args['id']}" - args = {} - response = requests.get(url=self.url, params=args, headers=self.header) - try: - metadata = parse_obj_as(Union[List[Metadata], Metadata], response.json()) - except: - return [] + if query.id: + response = self.get_metadata_by_id(id=query.id) + else: + args = parse_params(query=query) + response = requests.get(url=self.url, params=args, headers=self.header) + metadata = parse_obj_as(Union[List[Metadata], Metadata], response.json()) if isinstance(metadata, Metadata): metadata = [metadata] return metadata + def get_metadata_by_id(self, id: int) -> requests.Response: + return requests.get(f"{self.url}/{id}", headers=self.header) + def create_metadata(self, metadata: Metadata) -> Metadata: response = requests.post( url=self.url, data=metadata.json(), headers=self.header @@ -55,19 +57,15 @@ class MetadataFactory(object): def parse_params(query: MetadataQuery) -> str: - query = query.dict() + query = query.dict(exclude_none=True) args = {} for key in query.keys(): element = query[key] - if element is not None: - # convert times to strings - if type(element) == UTCDateTime: - element = element.isoformat() - # get string value of metadata category - if key == "category": - element = element.value - elif key == "id": - return {"id": element} - args[key] = element - + # convert times to strings + if isinstance(element, UTCDateTime): + element = element.isoformat() + # get string value of metadata category + if key == "category": + element = element.value + args[key] = element return args diff --git a/geomagio/apiclient/metadata.py b/geomagio/apiclient/metadata.py index 0835d5ae0..afb3ea63e 100644 --- a/geomagio/apiclient/metadata.py +++ b/geomagio/apiclient/metadata.py @@ -6,8 +6,7 @@ from typing import Dict, Optional from obspy import UTCDateTime import typer -from ..api.secure.MetadataQuery import MetadataQuery -from ..metadata import Metadata, MetadataCategory +from ..metadata import Metadata, MetadataCategory, MetadataQuery from .MetadataFactory import MetadataFactory @@ -58,7 +57,7 @@ def create( endtime=UTCDateTime(endtime) if endtime else None, id=id, location=location, - metadata=input_metadata["metadata"], + metadata=input_metadata, metadata_valid=metadata_valid, network=network, starttime=UTCDateTime(starttime) if starttime else None, @@ -78,7 +77,8 @@ def delete( metadata_dict = load_metadata(input_file=input_file) metadata = Metadata(**metadata_dict) deleted = MetadataFactory(url=url).delete_metadata(metadata=metadata) - print(deleted) + if not deleted: + sys.exit(1) @app.command() @@ -124,7 +124,7 @@ def get( raise ValueError("More than one matching record") print(metadata[0].json()) return - print([m.json() for m in metadata]) + print("[" + ",".join([m.json() for m in metadata]) + "]") @app.command() diff --git a/geomagio/api/secure/MetadataQuery.py b/geomagio/metadata/MetadataQuery.py similarity index 91% rename from geomagio/api/secure/MetadataQuery.py rename to geomagio/metadata/MetadataQuery.py index f414dd478..36e6c3ebe 100644 --- a/geomagio/api/secure/MetadataQuery.py +++ b/geomagio/metadata/MetadataQuery.py @@ -4,8 +4,8 @@ from obspy import UTCDateTime from pydantic import BaseModel from typing import Optional -from ...metadata import MetadataCategory -from ... import pydantic_utcdatetime +from .. import pydantic_utcdatetime +from .MetadataCategory import MetadataCategory class MetadataQuery(BaseModel): diff --git a/geomagio/metadata/__init__.py b/geomagio/metadata/__init__.py index 7502db024..37b2adc33 100644 --- a/geomagio/metadata/__init__.py +++ b/geomagio/metadata/__init__.py @@ -1,5 +1,6 @@ from .Metadata import Metadata from .MetadataCategory import MetadataCategory +from .MetadataQuery import MetadataQuery -__all__ = ["Metadata", "MetadataCategory"] +__all__ = ["Metadata", "MetadataCategory", "MetadataQuery"] -- GitLab