diff --git a/assets/src/components/DetailCard.js b/assets/src/components/DetailCard.js index c7513d66..a62db350 100644 --- a/assets/src/components/DetailCard.js +++ b/assets/src/components/DetailCard.js @@ -1,360 +1,368 @@ import React, {useContext,useEffect} from "react" import {Context} from "../Context" import { makeStyles } from '@material-ui/core/styles'; import "./detailscard.css" import Typography from '@material-ui/core/Typography'; import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; import Card from '@material-ui/core/Card'; import CardActions from '@material-ui/core/CardActions'; import CardContent from '@material-ui/core/CardContent'; import Button from '@material-ui/core/Button'; import { HiLink } from "react-icons/hi"; import DoneIcon from '@material-ui/icons/Done'; import WarningIcon from '@material-ui/icons/Warning'; import "./termcard.css" import { BsUnlock } from "react-icons/bs"; import { GrDiamond } from "react-icons/gr"; import Chip from '@material-ui/core/Chip'; import ClearSharpIcon from '@material-ui/icons/ClearSharp'; const useStyles = makeStyles((theme) => ({ root: { '& > *': { margin: theme.spacing(1), display: 'grid', }, flexGrow: 1, }, formControl: { margin: theme.spacing(1), width: 200, }, selectEmpty: { marginTop: theme.spacing(1), }, chip: { margin: 0.5, }, heading: { fontSize: theme.typography.pxToRem(15), fontWeight: theme.typography.fontWeightRegular, }, })); function DetailsCard({details}) { const { getSelectedInstitId, setUrl, url } = useContext(Context) const classes = useStyles(); useEffect(() => { setUrl(window.location.href) },) if (details !== 'null') { // // setInstitName(details.name) return (
{details.end_year && Inactive! {details.starting_year &&

From {details.starting_year} to {details.end_year}

}
}

{details.name}

    {details.issn && details.issn.map(item => { return ( item.issn_type === "1" ?
  • Print ISSN: {item.issn}
  • : item.issn_type === "2" ?
  • Electronic ISSN: {item.issn}
  • : item.issn_type === "3" ?
  • Other ISSN: {item.issn}
  • :null ) })}
{details.publisher && details.publisher.map(item => { return
})}
{details.country && details.country.map(item => { return
{item.name}
; })} {!details.end_year &&
Since {details.starting_year}
}
{details.oa_status ?
{details.oa_status.status !== "UNKNOWN" ?
{ details.oa_status.status === "Gold" ?

Open Acces Status:

: details.oa_status.status === "Diamond" ? <> Open Acces Status: {/* {details.oa_status.status} */} :details.oa_status.status === "Full" ?

Open Acces Status:

:details.oa_status.status === "hybrid" ?

Open Acces Status:

:details.oa_status.status === "Green" ?

Open Acces Status:

:details.oa_status.status === "none" ?

Open Acces Status:

:null }
:null}
:null}
{details.language && details.language.map(item =>(

Language: {item.name}

)) } {details.doaj_seal &&
} {details.doaj_status &&
} {details.lockss &&
} {details.portico &&
} {details.nlch &&
} {details.qoam_av_score && +
- +
+
} {details.ror ?
- +
:null} {details.fundref ?
- +
:null} +
{details.website ? :null} {details.oa_options ? :null} + {details.ir_name ? +
+ +
+ :null}
) } else { return null } } export default DetailsCard \ No newline at end of file diff --git a/assets/src/pages/About.js b/assets/src/pages/About.js index 5d8fbeea..c17fff75 100644 --- a/assets/src/pages/About.js +++ b/assets/src/pages/About.js @@ -1,9 +1,32 @@ import React from "react" function About () { return ( -

About page

+
+

OACCT – About (draft!)

+ +
+The OACCT (Open Access Compliance Check Tool) is an online resource, tailored to the Swiss academic community's needs, that gathers the most important information concerning Open-Access publishing. Its principal goal is to guide Swiss researchers in deciding where and how to publish their works in compliance with funders’ and institutional Open Access policies. OACCT provides a list of journals with information aggregated from several sources on a regular basis: + +
+ +

Data reuse & licence

+
+Please see our terms of use to learn how the data provided by our service may be reused. +
+ +

Financing

+
+The OACCT project was co-financed by swissuniversities within the P5 Program “Scientific information: Access, processing and safeguarding” and developed by the university libraries of EPFL and Université de Genève with the support of the Universitätsbibliothek Bern and Université de Lausanne +
+
) } export default About \ No newline at end of file diff --git a/assets/src/pages/Help.js b/assets/src/pages/Help.js index 850574b9..0b4200c8 100644 --- a/assets/src/pages/Help.js +++ b/assets/src/pages/Help.js @@ -1,9 +1,31 @@ import React from "react" -function Help () { +function Help() { return ( -

Help page

+
+

OACCT – Help (draft!)

+

How to use the tool

+
+A database search can be performed by using three search boxes (Institution, funder and journal). The search results contain the following information: + +
+
+API info to be added (maybe on a different page) +
+

Data reuse & licence

+
+Please see our terms of use to obtain information about how the data provided by this service may be reused. +
+ +
) } export default Help \ No newline at end of file diff --git a/assets/src/pages/welcome.js b/assets/src/pages/welcome.js index 95d1bf93..5ba95829 100644 --- a/assets/src/pages/welcome.js +++ b/assets/src/pages/welcome.js @@ -1,55 +1,111 @@ import React from "react" import "./welcome.css" import Container from '@material-ui/core/Container' import Grid from '@material-ui/core/Grid' +import Paper from '@material-ui/core/Paper'; import Box from '@material-ui/core/Box' import Link from '@material-ui/core/Link' +import { makeStyles } from '@material-ui/core/styles'; + +const useStyles = makeStyles((theme) => ({ + root: { + flexGrow: 1, + }, + paper: { + padding: theme.spacing(2), + textAlign: 'left', + color: 'balck', + backgroundColor:" #F8F8F8", + // height: 400, + // width: 400, + }, + // grid: { + + + // } + })) + function Welcome () { +const classes = useStyles(); return ( -
- -{/* */} -
-
- {/* */} -
-
-
-
-
-

Welcome!

-

Let's try the beta version!

-
-
-
-
-
-
-
-

Free to use!

-

Any questions? please contact us!

- contact -
-
-
-
-
-
-
-

Need help!

-

Any questions? please call us?

- contact -
-
-
- - -
-
-
+ +
+ + + + + +

Welcome!

+

The OACCT (Open Access Compliance Check Tool) is an online resource, tailored to the Swiss academic community's needs, that gathers the most important information concerning Open-Access publishing.

+
+
+
+ + + + +

Mission

+

+ + Its principal goal is to guide Swiss researchers in deciding where and how to publish their works in compliance with funders’ and institutional Open Access policies

+ + +
+ +
+
+ + + + +

Where do our data come from?

+ + +

OACCT provides a list of journals with information aggregated from several sources on a regular basis: +

+
+

+

    +
  • Journal ISSNs (source: ISSN International centre)
  • +
  • Publication conditions (source: Sherpa/Romeo)
  • +
  • Swiss institutions from swissuniversities
  • +
  • Funders from… + To be completed
  • +
+

+
+ +
+
+
+
+ + +
) } export default Welcome \ No newline at end of file diff --git a/django_api/admin.py b/django_api/admin.py index c819e02e..71630311 100644 --- a/django_api/admin.py +++ b/django_api/admin.py @@ -1,374 +1,385 @@ from django.contrib import admin from django import forms from datetime import date +import re from import_export.admin import ImportExportModelAdmin from django.contrib.admin import TabularInline from django.contrib.admin import SimpleListFilter from django.contrib.admin import RelatedOnlyFieldListFilter from django.utils.translation import gettext_lazy as _ #from inline_actions.admin import InlineActionsMixin #from inline_actions.admin import InlineActionsModelAdminMixin from django.shortcuts import render from django.http import HttpResponseRedirect from django.urls import reverse from django.utils.html import escape, mark_safe, format_html from .models import Country from .models import Language from .models import Issn from .models import Oa from .models import Publisher from .models import Journal from .models import Organization from .models import Version from .models import Licence from .models import Cost_factor_type from .models import Cost_factor from .models import Term from .models import ConditionType from .models import ConditionSet from .models import OrganizationCondition from .models import JournalCondition # Register your models here. @admin.register(Issn) class IssnAdmin(ImportExportModelAdmin): - list_filter = ('issn_type', ) + list_filter = ('issn_type', 'journal__publisher__name', ) + list_display = ("id", "issn", 'journal') class IssnInline(admin.TabularInline): model = Issn readonly_fields = ('issn', 'issn_type',) # This Inline is stricty read-only for the moment def has_change_permission(self, request, obj=None): return False def has_add_permission(self, request, obj=None): return False def has_delete_permission(self, request, obj=None): return False @admin.register(Journal) class JournalAdmin(ImportExportModelAdmin): list_display = ("id", "name", "get_journal_issns",) list_filter = ('publisher__name', 'oa_status') filter_horizontal = ('publisher', 'language', ) inlines = (IssnInline, ) def get_journal_issns(self, obj): return list(Issn.objects.filter(journal=obj)) get_journal_issns.short_description = 'ISSNs' @admin.register(Language) class LanguageAdmin(ImportExportModelAdmin): pass @admin.register(Organization) class OrganizationAdmin(ImportExportModelAdmin): list_display = ("id", "name") list_filter = ('is_funder',) filter_horizontal = ('country', ) @admin.register(Version) class VersionAdmin(ImportExportModelAdmin): pass @admin.register(Country) class CountryAdmin(ImportExportModelAdmin): pass @admin.register(Oa) class OaAdmin(ImportExportModelAdmin): pass @admin.register(Publisher) class PublisherAdmin(ImportExportModelAdmin): list_display = ("id", "name") # Experimental: what will happen with 200+ countries in the database? list_filter = ('country__name', ) filter_horizontal = ('country', ) @admin.register(Term) class TermAdmin(ImportExportModelAdmin): list_filter = ('version', 'licence') filter_horizontal = ('version', 'cost_factor', 'licence', ) # textarea input is better for comments def get_form(self, request, obj=None, **kwargs): kwargs['widgets'] = {'comment': forms.Textarea} return super().get_form(request, obj, **kwargs) @admin.register(ConditionType) class ConditionTypeAdmin(ImportExportModelAdmin): list_display = ("id", "condition_issuer") #class JournalConditionInline(InlineActionsMixin, TabularInline): class JournalConditionInline(TabularInline): model = JournalCondition extra = 1 inline_actions = ['connect_all_journals'] def connect_all_journals(self, request, obj, parent_obj=None): # Do stuff here, then return None to go to current view return None connect_all_journals.short_description = ("Connect Condition Set with some or all Journals") class OrganizationConditionInline(TabularInline): #class OrganizationConditionInline(InlineActionsMixin, TabularInline): model = OrganizationCondition extra = 1 def connect_with_all_journals(modeladmin, request, queryset): # print(request.POST) if request.POST.get('apply'): try: valid_from = date.fromisoformat(request.POST['valid_from']) valid_until = date.fromisoformat(request.POST['valid_until']) - issn_list = [x for x in request.POST['issn_list'].replace(',','\n',).replace(';','\n').replace(' ','\n').split('\n') if len(x) > 0] + #issn_list = [x for x in request.POST['issn_list'].replace(',','\n',).replace(';','\n').replace(' ','\n').replace('\r','\n',).split('\n') if len(x) > 0] + issn_list = set([x for x in re.split(' |,|;|\n|\r|\t', request.POST['issn_list']) if len(x) > 0]) print(issn_list) if valid_from > valid_until: raise ValueError # print((valid_from, valid_until)) if len(issn_list) == 0: all_journals = Journal.objects.all() else: - all_journals = Journal.objects.filter(issn__issn__in=issn_list).distinct() + # all_journals = [issn.journal for issn in Issn.objects.filter(issn__in=issn_list).distinct()] + journal_ids = list(Issn.objects.filter(issn__in=issn_list).values_list('journal', flat=True).distinct()) + # print(journal_ids) + all_journals = Journal.objects.filter(id__in=journal_ids) print(all_journals) print(len(issn_list), len(all_journals)) - all_journals =[] + # all_journals =[] + # The following block could certainly be optimized! AB 2021-08-12 for condition_set in queryset: # print('-----------------') # print(condition_set) for j in all_journals: # print(j) # search for existing connections existing_connections = JournalCondition.objects.filter(journal=j, condition_set=condition_set, valid_from__lt=date.today(), valid_until__gt=date.today()) # print(existing_connections) if len(existing_connections) == 0: new_journal_condition = JournalCondition(journal=j, condition_set=condition_set, valid_from=valid_from, valid_until=valid_until) new_journal_condition.save() + else: + # This should not happen, or could it? + print(f'{j} already connected with {condition_set}') return None except ValueError: pass return render(request, 'admin/get_validity_dates.html', context={'queryset': queryset, 'objects': 'journals'}) -connect_with_all_journals.short_description = 'Apply selected condition sets to all Journals' +connect_with_all_journals.short_description = 'Apply selected condition sets to multiple Journals' + def connect_with_all_organizations(modeladmin, request, queryset): if request.POST.get('apply'): try: valid_from = date.fromisoformat(request.POST['valid_from']) valid_until = date.fromisoformat(request.POST['valid_until']) if valid_from > valid_until: raise ValueError # print((valid_from, valid_until)) all_orgs = Organization.objects.all() for condition_set in queryset: # print('-----------------') # print(condition_set) for o in all_orgs: # print(o) # search for existing connections existing_connections = OrganizationCondition.objects.filter(organization=o, condition_set=condition_set, valid_from__lt=date.today(), valid_until__gt=date.today()) # print(existing_connections) if len(existing_connections) == 0: new_organization_condition = OrganizationCondition(organization=o, condition_set=condition_set, valid_from=valid_from, valid_until=valid_until) new_organization_condition.save() return None except ValueError: pass return render(request, 'admin/get_validity_dates.html', context={'queryset': queryset, 'objects': 'organizations'}) connect_with_all_organizations.short_description = 'Apply selected condition sets to all Organizations' def end_validity(modeladmin, request, queryset): # print(request.POST) if request.POST.get('apply'): try: valid_until = date.fromisoformat(request.POST['date']) print(valid_until) for x_condition in queryset: x_condition.valid_until = valid_until x_condition.save() return None except ValueError: pass return render(request, 'admin/get_single_validity_date.html', context={'queryset': queryset, 'limit': 'end', 'objects': 'selected journal-condition connections'}) end_validity.short_description = 'Set valid_until date' @admin.register(ConditionSet) class ConditionSetAdmin(ImportExportModelAdmin): # class ConditionSetAdmin(InlineActionsModelAdminMixin, ImportExportModelAdmin): list_display = ("id", "condition_type", "comment") search_fields = ['organization__name', 'journal__name', 'comment'] list_filter = ('condition_type', ) filter_horizontal = ('term', ) inlines = (OrganizationConditionInline, JournalConditionInline, ) actions = [connect_with_all_journals, connect_with_all_organizations] # textarea input is better for comments def get_form(self, request, obj=None, **kwargs): kwargs['widgets'] = {'comment': forms.Textarea} return super().get_form(request, obj, **kwargs) class XConditionValidListFilter(SimpleListFilter): # Human-readable title which will be displayed in the # right admin sidebar just above the filter options. title = _('currently valid') # Parameter for the filter that will be used in the URL query. parameter_name = 'valid' def lookups(self, request, model_admin): """ Returns a list of tuples. The first element in each tuple is the coded value for the option that will appear in the URL query. The second element is the human-readable name for the option that will appear in the right sidebar. """ return ( ('true', _('True')), ('false', _('False')), ) def queryset(self, request, queryset): """ Returns the filtered queryset based on the value provided in the query string and retrievable via `self.value()`. """ # Compare the requested value (either '80s' or '90s') # to decide how to filter the queryset. if self.value() == 'true': return queryset.filter(valid_from__lte=date.today(), valid_until__gte=date.today()) if self.value() == 'false': return queryset.exclude(valid_from__lte=date.today(), valid_until__gte=date.today()) class ConditionSetListDynamicFilter(SimpleListFilter): title = _('condition sets (publisher-dependant)') parameter_name = 'condition_set' def lookups(self, request, model_admin): if 'journal__publisher__name' in request.GET: # A publisher name filter is in effect journal_publisher_name = request.GET['journal__publisher__name'] condition_sets = set([c.condition_set for c in model_admin.model.objects.all().filter(journal__publisher__name=journal_publisher_name)]) else: condition_sets = set([c.condition_set for c in model_admin.model.objects.all()]) return [(s.id, str(s)) for s in condition_sets] def queryset(self, request, queryset): if self.value(): return queryset.filter(condition_set__id__exact=self.value()) @admin.register(OrganizationCondition) class OrganizationConditionAdmin(ImportExportModelAdmin): def link_to_conditionset(self, obj): link = reverse("admin:django_api_conditionset_change", args=[obj.condition_set.id]) return format_html(f'{obj.condition_set}') link_to_conditionset.short_description = 'Condition Set' #list_display = ("id", "organization_name", "condition_set", "valid_from", "valid_until") list_display = ("id", "organization_name", "link_to_conditionset", "valid_from", "valid_until") list_filter = ('condition_set__condition_type', XConditionValidListFilter, ('condition_set', RelatedOnlyFieldListFilter)) def organization_name(self, obj): return obj.organization.name @admin.register(Licence) class LicenceAdmin(ImportExportModelAdmin): pass @admin.register(JournalCondition) class JournalConditionAdmin(ImportExportModelAdmin): def link_to_conditionset(self, obj): link = reverse("admin:django_api_conditionset_change", args=[obj.condition_set.id]) return format_html(f'{obj.condition_set}') link_to_conditionset.short_description = 'Condition Set' list_display = ("id", "journal_name", "link_to_conditionset", "valid_from", "valid_until") list_filter = ('condition_set__condition_type', XConditionValidListFilter, 'journal__publisher__name', ConditionSetListDynamicFilter) actions =(end_validity,) def journal_name(self, obj): return obj.journal.name # unsuccessful attempt # def formfield_for_foreignkey(self, db_field, request, **kwargs): # if db_field.name == "journal": # kwargs["queryset"] = Journal.objects.filter(publisher__name__in=Publisher.objects.order_by().values('name').distinct()) # return super().formfield_for_foreignkey(db_field, request, **kwargs) @admin.register(Cost_factor) class Cost_factorAdmin(ImportExportModelAdmin): list_display = ("id", "comment", "amount", "symbol") list_filter = ('cost_factor_type', 'symbol') # textarea input is better for comments def get_form(self, request, obj=None, **kwargs): kwargs['widgets'] = {'comment': forms.Textarea} return super().get_form(request, obj, **kwargs) @admin.register(Cost_factor_type) class Cost_factor_typeAdmin(ImportExportModelAdmin): list_display = ("id", "name") diff --git a/templates/admin/get_validity_dates.html b/templates/admin/get_validity_dates.html index 89e8b2a5..70911684 100644 --- a/templates/admin/get_validity_dates.html +++ b/templates/admin/get_validity_dates.html @@ -1,35 +1,37 @@ {% extends "admin/base_site.html" %} {% block content %}
{% csrf_token %}

Enter the validity dates for the selected conditions:

From:
Until:
{% if objects == 'journals' %}

- Applicable to the following ISSNs (leave blank for all journals): +
+ Applicable to the following ISSNs (unknown items are ignored; leave completely blank for all journals):

- +
{% endif %} {% for obj in queryset %} {% endfor %} - +
{% endblock %} \ No newline at end of file