Skip to content

Commit

Permalink
40-logs-base-class (#45)
Browse files Browse the repository at this point in the history
* Added base log class which validates json and has serialization/deserialization method

* Made changes based on review

* Updated based on second review

---------

Co-authored-by: rthenhaus <rthenhaus>
  • Loading branch information
rc10house authored Jul 2, 2024
1 parent 433bf36 commit 255cf38
Show file tree
Hide file tree
Showing 7 changed files with 433 additions and 92 deletions.
55 changes: 55 additions & 0 deletions distill/core/log.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import json

from pydantic import BaseModel
from pydantic.type_adapter import TypeAdapter
from typing import Dict, Union

from distill.core.types import JsonDict, JSONSerializable
from distill.schemas.userale import UserAleSchema

ta = TypeAdapter(JsonDict)

class Log:
"""
Base class for log object representation.
Arguments:
data: a json parsable string or JsonObject to validate and parse
schema: a pydantic schema from which to validate the provided log;
defaults to UserAle log schema
"""

def __init__(self, data: Union[str, JsonDict], schema=UserAleSchema):
if not issubclass(schema, BaseModel):
raise TypeError("schema should inherit from pydantic.BaseModel")

if isinstance(data, str):
schema.model_validate_json(data, strict=True)
data = json.loads(data)
elif ta.validate_python(data):
schema.model_validate(data, strict=True)
else:
raise TypeError("ERROR: " + str(type(data)) + " data should be either a string or a JsonDict")
self.data = schema(**data)

# TODO: need to create ID field here on object initialization

def to_json(self) -> str:
return self.data.model_dump_json(by_alias=True)

def to_dict(self) -> JsonDict:
return self.data.model_dump(by_alias=True)
20 changes: 20 additions & 0 deletions distill/core/types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from typing import Union, List, Dict
from typing_extensions import Annotated, TypeAliasType

# TypeAliasType is necessary to avoid recursion error when validating this
# type with Pydantic
JSONSerializable = TypeAliasType(
"JSONSerializable",
Union[str,
int,
float,
bool,
None,
List['JSONSerializable'],
Dict[str, 'JSONSerializable']
],
)

JsonDict = Dict[str, 'JSONSerializable']


73 changes: 73 additions & 0 deletions distill/schemas/userale.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from enum import Enum
from typing import List, Optional

from pydantic import AliasGenerator, BaseModel, Field
from pydantic.alias_generators import to_camel
from pydantic.config import ConfigDict


class Browser(BaseModel):
browser: str
version: str


class Location(BaseModel):
x: Optional[int]
y: Optional[int]


class ScrnRes(BaseModel):
width: int
height: int


class Details(BaseModel):
window: bool


class UserAleSchema(BaseModel):
"""
A raw or custom log produced by UserAle
"""

model_config = ConfigDict(
title="Log",
alias_generator=AliasGenerator(
validation_alias=to_camel, serialization_alias=to_camel
),
)

target: str
path: List[str]
page_url: str
page_title: str
page_referrer: str
browser: Browser
client_time: int
micro_time: int = Field(..., lt=2)
location: Location
scrn_res: ScrnRes
type_field: str = Field(..., validation_alias="type", serialization_alias="type")
log_type: str
user_action: bool
details: Details
user_id: str
tool_version: Optional[str]
tool_name: Optional[str]
userale_version: Optional[str]
session_id: str
Loading

0 comments on commit 255cf38

Please sign in to comment.