Skip to content

Commit

Permalink
Add Create, Update, Delete provider. (#744, PR:#812)
Browse files Browse the repository at this point in the history
Issue #744
  • Loading branch information
Wim-De-Clercq committed May 2, 2023
1 parent 591d1d2 commit 0189142
Show file tree
Hide file tree
Showing 30 changed files with 1,303 additions and 242 deletions.
40 changes: 40 additions & 0 deletions atramhasis/alembic/versions/b2a7d7614973_add_provider_table.py
@@ -0,0 +1,40 @@
"""Add provider table
Revision ID: b2a7d7614973
Revises: 0978347c16f9
Create Date: 2023-03-22 15:10:27.781804
"""

from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision = "b2a7d7614973"
down_revision = "0978347c16f9"


def upgrade():
op.create_table(
"provider",
sa.Column("id", sa.String(), nullable=False),
sa.Column("conceptscheme_id", sa.Integer(), nullable=False),
sa.Column("uri_pattern", sa.Text(), nullable=False),
sa.Column("metadata", sa.JSON(), nullable=False),
sa.Column(
"expand_strategy",
sa.Enum("RECURSE", "VISIT", name="expandstrategy"),
nullable=True,
),
sa.ForeignKeyConstraint(
["conceptscheme_id"],
["conceptscheme.id"],
ondelete="CASCADE",
),
sa.PrimaryKeyConstraint("id"),
)


def downgrade():
op.drop_table("provider")
28 changes: 25 additions & 3 deletions atramhasis/data/datamanagers.py
Expand Up @@ -7,6 +7,7 @@
import uuid
from datetime import date
from datetime import datetime
from typing import List

import dateutil.relativedelta
import sqlalchemy as sa
Expand All @@ -22,21 +23,24 @@
from sqlalchemy import desc
from sqlalchemy import func
from sqlalchemy import select
from sqlalchemy.orm import Session
from sqlalchemy.orm import joinedload

from atramhasis.data import popular_concepts
from atramhasis.data.models import ConceptVisitLog
from atramhasis.data.models import ConceptschemeCounts
from atramhasis.skos import IDGenerationStrategy
from atramhasis.data.models import IDGenerationStrategy
from atramhasis.data.models import Provider
from atramhasis.scripts import delete_scheme


class DataManager:
"""
A DataManager abstracts all interactions with the database for a certain model.
"""

def __init__(self, session):
self.session = session
def __init__(self, session: Session) -> None:
self.session: Session = session


class ConceptSchemeManager(DataManager):
Expand Down Expand Up @@ -383,3 +387,21 @@ def get_most_recent_count_for_scheme(self, conceptscheme_id):
.order_by(desc('counted_at'))
).scalar_one()
return recent


class ProviderDataManager(DataManager):
"""A data manager for managing Providers."""

def get_provider_by_id(self, provider_id) -> Provider:
return self.session.execute(
select(Provider)
.filter(Provider.id == provider_id)
).scalar_one()

def get_all_providers(self) -> List[Provider]:
"""
Retrieve all providers from the database.
:return: All providers
"""
return self.session.execute(select(Provider)).scalars().all()
76 changes: 72 additions & 4 deletions atramhasis/data/models.py
@@ -1,15 +1,33 @@
import enum

from skosprovider_sqlalchemy.models import ConceptScheme
from sqlalchemy import Column
from sqlalchemy import DateTime
from sqlalchemy import Enum
from sqlalchemy import ForeignKey
from sqlalchemy import Integer
from sqlalchemy import JSON
from sqlalchemy import String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.sql import (
func
)
from sqlalchemy import Text
from sqlalchemy.orm import declarative_base
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.orm import relationship
from sqlalchemy.sql import func

Base = declarative_base()


class IDGenerationStrategy(enum.Enum):
NUMERIC = enum.auto()
GUID = enum.auto()
MANUAL = enum.auto()


class ExpandStrategy(enum.Enum):
RECURSE = 'recurse'
VISIT = 'visit'


class ConceptschemeVisitLog(Base):
__tablename__ = 'conceptscheme_visit_log'
id = Column(Integer, primary_key=True, autoincrement=True)
Expand All @@ -35,3 +53,53 @@ class ConceptschemeCounts(Base):
triples = Column(Integer, nullable=False)
conceptscheme_triples = Column(Integer, nullable=False)
avg_concept_triples = Column(Integer, nullable=False)


class Provider(Base):
__tablename__ = 'provider'

id = Column(String, primary_key=True)
conceptscheme_id = Column(
Integer,
ForeignKey(ConceptScheme.id),
nullable=False,
)
uri_pattern = Column(Text, nullable=False)
meta = Column('metadata', JSON, nullable=False) # metadata is reserved in sqlalchemy
expand_strategy = Column(Enum(ExpandStrategy))

conceptscheme = relationship(
ConceptScheme, uselist=False, single_parent=True, cascade='all, delete-orphan',
)

@hybrid_property
def default_language(self):
return self.meta.get('default_language')

@default_language.setter
def default_language(self, value):
self.meta['default_language'] = value

@hybrid_property
def force_display_language(self):
return self.meta.get('atramhasis.force_display_language')

@force_display_language.setter
def force_display_language(self, value):
self.meta['atramhasis.force_display_language'] = value

@hybrid_property
def id_generation_strategy(self):
return IDGenerationStrategy[self.meta.get('atramhasis.id_generation_strategy')]

@id_generation_strategy.setter
def id_generation_strategy(self, value):
self.meta['atramhasis.id_generation_strategy'] = value.name

@hybrid_property
def subject(self):
return self.meta.get('subject')

@subject.setter
def subject(self, value):
self.meta['subject'] = value
Empty file.
45 changes: 45 additions & 0 deletions atramhasis/json_processors/provider.py
@@ -0,0 +1,45 @@
from typing import Mapping

from skosprovider.registry import Registry
from sqlalchemy.orm import Session

from atramhasis import mappers
from atramhasis.data.datamanagers import ProviderDataManager
from atramhasis.data.models import Provider
from atramhasis.errors import ValidationError


def create_provider(json_data: Mapping, session: Session, skos_registry: Registry) -> Provider:
"""Process a provider JSON into a newly stored Provider."""
for provider in skos_registry.get_providers():
if provider.get_vocabulary_uri() == json_data["conceptscheme_uri"]:
raise ValidationError(
"Provider could not be validated.",
[{"conceptscheme_uri": "Collides with existing provider."}],
)
db_provider = mappers.map_provider(json_data)
if not db_provider.id:
# Store conceptscheme first so we can copy its id
session.add(db_provider.conceptscheme)
session.flush()
db_provider.id = str(db_provider.conceptscheme.id)

session.add(db_provider)
session.flush()

return db_provider


def update_provider(provider_id: str, json_data: Mapping, session: Session) -> Provider:
"""Process a JSON into to update an existing provider."""
manager = ProviderDataManager(session)
db_provider = manager.get_provider_by_id(provider_id)
db_provider = mappers.map_provider(json_data, provider=db_provider)
session.flush()
return db_provider


def delete_provider(provider_id, session: Session) -> None:
manager = ProviderDataManager(session)
db_provider = manager.get_provider_by_id(provider_id)
session.delete(db_provider)
37 changes: 37 additions & 0 deletions atramhasis/mappers.py
Expand Up @@ -4,12 +4,18 @@

from skosprovider_sqlalchemy.models import Collection
from skosprovider_sqlalchemy.models import Concept
from skosprovider_sqlalchemy.models import ConceptScheme
from skosprovider_sqlalchemy.models import Label
from skosprovider_sqlalchemy.models import Match
from skosprovider_sqlalchemy.models import Note
from skosprovider_sqlalchemy.models import Source
from skosprovider_sqlalchemy.providers import SQLAlchemyProvider
from sqlalchemy.exc import NoResultFound

from atramhasis.data.models import ExpandStrategy
from atramhasis.data.models import IDGenerationStrategy
from atramhasis.data.models import Provider


def is_html(value):
"""
Expand Down Expand Up @@ -222,3 +228,34 @@ def map_conceptscheme(conceptscheme, conceptscheme_json):
source = Source(citation=s.get('citation', ''))
conceptscheme.sources.append(source)
return conceptscheme


def map_provider(provider_json: dict, provider: Provider = None) -> Provider:
"""
Create a atramhasis.data.models.Provider from json data.
An existing provider can optionally be passed. When passed this one will be updated.
:param provider_json: JSON data as a dict.
:param provider: A provider which will be updated with the JSON data. When None
a new Provider instance will be returned.
:return: A Provider set with data from the JSON.
"""
if provider is None:
# Only executed on creation.
provider = Provider()
provider.conceptscheme = ConceptScheme(uri=provider_json['conceptscheme_uri'])
provider.id = provider_json.get("id")

provider.meta = provider_json.get("metadata") or {}
provider.default_language = provider_json.get("default_language")
provider.force_display_language = provider_json.get("force_display_language")
provider.id_generation_strategy = IDGenerationStrategy[
provider_json.get("id_generation_strategy") or 'NUMERIC'
]
provider.subject = provider_json.get("subject") or []
provider.uri_pattern = provider_json["uri_pattern"]
provider.expand_strategy = ExpandStrategy[
(provider_json.get("expand_strategy") or 'RECURSE').upper()
]
return provider

0 comments on commit 0189142

Please sign in to comment.