From 23480cbd1d0faebd3491f3dc8e643ba63e75d307 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magn=C3=BAs=20D=C3=A6hlen?= Date: Wed, 25 Sep 2024 13:20:20 +0200 Subject: [PATCH 1/2] Kick out django and clean up to make it forkable --- main.py | 64 ++++++---- nebula_carina/apps.py | 13 -- nebula_carina/management/__init__.py | 0 nebula_carina/management/commands/__init__.py | 0 .../management/commands/nebulamigrate.py | 22 ---- nebula_carina/models/migrations.py | 8 +- nebula_carina/ngql/schema/data_types.py | 115 +++++++++++------- nebula_carina/settings.py | 59 ++------- 8 files changed, 127 insertions(+), 154 deletions(-) delete mode 100644 nebula_carina/apps.py delete mode 100644 nebula_carina/management/__init__.py delete mode 100644 nebula_carina/management/commands/__init__.py delete mode 100644 nebula_carina/management/commands/nebulamigrate.py diff --git a/main.py b/main.py index 11eeafa..06d6a24 100644 --- a/main.py +++ b/main.py @@ -2,7 +2,14 @@ from fastapi import FastAPI -from example.models import VirtualCharacter, Love, Figure, Source, Support, LimitedCharacter +from example.models import ( + VirtualCharacter, + Love, + Figure, + Source, + Support, + LimitedCharacter, +) from nebula_carina.models.migrations import make_migrations, migrate from nebula_carina.models.model_builder import ModelBuilder from nebula_carina.models.models import EdgeModel @@ -14,8 +21,12 @@ app = FastAPI() + @app.get("/") async def root(): + pass + # rest = make_migrations() + # print(rest) # run_ngql('SHOW SPACES;') # print(show_spaces()) # print(use_space('main')) @@ -105,27 +116,32 @@ async def root(): # ).save() # EdgeModel(src_vid='char_test1', dst_vid='char_test2', ranking=0, edge_type=Love(way='gun', times=40)).save() # return EdgeModel.objects.find_between('char_test1', 'char_test2') - character1 = VirtualCharacter.objects.get('char_test1') - # LocalSession().session.release() - character2 = VirtualCharacter.objects.get('char_test2') - character1.get_out_edges(Love) - character2.get_reverse_edges(Love) - character1.get_out_edge_and_destinations(Love, VirtualCharacter) - character2.get_reverse_edge_and_sources(Love, VirtualCharacter) - VirtualCharacter.objects.find_destinations('char_test1', Love) - VirtualCharacter.objects.find_sources('char_test2', Love, distinct=False, limit=Limit(1)) - character2.get_sources(Love, VirtualCharacter) - character1.get_destinations(Love, VirtualCharacter) - # return character1 - # return rst - # return ModelBuilder.match( - # '(v)-[e:love]->(v2)', {'v': VirtualCharacter, 'e': EdgeModel, 'v2': VirtualCharacter}, - # condition=Q(v__id__in=[112, 113]), + # character1 = VirtualCharacter.objects.get("char_test1") + # # LocalSession().session.release() + # character2 = VirtualCharacter.objects.get("char_test2") + # character1.get_out_edges(Love) + # character2.get_reverse_edges(Love) + # character1.get_out_edge_and_destinations(Love, VirtualCharacter) + # character2.get_reverse_edge_and_sources(Love, VirtualCharacter) + # VirtualCharacter.objects.find_destinations("char_test1", Love) + # VirtualCharacter.objects.find_sources( + # "char_test2", Love, distinct=False, limit=Limit(1) + # ) + # character2.get_sources(Love, VirtualCharacter) + # character1.get_destinations(Love, VirtualCharacter) + # # return character1 + # # return rst + # # return ModelBuilder.match( + # # '(v)-[e:love]->(v2)', {'v': VirtualCharacter, 'e': EdgeModel, 'v2': VirtualCharacter}, + # # condition=Q(v__id__in=[112, 113]), + # # ) + # # EdgeModel(src_vid='char_test1', dst_vid='char_test2', ranking=0, edge_type=Love(way='gun', times=40)).save() + # return ModelBuilder.serialized_match( + # "(v)-[e:love]->(v2)", + # { + # "v": VirtualCharacter, + # "e": EdgeModel, + # "v2": VirtualCharacter, + # }, + # condition=Q(v__id="char_test1"), # ) - # EdgeModel(src_vid='char_test1', dst_vid='char_test2', ranking=0, edge_type=Love(way='gun', times=40)).save() - return ModelBuilder.serialized_match( - '(v)-[e:love]->(v2)', { - 'v': VirtualCharacter, 'e': EdgeModel, 'v2': VirtualCharacter, - }, - condition=Q(v__id="char_test1"), - ) diff --git a/nebula_carina/apps.py b/nebula_carina/apps.py deleted file mode 100644 index b958f6b..0000000 --- a/nebula_carina/apps.py +++ /dev/null @@ -1,13 +0,0 @@ -try: - from django.apps import AppConfig - from django.utils.translation import gettext_lazy as _ - - - class NebulaCarinaConfig(AppConfig): - name = "nebula_carina" - label = "nebula_carina" - - verbose_name = _("Nebula Carina") - -except ModuleNotFoundError: - pass diff --git a/nebula_carina/management/__init__.py b/nebula_carina/management/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/nebula_carina/management/commands/__init__.py b/nebula_carina/management/commands/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/nebula_carina/management/commands/nebulamigrate.py b/nebula_carina/management/commands/nebulamigrate.py deleted file mode 100644 index f5011e0..0000000 --- a/nebula_carina/management/commands/nebulamigrate.py +++ /dev/null @@ -1,22 +0,0 @@ -try: - from django.core.management.base import BaseCommand, CommandError - from nebula_carina.models.migrations import make_migrations, migrate - - - class Command(BaseCommand): - help = 'Run Nebula Graph migrations' - - def handle(self, *args, **options): - migrations = make_migrations() - if not migrations: - self.stdout.write('No Nebula Graph schema change found') - return - self.stdout.write('The following migration NGQLs will be executed: \n\n%s' % ('\n'.join(migrations))) - if input("Type 'yes' to continue, or 'no' to cancel\n") == 'yes': - migrate(make_migrations()) - self.stdout.write(self.style.SUCCESS('Nebula migration succeeded.')) - else: - self.stdout.write(self.style.NOTICE('Nebula migration cancelled.')) - -except ModuleNotFoundError: - pass diff --git a/nebula_carina/models/migrations.py b/nebula_carina/models/migrations.py index 826bb22..a95f0be 100644 --- a/nebula_carina/models/migrations.py +++ b/nebula_carina/models/migrations.py @@ -1,20 +1,20 @@ from nebula_carina.models.models import TagModel, EdgeTypeModel from nebula_carina.ngql.connection.connection import run_ngql from nebula_carina.ngql.schema.schema import show_tags, show_edges -from nebula_carina.settings import database_settings from importlib import import_module import inspect -def make_migrations(): +def make_migrations(model_paths: list[str] = None): existing_tags = show_tags() existing_edges = show_edges() ngql_list = [] - model_paths = database_settings.model_paths for model_path in model_paths: module = import_module(model_path) for name, cls in module.__dict__.items(): - if inspect.isclass(cls) and (issubclass(cls, TagModel) or issubclass(cls, EdgeTypeModel)): + if inspect.isclass(cls) and ( + issubclass(cls, TagModel) or issubclass(cls, EdgeTypeModel) + ): if cls.db_name() in existing_tags or cls.db_name() in existing_edges: alter_schema_ngql = cls.alter_schema_ngql() alter_schema_ngql and ngql_list.append(alter_schema_ngql) diff --git a/nebula_carina/ngql/schema/data_types.py b/nebula_carina/ngql/schema/data_types.py index 2c632d9..67604ef 100644 --- a/nebula_carina/ngql/schema/data_types.py +++ b/nebula_carina/ngql/schema/data_types.py @@ -15,12 +15,12 @@ class DataTypeMetaClass(type): def __init__(cls, classname, superclasses, attributedict): super().__init__(classname, superclasses, attributedict) - if cls.__name__ != 'DataType': + if cls.__name__ != "DataType": data_type_factory[pascal_case_to_snake_case(cls.__name__).upper()] = cls - if hasattr(cls, 'nebula_ttype'): - ttype2data_type[getattr(cls, 'nebula_ttype')] = cls - if getattr(cls, 'is_data_type_for_auto_convert', False): - python_type2data_type[getattr(cls, 'python_data_type')] = cls + if hasattr(cls, "nebula_ttype"): + ttype2data_type[getattr(cls, "nebula_ttype")] = cls + if getattr(cls, "is_data_type_for_auto_convert", False): + python_type2data_type[getattr(cls, "python_data_type")] = cls class DataType(metaclass=DataTypeMetaClass): @@ -53,9 +53,9 @@ class _DigitType(DataType): @classmethod def value2db_str(cls, value): if value is None: - return 'NULL' + return "NULL" if not str(value).isdigit(): - raise ValueError(f'{cls.__name__} value should be None or digit') + raise ValueError(f"{cls.__name__} value should be None or digit") return str(value) @@ -91,20 +91,20 @@ class String(DataType): @classmethod def value2db_str(cls, value): - return 'NULL' if value is None else f'"{value}"' + return "NULL" if value is None else f'"{value}"' class FixedString(DataType): python_data_type = str - __slots__ = ('max_length', ) + __slots__ = ("max_length",) def __init__(self, max_length): super().__init__() self.max_length = int(max_length) def __str__(self): - return f'{super().__str__()}({self.max_length})' + return f"{super().__str__()}({self.max_length})" def __eq__(self, other): if not isinstance(other, FixedString): @@ -113,7 +113,7 @@ def __eq__(self, other): @classmethod def value2db_str(cls, value): - return 'NULL' if value is None else f'"{value}"' + return "NULL" if value is None else f'"{value}"' class Bool(DataType): @@ -123,13 +123,13 @@ class Bool(DataType): @classmethod def value2db_str(cls, value): if value is None: - return 'NULL' - return 'true' if value else 'false' + return "NULL" + return "true" if value else "false" class Date(DataType): nebula_ttype = ttypes.Date - auto = '' + auto = "" python_data_type = date is_data_type_for_auto_convert = True @@ -137,95 +137,114 @@ class Date(DataType): def ttype2python_type(cls, value: ttypes.Date | str): if value is None: return - if value == 'date()': - return '' + if value == "date()": + return "" if isinstance(value, ttypes.Date): return date(value.year, value.month, value.day) - raise ValueError('Date value should be None or date') + raise ValueError("Date value should be None or date") @classmethod def value2db_str(cls, value: None | str | time): if value is None: - return 'NULL' + return "NULL" if value == Date.auto: - return 'date()' - assert isinstance(value, time), 'Date python value should be None or datetime.date' + return "date()" + assert isinstance( + value, time + ), "Date python value should be None or datetime.date" return f'date("{value}")' class Time(DataType): nebula_ttype = ttypes.Time - auto = '' + auto = "" python_data_type = time is_data_type_for_auto_convert = True @classmethod def clean_default(cls, default_value): if isinstance(default_value, time) and not default_value.tzinfo: - return default_value.replace(tzinfo=pytz.timezone(database_settings.timezone_name)) + return default_value.replace( + tzinfo=pytz.timezone(database_settings.timezone_name) + ) return default_value @classmethod def ttype2python_type(cls, value: ttypes.Time | str): if value is None: return - if value == 'time()': - return '' + if value == "time()": + return "" if isinstance(value, ttypes.Time): return time( - value.hour, value.minute, value.sec, value.microsec, - tzinfo=pytz.timezone(database_settings.timezone_name) + value.hour, + value.minute, + value.sec, + value.microsec, + tzinfo=pytz.timezone(database_settings.timezone_name), ) - raise ValueError('Time value should be None or Time') + raise ValueError("Time value should be None or Time") @classmethod def value2db_str(cls, value: None | str | time): if value is None: - return 'NULL' + return "NULL" if value == Time.auto: - return 'time()' - assert isinstance(value, time), 'DateTime python value should be None or datetime.time' + return "time()" + assert isinstance( + value, time + ), "DateTime python value should be None or datetime.time" return f'time("{value}")' class Datetime(DataType): nebula_ttype = ttypes.DateTime - auto = '' + auto = "" python_data_type = datetime is_data_type_for_auto_convert = True @classmethod def clean_default(cls, default_value): if isinstance(default_value, datetime) and not default_value.tzinfo: - return default_value.replace(tzinfo=pytz.timezone(database_settings.timezone_name)) + return default_value.replace( + tzinfo=pytz.timezone(database_settings.timezone_name) + ) return default_value @classmethod def ttype2python_type(cls, value: ttypes.DateTime | str): if value is None: return - if value == 'datetime()': - return '' + if value == "datetime()": + return "" if isinstance(value, ttypes.DateTime): return datetime( - value.year, value.month, value.day, value.hour, value.minute, value.sec, value.microsec, - tzinfo=pytz.timezone(database_settings.timezone_name) + value.year, + value.month, + value.day, + value.hour, + value.minute, + value.sec, + value.microsec, + tzinfo=pytz.timezone(database_settings.timezone_name), ) - raise ValueError('DateTime ngql value should be None or DateTime') + raise ValueError("DateTime ngql value should be None or DateTime") @classmethod def value2db_str(cls, value: None | str | datetime): if value is None: - return 'NULL' + return "NULL" if value == Datetime.auto: - return 'datetime()' - assert isinstance(value, datetime), 'DateTime python value should be None or datetime.datetime' + return "datetime()" + assert isinstance( + value, datetime + ), "DateTime python value should be None or datetime.datetime" return f'datetime("{value}")' def string_to_data_type(db_type_string: str) -> DataType: db_type_string = db_type_string.upper() - split_string = db_type_string.split('(', 1) + split_string = db_type_string.split("(", 1) db_type, additional = None, None if len(split_string) == 1: db_type = split_string[0] @@ -235,14 +254,20 @@ def string_to_data_type(db_type_string: str) -> DataType: if data_type_class := data_type_factory.get(db_type): return data_type_class(additional) if additional else data_type_class() else: - raise RuntimeError('Cannot find the data type!') + raise RuntimeError("Cannot find the data type!") -def ttype2python_value(val: any): - return (ttype2data_type[type(val)] if type(val) in ttype2data_type else DataType).ttype2python_type(val) +def ttype2python_value(val: any): + return ( + ttype2data_type[type(val)] if type(val) in ttype2data_type else DataType + ).ttype2python_type(val) def auto_convert_value_to_db_str(val: any): if isinstance(val, list): return f'[{", ".join([auto_convert_value_to_db_str(i) for i in val])}]' - return (python_type2data_type[type(val)] if type(val) in python_type2data_type else DataType).value2db_str(val) + return ( + python_type2data_type[type(val)] + if type(val) in python_type2data_type + else DataType + ).value2db_str(val) diff --git a/nebula_carina/settings.py b/nebula_carina/settings.py index 12db9c7..3f0ea1e 100644 --- a/nebula_carina/settings.py +++ b/nebula_carina/settings.py @@ -1,52 +1,19 @@ -from typing import Set, Optional, Union -import typing +from typing import Set, Optional from pydantic_settings import BaseSettings, SettingsConfigDict -try: - # for django - from django.conf import settings +class DatabaseSettings(BaseSettings): + max_connection_pool_size: int = 10 + servers: Set[str] = set() + user_name: str + password: str + default_space: str = "main" + auto_create_default_space_with_vid_desc: Optional[str] - class DjangoCarinaDatabaseSettings(object): - max_connection_pool_size: int = 10 - servers: Set[str] = set() - user_name: str - password: str - default_space: str = "main" - auto_create_default_space_with_vid_desc: Optional[str] + timezone_name: str = "UTC" + model_config = SettingsConfigDict( + env_prefix="nebula_", env_file=".env", extra="ignore" + ) - model_paths: Set[str] = set() - timezone_name: str = "UTC" - @staticmethod - def is_optional(tp): - return typing.get_origin(tp) is Union and type(None) in typing.get_args(tp) - - def __init__(self, **kwargs): - for key, type_ in DjangoCarinaDatabaseSettings.__dict__[ - "__annotations__" - ].items(): - if not self.is_optional(type_) and not hasattr( - DjangoCarinaDatabaseSettings, key - ): - assert ( - key in kwargs - ), f"Setting {key} is required but not provided in CARINA_SETTINGS." - key in kwargs and setattr(self, key, kwargs[key]) - - database_settings = DjangoCarinaDatabaseSettings(**settings.CARINA_SETTINGS) -except ModuleNotFoundError: - - class DatabaseSettings(BaseSettings): - max_connection_pool_size: int = 10 - servers: Set[str] = set() - user_name: str - password: str - default_space: str = "main" - auto_create_default_space_with_vid_desc: Optional[str] - - model_paths: Set[str] = set() - timezone_name: str = "UTC" - model_config = SettingsConfigDict(env_prefix="nebula_") - - database_settings = DatabaseSettings() +database_settings = DatabaseSettings() From 0c3a5ec4e7d6583eed77ca4260deff0d576397ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magn=C3=BAs=20D=C3=A6hlen?= Date: Fri, 18 Oct 2024 15:15:37 +0200 Subject: [PATCH 2/2] Add JSONString type and class override func --- nebula_carina/ngql/schema/data_types.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/nebula_carina/ngql/schema/data_types.py b/nebula_carina/ngql/schema/data_types.py index 2c632d9..c30dd5a 100644 --- a/nebula_carina/ngql/schema/data_types.py +++ b/nebula_carina/ngql/schema/data_types.py @@ -29,8 +29,11 @@ class DataType(metaclass=DataTypeMetaClass): nebula_ttype = None python_data_type = None is_data_type_for_auto_convert = False + class_override = None def __str__(self): + if self.class_override: + return pascal_case_to_snake_case(self.class_override.__name__).upper() return pascal_case_to_snake_case(self.__class__.__name__).upper() def __eq__(self, other): @@ -116,6 +119,16 @@ def value2db_str(cls, value): return 'NULL' if value is None else f'"{value}"' +class JSONString(DataType): + def __init__(self): + super().__init__() + self.class_override = String + + @classmethod + def value2db_str(cls, value): + return "NULL" if value is None else f"'{value}'" + + class Bool(DataType): python_data_type = bool is_data_type_for_auto_convert = True