diff --git a/assets/src/App.js b/assets/src/App.js
index b61db5cc..20af220f 100644
--- a/assets/src/App.js
+++ b/assets/src/App.js
@@ -1,62 +1,62 @@
import React from "react";
import "./App.css"
import SearchFilterFields from './pages/SearchFilterFields'
import Footer from './components/layout/Footer'
import About from "./pages/About"
import Help from "./pages/Help"
import {Switch, Route,} from "react-router-dom"
import Container from '@material-ui/core/Container';
import Box from '@material-ui/core/Box'
import Nav from "./components/layout/Nav"
import { makeStyles } from '@material-ui/core/styles';
const useStyles = makeStyles((theme) => ({
root: {
flexGrow: 1,
}
}));
function App () {
const classes = useStyles();
return(
{
- window.location.href = 'https://oacct-dev.epfl.ch/api/';
+ window.location.href = '/api/';
return null;
}}/>
{
- window.location.href = 'https://oacct-dev.epfl.ch/admin/';
+ window.location.href = '/admin/';
return null;
}}/>
)
}
export default App
\ No newline at end of file
diff --git a/assets/src/components/ResultCard.js b/assets/src/components/ResultCard.js
index 8f403477..1b86ad00 100644
--- a/assets/src/components/ResultCard.js
+++ b/assets/src/components/ResultCard.js
@@ -1,171 +1,171 @@
import React from "react"
import { makeStyles } from '@material-ui/core/styles';
import "./ResultCard.css"
import TermCard from "../components/TermCard"
import Accordion from '@material-ui/core/Accordion';
import AccordionSummary from '@material-ui/core/AccordionSummary';
import AccordionDetails from '@material-ui/core/AccordionDetails';
import Typography from '@material-ui/core/Typography';
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
import Badge from '@material-ui/core/Badge';
import { HiOutlineDocumentDuplicate } from "react-icons/hi";
import { HiOutlineDocument } from "react-icons/hi";
import Grid from '@material-ui/core/Grid'
import Box from '@material-ui/core/Box'
import Container from '@material-ui/core/Container';
const useStyles = makeStyles((theme) =>({
root: {
flexGrow: 1,
},
chip: {
margin: 0.5,
},
}))
function ResultCard({result}) {
const classes = useStyles();
//create one array to organize the frontend output
const termresult = []
const termArray = result?.map(i=>(
// get condition details
i.term?.map(j =>(
// termresult.push([j, i.id, i.comment, i.condition_type])
termresult.push([j, [i.id, i.comment, i.condition_type]])
))
))
//groupyBy array
function groupBy(objectArray, property) {
// console.log(objectArray)
return objectArray.reduce((acc, obj) => {
// console.log(obj[0])
const key = obj[0][property][0].description;
if (!acc[key]) {
acc[key] = [];
}
// Add object to list for given key's value
acc[key].push(obj);
return acc;
}, {});
}
const groupedTerm = groupBy(termresult, 'version')
//first version
// console.log(groupedTerm[1])
//convert object into array
const termItem = Object.entries(groupedTerm)
//manage the display order
function orderVersion (version) {
if (version[0] ==="Submitted version") {
version.unshift(3)
}
else if (version[0] === "Published version") {
version.unshift(1)
}
else if (version[0] === "Accepted version") {
version.unshift(2)
}
}
//apply the function for each version
termItem?.map(i=>(
orderVersion(i)
))
termItem.sort()
console.log(termItem)
const displayVersion =
termItem?.map(item =>(
}
aria-controls="panel1a-content"
id="panel1a-header"
>
{item?.map(i => (
{typeof i === "string" && i} {typeof i === "object" && i.length > 1 ? : typeof i === "object" && }
))
}
{item?.map(j => (
typeof j === "object" &&
j?.map(k => (
))
))
}
))
return (
//level 0
{displayVersion}
)
}
export default ResultCard
\ No newline at end of file
diff --git a/assets/src/components/TermCard.js b/assets/src/components/TermCard.js
index 22bafec4..e1e7ce0d 100644
--- a/assets/src/components/TermCard.js
+++ b/assets/src/components/TermCard.js
@@ -1,232 +1,242 @@
import React from "react"
import { makeStyles } from '@material-ui/core/styles';
import "./termcard.css"
import Chip from '@material-ui/core/Chip';
import DoneIcon from '@material-ui/icons/Done';
import HighlightOffIcon from '@material-ui/icons/HighlightOff';
import Accordion from '@material-ui/core/Accordion';
import AccordionSummary from '@material-ui/core/AccordionSummary';
import AccordionDetails from '@material-ui/core/AccordionDetails';
import Typography from '@material-ui/core/Typography';
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
import DateRangeIcon from '@material-ui/icons/DateRange';
import { FaCoins } from 'react-icons/fa';
import { RiFilePaper2Line } from 'react-icons/ri';
import Button from '@material-ui/core/Button';
import { HiLink } from "react-icons/hi";
import { GrInfo } from "react-icons/gr";
import Container from '@material-ui/core/Container'
import Grid from '@material-ui/core/Grid'
-import Box from '@material-ui/core/Box'
+import Card from '@material-ui/core/Card';
+import CardActions from '@material-ui/core/CardActions';
+import CardContent from '@material-ui/core/CardContent';
const useStyles = makeStyles((theme) =>({
card: {
width: '100%',
marginTop: "1rem",
textAlign:'left'
},
root: {
flexGrow: 1,
textAlign:'left',
},
chip: {
margin: 0.5,
},
heading: {
fontSize: theme.typography.pxToRem(15),
fontWeight: theme.typography.fontWeightRegular,
},
}))
function TermCard({term}) {
const classes = useStyles();
const licenceIcon =
term[0].licence?.map(i=>(
<>
>
))
function cost() {
const price = term[0].cost_factor?.map(i => (
i.amount
))
const price_symbol = term[0].cost_factor?.map(j => (
j.symbol
))
const cost_name = term[0].cost_factor?.map(k => (
k.cost_factor_type.name
))
const id = term[0].cost_factor?.map(k => (
k.cost_factor_type.id
))
return (
}
label={cost_name[0] + " : " + price[0] + " " + price_symbol}
// clickable={handleClick}
// color="secondary"
// onDelete={handleDelete}
className={classes.chip}
style={{ background: "#FFFFFF"}}
title="This is more information"
/>
)
}
const termArchive = term[0].ir_archiving && term[0].ir_archiving ? (
}
label={"IR Archiving"}
// variant="outlined"
// clickable={handleClick}
// color="secondary"
// onDelete={handleDelete}
style={{ background: "#FFFFFF"}}
title="This is more information"
/>
): (
}
label="IR Archiving"
// clickable={handleClick}
// color="secondary"
// onDelete={handleDelete}
style={{ background: "#f50057"}}
title="This is more information"
/>
)
const embargo = term[0].ir_archiving && term[0].ir_archiving ? (
}
label={"Embargo: " + term[0].embargo_months + " Month(s)"}
// variant="outlined"
// clickable={handleClick}
// color="secondary"
// onDelete={handleDelete}
style={{ background: "#FFFFFF"}}
title="This is more information"
/>
):
()
return (
-
-
-
- }
- aria-controls="panel1a-content"
- id="panel1a-header"
- >
-
+
+
+
+ {term.map( j=> (
+ j[1] &&
+ Condition set Title: {j[1]}
+ ))
+ }
Term ID: {term[0].id}
{term.map( j=> (
j[0] &&
From condition set Id: {j[0]}
))
}
+
+
+
+
+
+
+
+
+
+
+
+
{cost()}
{licenceIcon}
{termArchive}
{embargo}
-
-
-
-
-
-
- {term[0].comment ?
+
+
+
+
+ {term[0].comment ?
:null}
{term[0].source ?
-
+
:null}
+
+
+
-
-
-
-
)
}
export default TermCard
\ No newline at end of file
diff --git a/django_api/admin.py b/django_api/admin.py
index f279f4f1..880cb8e9 100644
--- a/django_api/admin.py
+++ b/django_api/admin.py
@@ -1,237 +1,237 @@
from django.contrib import admin
from datetime import date
from import_export.admin import ImportExportModelAdmin
from django.contrib.admin import TabularInline
#from inline_actions.admin import InlineActionsMixin
#from inline_actions.admin import InlineActionsModelAdminMixin
from django.shortcuts import render
from django.http import HttpResponseRedirect
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(Journal)
class JournalAdmin(ImportExportModelAdmin):
list_display = ("id", "name", "journal_issns",)
list_filter = ('publisher__name', 'oa_status')
filter_horizontal = ('publisher', 'language', )
def journal_issns(self, obj):
# TBD
pass
@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(Issn)
class IssnAdmin(ImportExportModelAdmin):
list_filter = ('issn_type', )
@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', )
@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 all Journals")
class OrganizationConditionInline(TabularInline):
#class OrganizationConditionInline(InlineActionsMixin, TabularInline):
model = OrganizationCondition
extra = 1
def connect_with_all_journals(modeladmin, request, queryset):
- print(request.POST)
+ # 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'])
if valid_from > valid_until:
raise ValueError
# print((valid_from, valid_until))
all_journals = Journal.objects.all()
for condition_set in queryset:
# print('-----------------')
# print(condition_set)
for j in all_journals:
- print(j)
+ # 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()
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'
def connect_with_all_organizations(modeladmin, request, queryset):
- print(request.POST)
+ # 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'])
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)
+ # 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'
@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]
@admin.register(OrganizationCondition)
class OrganizationConditionAdmin(ImportExportModelAdmin):
list_display = ("id", "organization_name", "condition_set", "valid_from", "valid_until")
list_filter = ('condition_set__condition_type', )
def organization_name(self, obj):
return obj.organization.name
@admin.register(Licence)
class LicenceAdmin(ImportExportModelAdmin):
pass
@admin.register(JournalCondition)
class JournalConditionAdmin(ImportExportModelAdmin):
list_display = ("id", "journal_name", "condition_set", "valid_from", "valid_until")
list_filter = ('condition_set__condition_type', )
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')
@admin.register(Cost_factor_type)
class Cost_factor_typeAdmin(ImportExportModelAdmin):
list_display = ("id", "name")
diff --git a/django_api/models.py b/django_api/models.py
index 2e0c90be..602bbe5c 100644
--- a/django_api/models.py
+++ b/django_api/models.py
@@ -1,212 +1,218 @@
from django.db import models
from django.contrib.auth.models import User
import datetime
from django.utils.translation import gettext as _
# Ref: database_model_20210421_MB.drawio 21.04.2021
class Country(models.Model):
name = models.CharField(verbose_name="Country name", max_length=120, null=True)
iso_code = models.CharField(max_length=3, null=True)
def __str__(self):
return f"{self.name}"
class Meta:
ordering = ('name',)
class Language(models.Model):
name = models.CharField(verbose_name="Language name", max_length=120, null=True)
iso_code = models.CharField(max_length=3, null=True)
def __str__(self):
return f"{self.name}"
class Meta:
ordering = ('name',)
class Oa(models.Model):
status = models.CharField(max_length=1000, null=True)
description = models.CharField(max_length=1000, null=True)
subscription = models.BooleanField(default=False)
accepted_manuscript = models.BooleanField(default=False)
apc = models.BooleanField(default=False)
final_version = models.BooleanField(default=False)
def __str__(self):
return f"{self.status}"
class Meta:
ordering = ('-subscription',)
class Publisher(models.Model):
name = models.CharField(verbose_name="Publisher name", max_length=1000, null=True)
city = models.CharField(max_length=100, null=True)
state = models.CharField(max_length=3, null=True)
country = models.ManyToManyField("Country")
starting_year = models.IntegerField()
website = models.URLField(max_length=1000)
oa_policies = models.URLField(max_length=1000)
def __str__(self):
return f"{self.name}"
class Meta:
ordering = ('name',)
class Journal(models.Model):
name = models.CharField(verbose_name="Journal name", max_length=800, blank=True, null=True) # search journal with name
name_short_iso_4 = models.CharField(max_length=300, blank=True, null=True)
publisher = models.ManyToManyField(Publisher)
website = models.URLField(max_length=300, blank=True, null=True)
language = models.ManyToManyField(Language)
# issn = models.ManyToManyField("Issn") # search journal with ISSN
oa_options = models.URLField(max_length=1000, blank=True, null=True)
oa_status = models.ForeignKey("Oa", on_delete=models.CASCADE)
starting_year = models.IntegerField(blank=True, null=True)
def __str__(self):
return f"{self.name} from {self.website}"
class Meta:
ordering = ('name',)
class Issn(models.Model):
PRINT = 1
ELECTRONIC = 2
OTHER = 3
TYPE_CHOICES = (
(PRINT, 'Print'),
(ELECTRONIC, 'Electronic'),
(OTHER, 'Other'),
)
issn = models.CharField(max_length=9, null=False)
journal = models.ForeignKey(Journal, on_delete=models.CASCADE, blank=True, null=True) # search journal with ISSN
issn_type = models.CharField(
choices=TYPE_CHOICES,
max_length=10,
blank=True
)
def __str__(self):
return f"{self.issn} ({dict(self.TYPE_CHOICES)[int(self.issn_type)]})"
class Meta:
ordering = ('issn',)
class Organization(models.Model):
name = models.CharField(verbose_name="Organization name", max_length=600, null=True)
website = models.URLField(max_length=600, blank=True, null=True)
country = models.ManyToManyField("Country")
ror = models.CharField(max_length=255, blank=True, null=True)
fundref = models.CharField(max_length=255, blank=True, null=True)
starting_year = models.IntegerField(blank=True, null=True)
is_funder = models.BooleanField(default=False)
def __str__(self):
return f"{self.name}"
class Meta:
ordering = ('name',)
class Version(models.Model):
description = models.CharField(max_length=300, null=False)
def __str__(self):
return f"{self.description}"
class Licence(models.Model):
name_or_abbrev = models.CharField(max_length=300, null=False)
website = models.URLField(max_length=600, null=True)
def __str__(self):
return f"{self.name_or_abbrev}"
class Cost_factor_type(models.Model):
name = models.CharField(max_length=300, null=False)
def __str__(self):
return f"{self.name}"
class Cost_factor(models.Model):
cost_factor_type = models.ForeignKey(Cost_factor_type, on_delete=models.CASCADE, blank=True, null=True)
amount = models.IntegerField(null=False)
symbol = models.CharField(max_length=10, null=False)
comment = models.CharField(max_length=120, default="")
def __str__(self):
return f"{self.amount} {self.symbol}"
class Term(models.Model):
version = models.ManyToManyField(Version)
cost_factor = models.ManyToManyField(Cost_factor)
embargo_months = models.IntegerField(blank=True, null=True)
ir_archiving = models.BooleanField(default=False)
licence = models.ManyToManyField(Licence)
comment = models.CharField(max_length=600, null=True)
source = models.URLField(max_length=600, null=True)
def __str__(self):
- # TODO fix this when the actual data become available
- return f"{self.id} about {';'.join([x.description for x in self.version.all()])} - {self.comment}"
- # return f"{self.id} - {self.comment}"
+ # TODO why does this break JSON uploads in the admin interface?
+ # return f"{self.id} about {';'.join([x.description for x in self.version.all()])} - {self.comment}"
+ return f"{self.id} - {self.comment}"
+
+ class Meta:
+ ordering = ('-ir_archiving','embargo_months', 'comment')
class ConditionType(models.Model):
condition_issuer = models.CharField(max_length=300, null=False)
def __str__(self):
return f"{self.condition_issuer}"
class ConditionSet(models.Model):
condition_type = models.ForeignKey(ConditionType, on_delete=models.CASCADE, blank=True, null=True)
organization = models.ManyToManyField(
Organization,
through='OrganizationCondition',
through_fields=('condition_set', 'organization')
)
journal = models.ManyToManyField(
Journal,
through='JournalCondition',
through_fields=('condition_set', 'journal')
)
term = models.ManyToManyField(Term)
comment = models.CharField(max_length=100, null=True, default=":-)")
def __str__(self):
return f"{self.id} {self.condition_type}|{self.comment}"
+ class Meta:
+ ordering = ('-condition_type__pk','comment')
+
class OrganizationCondition(models.Model):
organization = models.ForeignKey(Organization, on_delete=models.CASCADE, blank=True, null=True)
condition_set = models.ForeignKey(ConditionSet, on_delete=models.CASCADE, blank=True, null=True)
valid_from = models.DateField(blank=True, null=True)
valid_until = models.DateField(blank=True, null=True)
def __str__(self):
return f"{self.id} {self.organization}/{self.condition_set}"
class JournalCondition(models.Model):
journal = models.ForeignKey(Journal, on_delete=models.CASCADE, blank=True, null=True)
condition_set = models.ForeignKey(ConditionSet, on_delete=models.CASCADE, blank=True, null=True)
valid_from = models.DateField(blank=True, null=True)
valid_until = models.DateField(blank=True, null=True)
def __str__(self):
return f"{self.id} {self.journal.name}/{self.condition_set}"
diff --git a/django_api/serializers.py b/django_api/serializers.py
index b5ee1da4..224975a3 100644
--- a/django_api/serializers.py
+++ b/django_api/serializers.py
@@ -1,108 +1,128 @@
from rest_framework import serializers
from dj_rql.drf.serializers import RQLMixin
from .models import *
+
+
class JournalSerializer(RQLMixin,serializers.ModelSerializer):
class Meta:
model = Journal
fields = '__all__'
depth = 4
+
class OrgaSerializer(RQLMixin,serializers.ModelSerializer):
class Meta:
model = Organization
fields = '__all__'
depth = 4
+
class TermSerializer(RQLMixin,serializers.ModelSerializer):
class Meta:
model = Term
fields = '__all__'
depth = 4
+
class ConditionSetSerializer(RQLMixin,serializers.ModelSerializer):
term = TermSerializer(many=True, read_only=True)
class Meta:
model = ConditionSet
# pre filter for rql
# fields = ['id','condition_type','term','journal','organization']
# add for informations purpose
fields = '__all__'
depth = 4
+
class TermSerializer(RQLMixin,serializers.ModelSerializer):
class Meta:
model = Term
fields = '__all__'
depth = 2
+
class CountrySerializer(RQLMixin,serializers.ModelSerializer):
class Meta:
model = Country
fields = '__all__'
depth = 4
+
class LanguageSerializer(RQLMixin,serializers.ModelSerializer):
class Meta:
model = Language
fields = '__all__'
depth = 4
+
class IssnSerializer(RQLMixin,serializers.ModelSerializer):
class Meta:
model = Issn
fields = '__all__'
depth = 4
+
+
class OaSerializer(RQLMixin,serializers.ModelSerializer):
class Meta:
model = Oa
fields = '__all__'
depth = 4
+
class PublisherSerializer(RQLMixin,serializers.ModelSerializer):
class Meta:
model = Publisher
fields = '__all__'
depth = 4
+
class VersionSerializer(RQLMixin,serializers.ModelSerializer):
class Meta:
model = Version
fields = '__all__'
depth = 4
+
class LicenceSerializer(RQLMixin,serializers.ModelSerializer):
class Meta:
model = Licence
fields = '__all__'
depth = 4
+
class Cost_factor_typeSerializer(RQLMixin,serializers.ModelSerializer):
class Meta:
model = Cost_factor_type
fields = '__all__'
depth = 4
+
class Cost_factorSerializer(RQLMixin,serializers.ModelSerializer):
class Meta:
model = Cost_factor
fields = '__all__'
depth = 4
+
+
class ConditionTypeSerializer(RQLMixin,serializers.ModelSerializer):
class Meta:
model = ConditionType
fields = '__all__'
depth = 4
+
class OrganizationConditionSerializer(RQLMixin,serializers.ModelSerializer):
class Meta:
model = OrganizationCondition
fields = '__all__'
depth = 4
+
class JournalConditionSerializer(RQLMixin,serializers.ModelSerializer):
class Meta:
model = JournalCondition
fields = '__all__'
depth = 4