Skip to content

Commit

Permalink
feat: Add DOI mapping
Browse files Browse the repository at this point in the history
  • Loading branch information
drorganvidez committed Jul 8, 2024
1 parent 633e42a commit 19143b8
Show file tree
Hide file tree
Showing 7 changed files with 334 additions and 181 deletions.
21 changes: 9 additions & 12 deletions app/modules/dataset/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ def get_file_total_size(self):
return sum(file.size for fm in self.feature_models for file in fm.files)

def get_file_total_size_for_human(self):
return get_human_readable_size(self.get_file_total_size())
from app.modules.dataset.services import SizeService
return SizeService().get_human_readable_size(self.get_file_total_size())

def get_uvlhub_doi(self):
from app.modules.dataset.services import DataSetService
Expand Down Expand Up @@ -151,15 +152,16 @@ class File(db.Model):
feature_model_id = db.Column(db.Integer, db.ForeignKey('feature_model.id'), nullable=False)

def get_formatted_size(self):
return get_human_readable_size(self.size)
from app.modules.dataset.services import SizeService
return SizeService().get_human_readable_size(self.size)

def to_dict(self):
return {
'id': self.id,
'name': self.name,
'checksum': self.checksum,
'size_in_bytes': self.size,
'size_in_human_format': get_human_readable_size(self.size),
'size_in_human_format': self.get_formatted_size(),
'url': f'{request.host_url.rstrip("/")}/file/download/{self.id}',
}

Expand Down Expand Up @@ -248,12 +250,7 @@ def __repr__(self):
)


def get_human_readable_size(size):
if size < 1024:
return f'{size} bytes'
elif size < 1024 ** 2:
return f'{round(size / 1024, 2)} KB'
elif size < 1024 ** 3:
return f'{round(size / (1024 ** 2), 2)} MB'
else:
return f'{round(size / (1024 ** 3), 2)} GB'
class DOIMapping(db.Model):
id = db.Column(db.Integer, primary_key=True)
dataset_doi_old = db.Column(db.String(120))
dataset_doi_new = db.Column(db.String(120))
26 changes: 26 additions & 0 deletions app/modules/dataset/repositories.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
from datetime import datetime, timezone
import re
from flask_login import current_user
import unidecode
from typing import Optional

from sqlalchemy import desc, func, or_, any_

from app.modules.dataset.models import (
Author,
DOIMapping,
DSDownloadRecord,
DSMetaData,
DSViewRecord,
Expand Down Expand Up @@ -50,6 +53,21 @@ def total_dataset_views(self) -> int:
max_id = self.model.query.with_entities(func.max(self.model.id)).scalar()
return max_id if max_id is not None else 0

def the_record_exists(self, dataset: DataSet, user_cookie: str):
return self.model.query.filter_by(
user_id=current_user.id if current_user.is_authenticated else None,
dataset_id=dataset.id,
view_cookie=user_cookie
).first()

def create_new_record(self, dataset: DataSet, user_cookie: str) -> DSViewRecord:
return self.create(
user_id=current_user.id if current_user.is_authenticated else None,
dataset_id=dataset.id,
view_date=datetime.now(timezone.utc),
view_cookie=user_cookie,
)


class DataSetRepository(BaseRepository):
def __init__(self):
Expand Down Expand Up @@ -175,3 +193,11 @@ def __init__(self):
def total_feature_model_views(self) -> int:
max_id = self.model.query.with_entities(func.max(self.model.id)).scalar()
return max_id if max_id is not None else 0


class DOIMappingRepository(BaseRepository):
def __init__(self):
super().__init__(DOIMapping)

def get_new_doi(self, old_doi: str) -> str:
return self.model.query.filter_by(dataset_doi_old=old_doi).first()
36 changes: 14 additions & 22 deletions app/modules/dataset/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,21 @@

from core.configuration.configuration import uploads_folder_name
from flask import (
redirect,
render_template,
request,
jsonify,
send_from_directory,
current_app,
make_response,
abort,
url_for,
)
from flask_login import login_required, current_user

from app.modules.dataset.forms import DataSetForm
from app.modules.dataset.models import (
DSDownloadRecord,
DSViewRecord,
DataSet,
File,
FileDownloadRecord,
Expand All @@ -42,6 +43,7 @@
FeatureModelService,
FileService,
FileDownloadRecordService,
DOIMappingService
)
from app.modules.zenodo.services import ZenodoService

Expand All @@ -50,6 +52,8 @@
author_service = AuthorService()
dsmetadata_service = DSMetaDataService()
zenodo_service = ZenodoService()
doi_mapping_service = DOIMappingService()
ds_view_record_service = DSViewRecordService()


@dataset_bp.route("/dataset/upload", methods=["GET", "POST"])
Expand Down Expand Up @@ -480,35 +484,23 @@ def view_file(file_id):
@dataset_bp.route("/doi/<path:doi>/", methods=["GET"])
def subdomain_index(doi):

# Check if the DOI is an old DOI
new_doi = doi_mapping_service.get_new_doi(doi)
if new_doi:
# Redirect to the same path with the new DOI
return redirect(url_for('dataset.subdomain_index', doi=new_doi), code=302)

# Try to search the dataset by the provided DOI (which should already be the new one)
ds_meta_data = dsmetadata_service.filter_by_doi(doi)

if not ds_meta_data:
abort(404)

# Get dataset
dataset = ds_meta_data.data_set

# Get the cookie from the request or generate a new one if it does not exist
user_cookie = request.cookies.get("view_cookie")
if not user_cookie:
user_cookie = str(uuid.uuid4())

# Check if the view record already exists for this cookie
existing_record = DSViewRecord.query.filter_by(
user_id=current_user.id if current_user.is_authenticated else None,
dataset_id=dataset.id,
view_cookie=user_cookie
).first()

if not existing_record:
# Record the view in your database
DSViewRecordService().create(
user_id=current_user.id if current_user.is_authenticated else None,
dataset_id=dataset.id,
view_date=datetime.now(timezone.utc),
view_cookie=user_cookie,
)

# Save the cookie to the user's browser
user_cookie = ds_view_record_service.create_cookie(dataset=dataset)
resp = make_response(render_template("dataset/view_dataset.html", dataset=dataset))
resp.set_cookie("view_cookie", user_cookie)

Expand Down
63 changes: 57 additions & 6 deletions app/modules/dataset/services.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import os
from typing import Optional
import uuid

from app.modules.dataset.models import DataSet, DSMetaData
from flask import request

from app.modules.dataset.models import DSViewRecord, DataSet, DSMetaData
from app.modules.dataset.repositories import (
AuthorRepository,
DOIMappingRepository,
DSDownloadRecordRepository,
DSMetaDataRepository,
DSViewRecordRepository,
Expand Down Expand Up @@ -98,11 +102,6 @@ def filter_by_doi(self, doi: str) -> Optional[DSMetaData]:
return self.repository.filter_by_doi(doi)


class DSViewRecordService(BaseService):
def __init__(self):
super().__init__(DSViewRecordRepository())


class FMMetaDataService(BaseService):
def __init__(self):
super().__init__(FMMetaDataRepository())
Expand All @@ -116,3 +115,55 @@ def __init__(self):
class FileDownloadRecordService(BaseService):
def __init__(self):
super().__init__(FileDownloadRecordRepository())


class DOIMappingService(BaseService):
def __init__(self):
super().__init__(DOIMappingRepository())

def get_new_doi(self, old_doi: str) -> str:
doi_mapping = self.repository.get_new_doi(old_doi)
if doi_mapping:
return doi_mapping.dataset_doi_new
else:
return None


class DSViewRecordService(BaseService):
def __init__(self):
super().__init__(DSViewRecordRepository())

def the_record_exists(self, dataset: DataSet, user_cookie: str):
return self.repository.the_record_exists(dataset, user_cookie)

def create_new_record(self, dataset: DataSet, user_cookie: str) -> DSViewRecord:
return self.repository.create_new_record(dataset, user_cookie)

def create_cookie(self, dataset: DataSet) -> str:

user_cookie = request.cookies.get("view_cookie")
if not user_cookie:
user_cookie = str(uuid.uuid4())

existing_record = self.the_record_exists(dataset=dataset, user_cookie=user_cookie)

if not existing_record:
self.create_new_record(dataset=dataset, user_cookie=user_cookie)

return user_cookie


class SizeService():

def __init__(self):
pass

def get_human_readable_size(self, size: int) -> str:
if size < 1024:
return f'{size} bytes'
elif size < 1024 ** 2:
return f'{round(size / 1024, 2)} KB'
elif size < 1024 ** 3:
return f'{round(size / (1024 ** 2), 2)} MB'
else:
return f'{round(size / (1024 ** 3), 2)} GB'
1 change: 0 additions & 1 deletion migrations/alembic.ini
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
# the 'revision' command, regardless of autogenerate
# revision_environment = false


# Logging configuration
[loggers]
keys = root,sqlalchemy,alembic,flask_migrate
Expand Down
43 changes: 43 additions & 0 deletions migrations/versions/4ce46845a4b0_.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
"""empty message
Revision ID: 4ce46845a4b0
Revises:
Create Date: 2024-07-08 12:38:55.095000
"""
from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision = '4ce46845a4b0'
down_revision = '97d142f05ca0'
branch_labels = None
depends_on = None


def upgrade():
conn = op.get_bind()
inspector = sa.Inspector.from_engine(conn)

if 'doi_mapping' not in inspector.get_table_names():
op.create_table('doi_mapping',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('dataset_doi_old', sa.String(length=120), nullable=True),
sa.Column('dataset_doi_new', sa.String(length=120), nullable=True),
sa.PrimaryKeyConstraint('id')
)

if 'webhook' not in inspector.get_table_names():
op.create_table('webhook',
sa.Column('id', sa.Integer(), nullable=False),
sa.PrimaryKeyConstraint('id')
)
# ### end Alembic commands ###


def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table('webhook')
op.drop_table('doi_mapping')
# ### end Alembic commands ###
Loading

0 comments on commit 19143b8

Please sign in to comment.