diff --git a/docs/dev/journals-api.rst b/docs/dev/journals-api.rst index 46c19a80..68feffcc 100644 --- a/docs/dev/journals-api.rst +++ b/docs/dev/journals-api.rst @@ -188,9 +188,9 @@ Response: "Biological Sciences" ], subject_categories: [ - "Humanities, Multidisciplinary", - "Social Sciences, Interdisciplinary", - "Sociology" + "Humanities, Multidisciplinary", + "Social Sciences, Interdisciplinary", + "Sociology" ], "subject_descriptors": "biologia\nbotanica", "succeeding_title": null, @@ -630,6 +630,11 @@ Response: ], "study_areas": [ + ], + subject_categories: [ + "Humanities, Multidisciplinary", + "Social Sciences, Interdisciplinary", + "Sociology" ], "subject_descriptors": "public health", "succeeding_title": null, @@ -651,131 +656,137 @@ Response: **Example of version 2 with multiple collections**:: + { + "abstract_keyword_languages": null, + "acronym": "RSP", + "collections": [ + "Saude Publica", + "Brasil" + ], + "contact": null, + "copyrighter": "Faculdade de Sa\u00c3\u00bade P\u00c3\u00bablica da Universidade de S\u00c3\u00a3o Paulo", + "cover": null, + "created": "1998-04-30T00:00:00", + "creator": "/api/v2/users/1/", + "ctrl_vocabulary": "decs", + "current_ahead_documents": 0, + "editor_address": "Avenida Dr. Arnaldo, 715, 01246-904 S\u00c3\u00a3o Paulo SP Brazil, Tel./Fax: +55 11 3068-0539", + "editor_address_city": "", + "editor_address_country": "", + "editor_address_state": "", + "editor_address_zip": "", + "editor_email": "", + "editor_name": "", + "editor_phone1": "", + "editor_phone2": null, + "editorial_standard": "vancouv", + "eletronic_issn": "", + "final_num": "", + "final_vol": "", + "final_year": null, + "frequency": "B", + "id": 20, + "index_coverage": "cab-health\nembase\npopline\nlilacs\nadsa\u00c3\u00bade\ndocpal\nabstracts on hygiene and communicable diseases\nabstracts on zooparasitology\nbiological abstracts\ncurrent contents/social & behavioral science\nentomology abstracts\nexcerpta medica\nindex medicus\nmicrobiology abstracts\nnutrition abstracts and reviews-seriesb\nreview medical veterinary entomology\nsafety science abstracts journal\nsocial science citation index\ntropical diseases bulletin\nveterinary bulletin\nvirology abstracts\nisi \npubmed", + "init_num": "1", + "init_vol": "1", + "init_year": "1967", + "is_indexed_aehci": false, + "is_indexed_scie": false, + "is_indexed_ssci": false, + "is_trashed": false, + "issues": [ + "/api/v2/issues/184/", + "/api/v2/issues/186/", + "/api/v2/issues/187/", + "/api/v2/issues/188/", + ], + "languages": [ + "en", + "pt", + "es" + ], + "logo": null, + "medline_code": null, + "medline_title": null, + "missions": { + "en": "To publish and divulge scientific production on subjects of relevance to Public Health", + "es": "Publicar y diseminar productos del trabajo cient\u00c3\u00adfico relevantes para la Salud P\u00c3\u00bablica", + "pt": "Publicar e disseminar produtos do trabalho cient\u00c3\u00adfico que sejam relevantes para a Sa\u00c3\u00bade P\u00c3\u00bablica" + }, + "national_code": "068227-6", + "notes": "", + "other_previous_title": "", + "other_titles": { + "other": "Rev Saude Publica", + "paralleltitle": "Journal of Public Health" + }, + "previous_ahead_documents": 0, + "previous_title": null, + "print_issn": "0034-8910", + "pub_level": "CT", + "pub_status": { + "Saude Publica": "deceased" + }, + "pub_status_history": [ { - "abstract_keyword_languages": null, - "acronym": "RSP", - "collections": [ - "Saude Publica" - ], - "contact": null, - "copyrighter": "Faculdade de Sa\u00c3\u00bade P\u00c3\u00bablica da Universidade de S\u00c3\u00a3o Paulo", - "cover": null, - "created": "1998-04-30T00:00:00", - "creator": "/api/v2/users/1/", - "ctrl_vocabulary": "decs", - "current_ahead_documents": 0, - "editor_address": "Avenida Dr. Arnaldo, 715, 01246-904 S\u00c3\u00a3o Paulo SP Brazil, Tel./Fax: +55 11 3068-0539", - "editor_address_city": "", - "editor_address_country": "", - "editor_address_state": "", - "editor_address_zip": "", - "editor_email": "", - "editor_name": "", - "editor_phone1": "", - "editor_phone2": null, - "editorial_standard": "vancouv", - "eletronic_issn": "", - "final_num": "", - "final_vol": "", - "final_year": null, - "frequency": "B", - "id": 20, - "index_coverage": "cab-health\nembase\npopline\nlilacs\nadsa\u00c3\u00bade\ndocpal\nabstracts on hygiene and communicable diseases\nabstracts on zooparasitology\nbiological abstracts\ncurrent contents/social & behavioral science\nentomology abstracts\nexcerpta medica\nindex medicus\nmicrobiology abstracts\nnutrition abstracts and reviews-seriesb\nreview medical veterinary entomology\nsafety science abstracts journal\nsocial science citation index\ntropical diseases bulletin\nveterinary bulletin\nvirology abstracts\nisi \npubmed", - "init_num": "1", - "init_vol": "1", - "init_year": "1967", - "is_indexed_aehci": false, - "is_indexed_scie": false, - "is_indexed_ssci": false, - "is_trashed": false, - "issues": [ - "/api/v2/issues/184/", - "/api/v2/issues/186/", - "/api/v2/issues/187/", - "/api/v2/issues/188/", - ], - "languages": [ - "en", - "pt", - "es" - ], - "logo": null, - "medline_code": null, - "medline_title": null, - "missions": { - "en": "To publish and divulge scientific production on subjects of relevance to Public Health", - "es": "Publicar y diseminar productos del trabajo cient\u00c3\u00adfico relevantes para la Salud P\u00c3\u00bablica", - "pt": "Publicar e disseminar produtos do trabalho cient\u00c3\u00adfico que sejam relevantes para a Sa\u00c3\u00bade P\u00c3\u00bablica" - }, - "national_code": "068227-6", - "notes": "", - "other_previous_title": "", - "other_titles": { - "other": "Rev Saude Publica", - "paralleltitle": "Journal of Public Health" - }, - "previous_ahead_documents": 0, - "previous_title": null, - "print_issn": "0034-8910", - "pub_level": "CT", - "pub_status": { - "Saude Publica": "deceased" - }, - "pub_status_history": [ - { - "date": "2014-08-14T14:57:05.940893", - "status": "deceased" - }, - { - "date": "2014-04-23T10:30:29.470427", - "status": "current" - } - ], - "pub_status_reason": { - "Saude Publica": "teste" - }, - "publication_city": "S\u00c3\u00a3o Paulo", - "publisher_country": "BR", - "publisher_name": "Faculdade de Sa\u00c3\u00bade P\u00c3\u00bablica da Universidade de S\u00c3\u00a3o Paulo", - "publisher_state": "SP", - "resource_uri": "/api/v2/journals/20/", - "scielo_issn": "print", - "secs_code": "", - "sections": [ - "/api/v2/sections/44/", - "/api/v2/sections/45/", - "/api/v2/sections/46/", - "/api/v2/sections/47/", - "/api/v2/sections/48/", - "/api/v2/sections/49/", - "/api/v2/sections/50/", - "/api/v2/sections/51/", - "/api/v2/sections/52/", - "/api/v2/sections/53/", - "/api/v2/sections/54/", - "/api/v2/sections/55/", - ], - "short_title": "Rev. Sa\u00c3\u00bade P\u00c3\u00bablica", - "sponsors": [ + "date": "2014-08-14T14:57:05.940893", + "status": "deceased" + }, + { + "date": "2014-04-23T10:30:29.470427", + "status": "current" + } + ], + "pub_status_reason": { + "Saude Publica": "teste" + }, + "publication_city": "S\u00c3\u00a3o Paulo", + "publisher_country": "BR", + "publisher_name": "Faculdade de Sa\u00c3\u00bade P\u00c3\u00bablica da Universidade de S\u00c3\u00a3o Paulo", + "publisher_state": "SP", + "resource_uri": "/api/v2/journals/20/", + "scielo_issn": "print", + "secs_code": "", + "sections": [ + "/api/v2/sections/44/", + "/api/v2/sections/45/", + "/api/v2/sections/46/", + "/api/v2/sections/47/", + "/api/v2/sections/48/", + "/api/v2/sections/49/", + "/api/v2/sections/50/", + "/api/v2/sections/51/", + "/api/v2/sections/52/", + "/api/v2/sections/53/", + "/api/v2/sections/54/", + "/api/v2/sections/55/", + ], + "short_title": "Rev. Sa\u00c3\u00bade P\u00c3\u00bablica", + "sponsors": [ - ], - "study_areas": [ + ], + "study_areas": [ - ], - "subject_descriptors": "saude coletiva\nsaude publica\nmicrobiologia", - "succeeding_title": null, - "title": "Revista de Sa\u00c3\u00bade P\u00c3\u00bablica", - "title_iso": "Rev. sa\u00c3\u00bade p\u00c3\u00bablica", - "twitter_user": null, - "updated": "2014-04-03T15:08:35.586311", - "url_journal": null, - "url_online_submission": null, - "use_license": { - "disclaimer": "

", - "id": 4, - "is_default": false, - "license_code": "nd", - "reference_url": null, - "resource_uri": "/api/v2/uselicenses/4/" - } - } + ], + subject_categories: [ + "Humanities, Multidisciplinary", + "Social Sciences, Interdisciplinary", + "Sociology" + ], + "subject_descriptors": "saude coletiva\nsaude publica\nmicrobiologia", + "succeeding_title": null, + "title": "Revista de Sa\u00c3\u00bade P\u00c3\u00bablica", + "title_iso": "Rev. sa\u00c3\u00bade p\u00c3\u00bablica", + "twitter_user": null, + "updated": "2014-04-03T15:08:35.586311", + "url_journal": null, + "url_online_submission": null, + "use_license": { + "disclaimer": "

", + "id": 4, + "is_default": false, + "license_code": "nd", + "reference_url": null, + "resource_uri": "/api/v2/uselicenses/4/" + } + } diff --git a/requirements.txt b/requirements.txt index 742d908f..c3d02432 100644 --- a/requirements.txt +++ b/requirements.txt @@ -17,7 +17,7 @@ django-tastypie==0.9.16 django-waffle==0.9.1 -e git+git://github.com/scieloorg/django-htmlmin.git#egg=django-htmlmin -e git+git://github.com/scieloorg/django-cache-machine.git#egg=django-cache-machine -packtools==0.5dev-20140924 +packtools==0.5 Celery django-celery django-kombu diff --git a/scielomanager/api/resources_v2.py b/scielomanager/api/resources_v2.py index d385bd65..be423e23 100644 --- a/scielomanager/api/resources_v2.py +++ b/scielomanager/api/resources_v2.py @@ -81,6 +81,13 @@ class Meta(ApiKeyAuthMeta): allowed_methods = ['get', ] +class SubjectCategoryResource(ModelResource): + class Meta(ApiKeyAuthMeta): + queryset = models.SubjectCategory.objects.all() + resource_name = 'subjectcategory' + allowed_methods = ['get', ] + + class SectionResource(ModelResource): journal = fields.ForeignKey('api.resources_v2.JournalResource', 'journal') @@ -235,6 +242,8 @@ class JournalResource(ModelResource): collections = fields.ManyToManyField(CollectionResource, 'collections') issues = fields.OneToManyField(IssueResource, 'issue_set') sections = fields.OneToManyField(SectionResource, 'section_set') + subject_categories = fields.ManyToManyField(SubjectCategoryResource, + 'subject_categories', readonly=True) #Recursive field previous_title = fields.ForeignKey('self', 'previous_title', null=True) @@ -304,6 +313,10 @@ def dehydrate_pub_status_reason(self, bundle): def dehydrate_collections(self, bundle): return [col.name for col in bundle.obj.collections.all()] + def dehydrate_subject_categories(self, bundle): + return [subject_category.term + for subject_category in bundle.obj.subject_categories.all()] + class DataChangeEventResource(ModelResource): collection_uri = fields.ForeignKey(CollectionResource, 'collection') diff --git a/scielomanager/api/tests_resources_v2.py b/scielomanager/api/tests_resources_v2.py index f3b3d73b..745309d5 100644 --- a/scielomanager/api/tests_resources_v2.py +++ b/scielomanager/api/tests_resources_v2.py @@ -196,6 +196,7 @@ def test_api_datamodel(self): u'twitter_user', u'previous_title', u'succeeding_title', + u'subject_categories' ] json_keys = set(response.json.keys()) diff --git a/scielomanager/articletrack/tests/tests_pages.py b/scielomanager/articletrack/tests/tests_pages.py index e221872e..80adf822 100644 --- a/scielomanager/articletrack/tests/tests_pages.py +++ b/scielomanager/articletrack/tests/tests_pages.py @@ -281,20 +281,21 @@ def get_xml_uri(self, attempt_id, target_name): balaio() self.mocker.result(BalaioTest()) - XMLValidator = self.mocker.replace('packtools.stylechecker.XMLValidator') - XMLValidator(mocker.ANY) - self.mocker.result(packtools_double.XMLValidatorDouble(mocker.ANY)) + stub_analyze_xml = packtools_double.make_stub_analyze_xml('valid') + mock_utils = self.mocker.replace('validator.utils') + mock_utils.analyze_xml + self.mocker.result(stub_analyze_xml) self.mocker.replay() + # when response = self.app.get( reverse('notice_detail', args=[notice.checkin.pk]), user=self.user) + # then xml_data = response.context['xml_data'] - self.assertEqual(response.status_code, 200) self.assertTrue(xml_data['can_be_analyzed'][0]) - self.assertIsNone(response.context['results']) self.assertIsNone(response.context['xml_exception']) self.assertEqual(xml_data['uri'], expected_response['uri']) self.assertEqual(xml_data['file_name'], expected_response['filename']) @@ -376,9 +377,6 @@ def get_xml_uri(self, attempt_id, target_name): results = response.context['results'] self.assertIsNotNone(results) self.assertIsNotNone(results['annotations']) - validation_errors = results['validation_errors'] - self.assertIsNotNone(validation_errors) - self.assertEqual(1, len(validation_errors)) def test_xml_not_found(self): self._addWaffleFlag() diff --git a/scielomanager/audit_log/helpers.py b/scielomanager/audit_log/helpers.py index 75fb926b..7f5c0747 100644 --- a/scielomanager/audit_log/helpers.py +++ b/scielomanager/audit_log/helpers.py @@ -203,7 +203,6 @@ def collect_new_values(form, formsets=None, as_json_string=False): "formsets_data": [], } - new_values = [] for field_name in get_auditable_fields(form): field_value = form.cleaned_data[field_name] result["form_data"][field_name] = field_serializer(field_serializer(field_value)) diff --git a/scielomanager/editorialmanager/migrations/0003_auto__chg_field_editorialboard_issue__add_unique_editorialboard_issue.py b/scielomanager/editorialmanager/migrations/0003_auto__chg_field_editorialboard_issue__add_unique_editorialboard_issue.py index 42ce0a5f..1e11613a 100644 --- a/scielomanager/editorialmanager/migrations/0003_auto__chg_field_editorialboard_issue__add_unique_editorialboard_issue.py +++ b/scielomanager/editorialmanager/migrations/0003_auto__chg_field_editorialboard_issue__add_unique_editorialboard_issue.py @@ -82,7 +82,8 @@ def backwards(self, orm): 'editorialmanager.roletype': { 'Meta': {'object_name': 'RoleType'}, 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '256'}) + 'name': ('django.db.models.fields.CharField', [], {'max_length': '256'}), + 'weight': ('django.db.models.fields.PositiveSmallIntegerField', [], {'default': '10'}) }, 'journalmanager.collection': { 'Meta': {'ordering': "['name']", 'object_name': 'Collection'}, @@ -274,4 +275,4 @@ def backwards(self, orm): } } - complete_apps = ['editorialmanager'] \ No newline at end of file + complete_apps = ['editorialmanager'] diff --git a/scielomanager/editorialmanager/migrations/0004_auto__add_field_editorialmember_research_id__add_field_editorialmember.py b/scielomanager/editorialmanager/migrations/0004_auto__add_field_editorialmember_research_id__add_field_editorialmember.py new file mode 100644 index 00000000..60b515f0 --- /dev/null +++ b/scielomanager/editorialmanager/migrations/0004_auto__add_field_editorialmember_research_id__add_field_editorialmember.py @@ -0,0 +1,285 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding field 'EditorialMember.research_id' + db.add_column('editorialmanager_editorialmember', 'research_id', + self.gf('django.db.models.fields.CharField')(max_length=256, null=True, blank=True), + keep_default=False) + + # Adding field 'EditorialMember.orcid' + db.add_column('editorialmanager_editorialmember', 'orcid', + self.gf('django.db.models.fields.CharField')(max_length=256, null=True, blank=True), + keep_default=False) + + + def backwards(self, orm): + # Deleting field 'EditorialMember.research_id' + db.delete_column('editorialmanager_editorialmember', 'research_id') + + # Deleting field 'EditorialMember.orcid' + db.delete_column('editorialmanager_editorialmember', 'orcid') + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'editorialmanager.editorialboard': { + 'Meta': {'object_name': 'EditorialBoard'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'issue': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['journalmanager.Issue']", 'unique': 'True'}) + }, + 'editorialmanager.editorialmember': { + 'Meta': {'ordering': "('board', 'role__weight')", 'object_name': 'EditorialMember'}, + 'board': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['editorialmanager.EditorialBoard']"}), + 'city': ('django.db.models.fields.CharField', [], {'max_length': '256', 'null': 'True', 'blank': 'True'}), + 'country': ('django.db.models.fields.CharField', [], {'max_length': '256', 'null': 'True', 'blank': 'True'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '256'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'institution': ('django.db.models.fields.CharField', [], {'max_length': '256', 'null': 'True', 'blank': 'True'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '256'}), + 'link_cv': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), + 'orcid': ('django.db.models.fields.CharField', [], {'max_length': '256', 'null': 'True', 'blank': 'True'}), + 'research_id': ('django.db.models.fields.CharField', [], {'max_length': '256', 'null': 'True', 'blank': 'True'}), + 'role': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['editorialmanager.RoleType']"}), + 'state': ('django.db.models.fields.CharField', [], {'max_length': '256', 'null': 'True', 'blank': 'True'}) + }, + 'editorialmanager.roletype': { + 'Meta': {'ordering': "('weight', 'name')", 'object_name': 'RoleType'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '256'}), + 'weight': ('django.db.models.fields.PositiveSmallIntegerField', [], {'default': '10'}) + }, + 'journalmanager.collection': { + 'Meta': {'ordering': "['name']", 'object_name': 'Collection'}, + 'acronym': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '16', 'blank': 'True'}), + 'address': ('django.db.models.fields.TextField', [], {}), + 'address_complement': ('django.db.models.fields.CharField', [], {'max_length': '128', 'blank': 'True'}), + 'address_number': ('django.db.models.fields.CharField', [], {'max_length': '8'}), + 'city': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}), + 'collection': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'user_collection'", 'to': "orm['auth.User']", 'through': "orm['journalmanager.UserCollections']", 'blank': 'True', 'symmetrical': 'False', 'null': 'True'}), + 'country': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}), + 'fax': ('django.db.models.fields.CharField', [], {'max_length': '16', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'logo': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}), + 'name_slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'unique': 'True', 'null': 'True', 'blank': 'True'}), + 'phone': ('django.db.models.fields.CharField', [], {'max_length': '16', 'blank': 'True'}), + 'state': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}), + 'url': ('django.db.models.fields.URLField', [], {'max_length': '200'}), + 'zip_code': ('django.db.models.fields.CharField', [], {'max_length': '16', 'null': 'True', 'blank': 'True'}) + }, + 'journalmanager.institution': { + 'Meta': {'ordering': "['name']", 'object_name': 'Institution'}, + 'acronym': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '16', 'blank': 'True'}), + 'address': ('django.db.models.fields.TextField', [], {}), + 'address_complement': ('django.db.models.fields.CharField', [], {'max_length': '128', 'blank': 'True'}), + 'address_number': ('django.db.models.fields.CharField', [], {'max_length': '8'}), + 'cel': ('django.db.models.fields.CharField', [], {'max_length': '16', 'blank': 'True'}), + 'city': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}), + 'complement': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}), + 'country': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}), + 'fax': ('django.db.models.fields.CharField', [], {'max_length': '16', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_trashed': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '256', 'db_index': 'True'}), + 'phone': ('django.db.models.fields.CharField', [], {'max_length': '16', 'blank': 'True'}), + 'state': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}), + 'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'zip_code': ('django.db.models.fields.CharField', [], {'max_length': '16', 'null': 'True', 'blank': 'True'}) + }, + 'journalmanager.issue': { + 'Meta': {'ordering': "('created', 'id')", 'object_name': 'Issue'}, + 'cover': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'ctrl_vocabulary': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}), + 'editorial_standard': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_marked_up': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_trashed': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}), + 'journal': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['journalmanager.Journal']"}), + 'label': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '64', 'null': 'True', 'blank': 'True'}), + 'number': ('django.db.models.fields.CharField', [], {'max_length': '16', 'blank': 'True'}), + 'order': ('django.db.models.fields.IntegerField', [], {'blank': 'True'}), + 'publication_end_month': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'publication_start_month': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'publication_year': ('django.db.models.fields.IntegerField', [], {}), + 'section': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['journalmanager.Section']", 'symmetrical': 'False', 'blank': 'True'}), + 'spe_text': ('django.db.models.fields.CharField', [], {'max_length': '15', 'null': 'True', 'blank': 'True'}), + 'suppl_text': ('django.db.models.fields.CharField', [], {'max_length': '15', 'null': 'True', 'blank': 'True'}), + 'total_documents': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'type': ('django.db.models.fields.CharField', [], {'default': "'regular'", 'max_length': '15'}), + 'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'use_license': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['journalmanager.UseLicense']", 'null': 'True'}), + 'volume': ('django.db.models.fields.CharField', [], {'max_length': '16', 'blank': 'True'}) + }, + 'journalmanager.journal': { + 'Meta': {'ordering': "('title', 'id')", 'object_name': 'Journal'}, + 'abstract_keyword_languages': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'abstract_keyword_languages'", 'symmetrical': 'False', 'to': "orm['journalmanager.Language']"}), + 'acronym': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'collections': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['journalmanager.Collection']", 'through': "orm['journalmanager.Membership']", 'symmetrical': 'False'}), + 'copyrighter': ('django.db.models.fields.CharField', [], {'max_length': '254'}), + 'cover': ('scielomanager.custom_fields.ContentTypeRestrictedFileField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'creator': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'enjoy_creator'", 'to': "orm['auth.User']"}), + 'ctrl_vocabulary': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'current_ahead_documents': ('django.db.models.fields.IntegerField', [], {'default': '0', 'max_length': '3', 'null': 'True', 'blank': 'True'}), + 'editor': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'editor_journal'", 'null': 'True', 'to': "orm['auth.User']"}), + 'editor_address': ('django.db.models.fields.CharField', [], {'max_length': '512'}), + 'editor_address_city': ('django.db.models.fields.CharField', [], {'max_length': '256'}), + 'editor_address_country': ('scielo_extensions.modelfields.CountryField', [], {'max_length': '2'}), + 'editor_address_state': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'editor_address_zip': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'editor_email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}), + 'editor_name': ('django.db.models.fields.CharField', [], {'max_length': '512'}), + 'editor_phone1': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + 'editor_phone2': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}), + 'editorial_standard': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'eletronic_issn': ('django.db.models.fields.CharField', [], {'max_length': '9', 'db_index': 'True'}), + 'final_num': ('django.db.models.fields.CharField', [], {'max_length': '16', 'blank': 'True'}), + 'final_vol': ('django.db.models.fields.CharField', [], {'max_length': '16', 'blank': 'True'}), + 'final_year': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), + 'frequency': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'index_coverage': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'init_num': ('django.db.models.fields.CharField', [], {'max_length': '16', 'null': 'True', 'blank': 'True'}), + 'init_vol': ('django.db.models.fields.CharField', [], {'max_length': '16', 'null': 'True', 'blank': 'True'}), + 'init_year': ('django.db.models.fields.CharField', [], {'max_length': '4'}), + 'is_indexed_aehci': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_indexed_scie': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_indexed_ssci': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_trashed': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}), + 'languages': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['journalmanager.Language']", 'symmetrical': 'False'}), + 'logo': ('scielomanager.custom_fields.ContentTypeRestrictedFileField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'medline_code': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'}), + 'medline_title': ('django.db.models.fields.CharField', [], {'max_length': '256', 'null': 'True', 'blank': 'True'}), + 'national_code': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'}), + 'notes': ('django.db.models.fields.TextField', [], {'max_length': '254', 'null': 'True', 'blank': 'True'}), + 'other_previous_title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'previous_ahead_documents': ('django.db.models.fields.IntegerField', [], {'default': '0', 'max_length': '3', 'null': 'True', 'blank': 'True'}), + 'previous_title': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'prev_title'", 'null': 'True', 'to': "orm['journalmanager.Journal']"}), + 'print_issn': ('django.db.models.fields.CharField', [], {'max_length': '9', 'db_index': 'True'}), + 'pub_level': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'publication_city': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'publisher_country': ('scielo_extensions.modelfields.CountryField', [], {'max_length': '2'}), + 'publisher_name': ('django.db.models.fields.CharField', [], {'max_length': '256'}), + 'publisher_state': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'scielo_issn': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'secs_code': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}), + 'short_title': ('django.db.models.fields.CharField', [], {'max_length': '256', 'null': 'True', 'db_index': 'True'}), + 'sponsor': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'journal_sponsor'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['journalmanager.Sponsor']"}), + 'study_areas': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'journals_migration_tmp'", 'null': 'True', 'to': "orm['journalmanager.StudyArea']"}), + 'subject_categories': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'journals'", 'null': 'True', 'to': "orm['journalmanager.SubjectCategory']"}), + 'subject_descriptors': ('django.db.models.fields.CharField', [], {'max_length': '1024'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '256', 'db_index': 'True'}), + 'title_iso': ('django.db.models.fields.CharField', [], {'max_length': '256', 'db_index': 'True'}), + 'twitter_user': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}), + 'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'url_journal': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}), + 'url_online_submission': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}), + 'use_license': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['journalmanager.UseLicense']"}) + }, + 'journalmanager.language': { + 'Meta': {'ordering': "['name']", 'object_name': 'Language'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'iso_code': ('django.db.models.fields.CharField', [], {'max_length': '2'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}) + }, + 'journalmanager.membership': { + 'Meta': {'unique_together': "(('journal', 'collection'),)", 'object_name': 'Membership'}, + 'collection': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['journalmanager.Collection']"}), + 'created_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'journal': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['journalmanager.Journal']"}), + 'reason': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}), + 'since': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'inprogress'", 'max_length': '16'}) + }, + 'journalmanager.section': { + 'Meta': {'ordering': "('id',)", 'object_name': 'Section'}, + 'code': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '21', 'blank': 'True'}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_trashed': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}), + 'journal': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['journalmanager.Journal']"}), + 'legacy_code': ('django.db.models.fields.CharField', [], {'max_length': '16', 'null': 'True', 'blank': 'True'}), + 'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}) + }, + 'journalmanager.sponsor': { + 'Meta': {'ordering': "['name']", 'object_name': 'Sponsor', '_ormbases': ['journalmanager.Institution']}, + 'collections': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['journalmanager.Collection']", 'symmetrical': 'False'}), + 'institution_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['journalmanager.Institution']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'journalmanager.studyarea': { + 'Meta': {'object_name': 'StudyArea'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'study_area': ('django.db.models.fields.CharField', [], {'max_length': '256'}) + }, + 'journalmanager.subjectcategory': { + 'Meta': {'object_name': 'SubjectCategory'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'term': ('django.db.models.fields.CharField', [], {'max_length': '256', 'db_index': 'True'}) + }, + 'journalmanager.uselicense': { + 'Meta': {'ordering': "['license_code']", 'object_name': 'UseLicense'}, + 'disclaimer': ('django.db.models.fields.TextField', [], {'max_length': '512', 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_default': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'license_code': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}), + 'reference_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}) + }, + 'journalmanager.usercollections': { + 'Meta': {'unique_together': "(('user', 'collection'),)", 'object_name': 'UserCollections'}, + 'collection': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['journalmanager.Collection']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_default': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_manager': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) + } + } + + complete_apps = ['editorialmanager'] \ No newline at end of file diff --git a/scielomanager/editorialmanager/migrations/0005_auto__chg_field_editorialmember_email.py b/scielomanager/editorialmanager/migrations/0005_auto__chg_field_editorialmember_email.py new file mode 100644 index 00000000..4b834947 --- /dev/null +++ b/scielomanager/editorialmanager/migrations/0005_auto__chg_field_editorialmember_email.py @@ -0,0 +1,273 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Changing field 'EditorialMember.email' + db.alter_column('editorialmanager_editorialmember', 'email', self.gf('django.db.models.fields.EmailField')(max_length=75, null=True)) + + def backwards(self, orm): + + # Changing field 'EditorialMember.email' + db.alter_column('editorialmanager_editorialmember', 'email', self.gf('django.db.models.fields.EmailField')(default=None, max_length=75)) + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'editorialmanager.editorialboard': { + 'Meta': {'object_name': 'EditorialBoard'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'issue': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['journalmanager.Issue']", 'unique': 'True'}) + }, + 'editorialmanager.editorialmember': { + 'Meta': {'ordering': "('board', 'role__weight')", 'object_name': 'EditorialMember'}, + 'board': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['editorialmanager.EditorialBoard']"}), + 'city': ('django.db.models.fields.CharField', [], {'max_length': '256', 'null': 'True', 'blank': 'True'}), + 'country': ('django.db.models.fields.CharField', [], {'max_length': '256', 'null': 'True', 'blank': 'True'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '256'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'institution': ('django.db.models.fields.CharField', [], {'max_length': '256', 'null': 'True', 'blank': 'True'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '256'}), + 'link_cv': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), + 'role': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['editorialmanager.RoleType']"}), + 'state': ('django.db.models.fields.CharField', [], {'max_length': '256', 'null': 'True', 'blank': 'True'}) + }, + 'editorialmanager.roletype': { + 'Meta': {'ordering': "('weight', 'name')", 'object_name': 'RoleType'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '256'}), + 'weight': ('django.db.models.fields.PositiveSmallIntegerField', [], {'default': '10'}) + }, + 'journalmanager.collection': { + 'Meta': {'ordering': "['name']", 'object_name': 'Collection'}, + 'acronym': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '16', 'blank': 'True'}), + 'address': ('django.db.models.fields.TextField', [], {}), + 'address_complement': ('django.db.models.fields.CharField', [], {'max_length': '128', 'blank': 'True'}), + 'address_number': ('django.db.models.fields.CharField', [], {'max_length': '8'}), + 'city': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}), + 'collection': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'user_collection'", 'to': "orm['auth.User']", 'through': "orm['journalmanager.UserCollections']", 'blank': 'True', 'symmetrical': 'False', 'null': 'True'}), + 'country': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}), + 'fax': ('django.db.models.fields.CharField', [], {'max_length': '16', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'logo': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}), + 'name_slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'unique': 'True', 'null': 'True', 'blank': 'True'}), + 'phone': ('django.db.models.fields.CharField', [], {'max_length': '16', 'blank': 'True'}), + 'state': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}), + 'url': ('django.db.models.fields.URLField', [], {'max_length': '200'}), + 'zip_code': ('django.db.models.fields.CharField', [], {'max_length': '16', 'null': 'True', 'blank': 'True'}) + }, + 'journalmanager.institution': { + 'Meta': {'ordering': "['name']", 'object_name': 'Institution'}, + 'acronym': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '16', 'blank': 'True'}), + 'address': ('django.db.models.fields.TextField', [], {}), + 'address_complement': ('django.db.models.fields.CharField', [], {'max_length': '128', 'blank': 'True'}), + 'address_number': ('django.db.models.fields.CharField', [], {'max_length': '8'}), + 'cel': ('django.db.models.fields.CharField', [], {'max_length': '16', 'blank': 'True'}), + 'city': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}), + 'complement': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}), + 'country': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}), + 'fax': ('django.db.models.fields.CharField', [], {'max_length': '16', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_trashed': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '256', 'db_index': 'True'}), + 'phone': ('django.db.models.fields.CharField', [], {'max_length': '16', 'blank': 'True'}), + 'state': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}), + 'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'zip_code': ('django.db.models.fields.CharField', [], {'max_length': '16', 'null': 'True', 'blank': 'True'}) + }, + 'journalmanager.issue': { + 'Meta': {'ordering': "('created', 'id')", 'object_name': 'Issue'}, + 'cover': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'ctrl_vocabulary': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}), + 'editorial_standard': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_marked_up': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_trashed': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}), + 'journal': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['journalmanager.Journal']"}), + 'label': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '64', 'null': 'True', 'blank': 'True'}), + 'number': ('django.db.models.fields.CharField', [], {'max_length': '16', 'blank': 'True'}), + 'order': ('django.db.models.fields.IntegerField', [], {'blank': 'True'}), + 'publication_end_month': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'publication_start_month': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'publication_year': ('django.db.models.fields.IntegerField', [], {}), + 'section': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['journalmanager.Section']", 'symmetrical': 'False', 'blank': 'True'}), + 'spe_text': ('django.db.models.fields.CharField', [], {'max_length': '15', 'null': 'True', 'blank': 'True'}), + 'suppl_text': ('django.db.models.fields.CharField', [], {'max_length': '15', 'null': 'True', 'blank': 'True'}), + 'total_documents': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'type': ('django.db.models.fields.CharField', [], {'default': "'regular'", 'max_length': '15'}), + 'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'use_license': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['journalmanager.UseLicense']", 'null': 'True'}), + 'volume': ('django.db.models.fields.CharField', [], {'max_length': '16', 'blank': 'True'}) + }, + 'journalmanager.journal': { + 'Meta': {'ordering': "('title', 'id')", 'object_name': 'Journal'}, + 'abstract_keyword_languages': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'abstract_keyword_languages'", 'symmetrical': 'False', 'to': "orm['journalmanager.Language']"}), + 'acronym': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'collections': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['journalmanager.Collection']", 'through': "orm['journalmanager.Membership']", 'symmetrical': 'False'}), + 'copyrighter': ('django.db.models.fields.CharField', [], {'max_length': '254'}), + 'cover': ('scielomanager.custom_fields.ContentTypeRestrictedFileField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'creator': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'enjoy_creator'", 'to': "orm['auth.User']"}), + 'ctrl_vocabulary': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'current_ahead_documents': ('django.db.models.fields.IntegerField', [], {'default': '0', 'max_length': '3', 'null': 'True', 'blank': 'True'}), + 'editor': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'editor_journal'", 'null': 'True', 'to': "orm['auth.User']"}), + 'editor_address': ('django.db.models.fields.CharField', [], {'max_length': '512'}), + 'editor_address_city': ('django.db.models.fields.CharField', [], {'max_length': '256'}), + 'editor_address_country': ('scielo_extensions.modelfields.CountryField', [], {'max_length': '2'}), + 'editor_address_state': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'editor_address_zip': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'editor_email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}), + 'editor_name': ('django.db.models.fields.CharField', [], {'max_length': '512'}), + 'editor_phone1': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + 'editor_phone2': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}), + 'editorial_standard': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'eletronic_issn': ('django.db.models.fields.CharField', [], {'max_length': '9', 'db_index': 'True'}), + 'final_num': ('django.db.models.fields.CharField', [], {'max_length': '16', 'blank': 'True'}), + 'final_vol': ('django.db.models.fields.CharField', [], {'max_length': '16', 'blank': 'True'}), + 'final_year': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), + 'frequency': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'index_coverage': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'init_num': ('django.db.models.fields.CharField', [], {'max_length': '16', 'null': 'True', 'blank': 'True'}), + 'init_vol': ('django.db.models.fields.CharField', [], {'max_length': '16', 'null': 'True', 'blank': 'True'}), + 'init_year': ('django.db.models.fields.CharField', [], {'max_length': '4'}), + 'is_indexed_aehci': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_indexed_scie': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_indexed_ssci': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_trashed': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}), + 'languages': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['journalmanager.Language']", 'symmetrical': 'False'}), + 'logo': ('scielomanager.custom_fields.ContentTypeRestrictedFileField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'medline_code': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'}), + 'medline_title': ('django.db.models.fields.CharField', [], {'max_length': '256', 'null': 'True', 'blank': 'True'}), + 'national_code': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'}), + 'notes': ('django.db.models.fields.TextField', [], {'max_length': '254', 'null': 'True', 'blank': 'True'}), + 'other_previous_title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'previous_ahead_documents': ('django.db.models.fields.IntegerField', [], {'default': '0', 'max_length': '3', 'null': 'True', 'blank': 'True'}), + 'previous_title': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'prev_title'", 'null': 'True', 'to': "orm['journalmanager.Journal']"}), + 'print_issn': ('django.db.models.fields.CharField', [], {'max_length': '9', 'db_index': 'True'}), + 'pub_level': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'publication_city': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'publisher_country': ('scielo_extensions.modelfields.CountryField', [], {'max_length': '2'}), + 'publisher_name': ('django.db.models.fields.CharField', [], {'max_length': '256'}), + 'publisher_state': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'scielo_issn': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'secs_code': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}), + 'short_title': ('django.db.models.fields.CharField', [], {'max_length': '256', 'null': 'True', 'db_index': 'True'}), + 'sponsor': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'journal_sponsor'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['journalmanager.Sponsor']"}), + 'study_areas': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'journals_migration_tmp'", 'null': 'True', 'to': "orm['journalmanager.StudyArea']"}), + 'subject_categories': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'journals'", 'null': 'True', 'to': "orm['journalmanager.SubjectCategory']"}), + 'subject_descriptors': ('django.db.models.fields.CharField', [], {'max_length': '1024'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '256', 'db_index': 'True'}), + 'title_iso': ('django.db.models.fields.CharField', [], {'max_length': '256', 'db_index': 'True'}), + 'twitter_user': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}), + 'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'url_journal': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}), + 'url_online_submission': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}), + 'use_license': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['journalmanager.UseLicense']"}) + }, + 'journalmanager.language': { + 'Meta': {'ordering': "['name']", 'object_name': 'Language'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'iso_code': ('django.db.models.fields.CharField', [], {'max_length': '2'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}) + }, + 'journalmanager.membership': { + 'Meta': {'unique_together': "(('journal', 'collection'),)", 'object_name': 'Membership'}, + 'collection': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['journalmanager.Collection']"}), + 'created_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'journal': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['journalmanager.Journal']"}), + 'reason': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}), + 'since': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'inprogress'", 'max_length': '16'}) + }, + 'journalmanager.section': { + 'Meta': {'ordering': "('id',)", 'object_name': 'Section'}, + 'code': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '21', 'blank': 'True'}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_trashed': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}), + 'journal': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['journalmanager.Journal']"}), + 'legacy_code': ('django.db.models.fields.CharField', [], {'max_length': '16', 'null': 'True', 'blank': 'True'}), + 'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}) + }, + 'journalmanager.sponsor': { + 'Meta': {'ordering': "['name']", 'object_name': 'Sponsor', '_ormbases': ['journalmanager.Institution']}, + 'collections': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['journalmanager.Collection']", 'symmetrical': 'False'}), + 'institution_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['journalmanager.Institution']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'journalmanager.studyarea': { + 'Meta': {'object_name': 'StudyArea'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'study_area': ('django.db.models.fields.CharField', [], {'max_length': '256'}) + }, + 'journalmanager.subjectcategory': { + 'Meta': {'object_name': 'SubjectCategory'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'term': ('django.db.models.fields.CharField', [], {'max_length': '256', 'db_index': 'True'}) + }, + 'journalmanager.uselicense': { + 'Meta': {'ordering': "['license_code']", 'object_name': 'UseLicense'}, + 'disclaimer': ('django.db.models.fields.TextField', [], {'max_length': '512', 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_default': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'license_code': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}), + 'reference_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}) + }, + 'journalmanager.usercollections': { + 'Meta': {'unique_together': "(('user', 'collection'),)", 'object_name': 'UserCollections'}, + 'collection': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['journalmanager.Collection']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_default': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_manager': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) + } + } + + complete_apps = ['editorialmanager'] diff --git a/scielomanager/editorialmanager/models.py b/scielomanager/editorialmanager/models.py index 421b8a79..8e979cb4 100644 --- a/scielomanager/editorialmanager/models.py +++ b/scielomanager/editorialmanager/models.py @@ -27,13 +27,15 @@ class EditorialMember(models.Model): first_name = models.CharField(_('First Name'), max_length=256) last_name = models.CharField(_('Last Name'), max_length=256) - email = models.EmailField(_('E-mail')) + email = models.EmailField(_('E-mail'), null=True, blank=True) institution = models.CharField(_('institution'), max_length=256, null=True, blank=True) link_cv = models.URLField(_('Link CV'), null=True, blank=True) city = models.CharField(_('City'), max_length=256, null=True, blank=True) state = models.CharField(_('State'), max_length=256, null=True, blank=True) country = models.CharField(_('Country'), max_length=256, null=True, blank=True) + research_id = models.CharField(_('ResearchID'), max_length=256, null=True, blank=True) + orcid = models.CharField(_('ORCID'), max_length=256, null=True, blank=True) def __unicode__(self): return self.get_full_name() diff --git a/scielomanager/editorialmanager/templates/board/board_member_delete_data.html b/scielomanager/editorialmanager/templates/board/board_member_delete_data.html index e7457c13..15af8ac0 100644 --- a/scielomanager/editorialmanager/templates/board/board_member_delete_data.html +++ b/scielomanager/editorialmanager/templates/board/board_member_delete_data.html @@ -1,6 +1,6 @@ {% load i18n %} -
+ {% csrf_token %}

{% trans "This operation cannot be undone" %}

diff --git a/scielomanager/editorialmanager/templates/board/board_member_edit_form.html b/scielomanager/editorialmanager/templates/board/board_member_edit_form.html index 24c45cb4..f87ea7fb 100644 --- a/scielomanager/editorialmanager/templates/board/board_member_edit_form.html +++ b/scielomanager/editorialmanager/templates/board/board_member_edit_form.html @@ -1,6 +1,6 @@ {% load show_field i18n %} - + {% csrf_token %} {% for field in form %} {% show_field field %} diff --git a/scielomanager/editorialmanager/tests/modelfactories.py b/scielomanager/editorialmanager/tests/modelfactories.py index df1fe0d5..947374d4 100644 --- a/scielomanager/editorialmanager/tests/modelfactories.py +++ b/scielomanager/editorialmanager/tests/modelfactories.py @@ -34,4 +34,6 @@ class EditorialMemberFactory(factory.Factory): city = factory.Sequence(lambda n: 'city_%s' % n) state = factory.Sequence(lambda n: 'state_%s' % n) country = factory.Sequence(lambda n: 'country_%s' % n) + research_id = factory.Sequence(lambda n: 'A-%04d-2014' % int(n)) + orcid = factory.Sequence(lambda n: '0000-0001-0002-%04d' % int(n)) diff --git a/scielomanager/editorialmanager/tests/test_forms.py b/scielomanager/editorialmanager/tests/test_forms.py index b39fc640..ff76c251 100644 --- a/scielomanager/editorialmanager/tests/test_forms.py +++ b/scielomanager/editorialmanager/tests/test_forms.py @@ -1,10 +1,35 @@ -#coding: utf-8 +# coding: utf-8 from django_webtest import WebTest from django.core.urlresolvers import reverse from journalmanager.tests import modelfactories -from journalmanager.tests.tests_forms import _makePermission + +from . import modelfactories as editorial_modelfactories +from editorialmanager.models import EditorialMember, EditorialBoard +from audit_log.models import AuditLogEntry, ADDITION, CHANGE, DELETION + + +def _makePermission(perm, model, app_label='editorialmanager'): + """ + Retrieves a Permission according to the given model and app_label. + """ + from django.contrib.contenttypes import models + from django.contrib.auth import models as auth_models + + ct = models.ContentType.objects.get(model=model, app_label=app_label) + return auth_models.Permission.objects.get(codename=perm, content_type=ct) + + +def _add_required_permission_to_group(group): + # required permissions + perm_add_editorialmember = _makePermission(perm='add_editorialmember', model='editorialmember') + perm_change_editorialmember = _makePermission(perm='change_editorialmember', model='editorialmember') + perm_delete_editorialmember = _makePermission(perm='delete_editorialmember', model='editorialmember') + + group.permissions.add(perm_add_editorialmember) + group.permissions.add(perm_change_editorialmember) + group.permissions.add(perm_delete_editorialmember) class RestrictedJournalFormTests(WebTest): @@ -73,8 +98,8 @@ class AddUserAsEditorFormTests(WebTest): def setUp(self): - perm1 = _makePermission(perm='list_editor_journal', model='journal') - perm2 = _makePermission(perm='change_editor', model='journal') + perm1 = _makePermission(perm='list_editor_journal', model='journal', app_label='journalmanager') + perm2 = _makePermission(perm='change_editor', model='journal', app_label='journalmanager') #create a group 'Librarian' group = modelfactories.GroupFactory(name="Librarian") @@ -116,9 +141,372 @@ def test_add_user_as_editor_formdata(self): response.mustcontain('Successfully selected %s as editor of this Journal' % user_editor.get_full_name()) +class EditorialMemberFormAsEditorTests(WebTest): + + def setUp(self): + # create a group 'Editors' + group = modelfactories.GroupFactory(name="Editors") + # create a user and set group 'Editors' + self.user = modelfactories.UserFactory(is_active=True) + self.user.groups.add(group) + + self.collection = modelfactories.CollectionFactory.create() + self.collection.add_user(self.user, is_manager=False) + self.collection.make_default_to_user(self.user) + + self.journal = modelfactories.JournalFactory.create() + self.journal.join(self.collection, self.user) + # set the user as editor of the journal + self.journal.editor = self.user + + # create an issue + self.issue = modelfactories.IssueFactory.create() + self.issue.journal = self.journal + self.journal.save() + self.issue.save() + _add_required_permission_to_group(group) + def tearDown(self): + pass + def test_ADD_board_memeber_valid_POST_is_valid(self): + """ + User of the group "Editors" successfully ADD a new board member + """ + # with + role = editorial_modelfactories.RoleTypeFactory.create() + response = self.app.get(reverse("editorial.board.add", args=[self.journal.id, self.issue.id]), user=self.user) + pre_submittion_audit_logs_count = AuditLogEntry.objects.all().count() + # when + member_data = { + 'role': role, + 'first_name': 'first name', + 'last_name': 'last name', + 'email': 'email@example.com', + 'institution': 'institution name', + 'link_cv': 'http://scielo.org/php/index.php', + 'state': 'SP', + 'country': 'Brasil', + } + # when + form = response.forms['member-form'] + form.set('role', member_data['role'].pk) + form['first_name'] = member_data['first_name'] + form['last_name'] = member_data['last_name'] + form['email'] = member_data['email'] + form['institution'] = member_data['institution'] + form['link_cv'] = member_data['link_cv'] + form['state'] = member_data['state'] + form['country'] = member_data['country'] + + response = form.submit().follow() + + # then + self.assertIn('Board Member created successfully.', response.body) + self.assertTemplateUsed(response, 'board/board_list.html') + + self.assertIn(member_data['role'].name, response.body) + self.assertIn(member_data['first_name'], response.body) + self.assertIn(member_data['last_name'], response.body) + self.assertIn(member_data['email'], response.body) + self.assertIn(member_data['institution'], response.body) + # the link_cv is not displayed in the frontend + self.assertIn(member_data['state'], response.body) + self.assertIn(member_data['country'], response.body) + + # check new member in DB + members = EditorialMember.objects.filter( + role=member_data['role'], + first_name=member_data['first_name'], + last_name=member_data['last_name'], + email=member_data['email'], + institution=member_data['institution'], + link_cv=member_data['link_cv'], + state=member_data['state'], + country=member_data['country'], + ) + self.assertEqual(len(members), 1) + # check relations + self.assertIsNotNone(self.issue.editorialboard) + self.assertEqual(len(self.issue.editorialboard.editorialmember_set.all()), 1) + self.assertEqual(members[0].pk, self.issue.editorialboard.editorialmember_set.all()[0].pk) + + # check audit log: + self.assertEqual(pre_submittion_audit_logs_count, 0) + audit_entries = AuditLogEntry.objects.all() + self.assertEqual(audit_entries.count(), 1) + entry = audit_entries[0] + self.assertEqual(entry.action_flag, ADDITION) # Flag correspond with ADDITION action + self.assertEqual(entry.content_type.model_class(), EditorialMember) + audited_obj = entry.get_audited_object() + self.assertEqual(audited_obj._meta.object_name, 'EditorialMember') + self.assertEqual(audited_obj.pk, members[0].pk) + + self.assertIn(u'Added fields:', entry.change_message) # message starts with u'Added fields:' + for field_name in member_data.keys(): + self.assertIn(field_name, entry.change_message) + + def test_ADD_board_memeber_invalid_POST_is_invalid(self): + """ + User of the group "Editors" UNsuccessfully ADD a new board member with a invalid email + """ + + # with + role = editorial_modelfactories.RoleTypeFactory.create() + response = self.app.get(reverse("editorial.board.add", args=[self.journal.id, self.issue.id]), user=self.user) + pre_submittion_audit_logs_count = AuditLogEntry.objects.all().count() + # when + member_data = { + 'role': role, + 'first_name': 'first name', + 'last_name': 'last name', + 'email': 'email@example.com', + 'institution': 'institution name', + 'link_cv': '@invalid_url/index.php', + 'state': 'SP', + 'country': 'Brasil', + } + # when + form = response.forms['member-form'] + form.set('role', member_data['role'].pk) + form['first_name'] = member_data['first_name'] + form['last_name'] = member_data['last_name'] + form['email'] = member_data['email'] + form['institution'] = member_data['institution'] + form['link_cv'] = member_data['link_cv'] + form['state'] = member_data['state'] + form['country'] = member_data['country'] + + response = form.submit() + + # check output + self.assertTemplateUsed(response, 'board/board_member_edit_form.html') + self.assertFalse(response.context['form'].is_valid()) + expected_errors = {'link_cv': [u'Enter a valid URL.']} + self.assertEqual(response.context['form'].errors, expected_errors) + self.assertIn('Check mandatory fields.', response.body) + # expected extra context data + expected_post_url = reverse('editorial.board.add', args=[self.journal.pk, self.issue.pk, ]) + expected_board_url = reverse('editorial.board', args=[self.journal.pk, ]) + self.assertEqual(response.context['post_url'], expected_post_url) + self.assertEqual(response.context['board_url'], expected_board_url) + + # check relations: board created if none, but no members added + self.assertIsNotNone(self.issue.editorialboard) + self.assertEqual(len(self.issue.editorialboard.editorialmember_set.all()), 0) + + # check audit logs: no logs generated + self.assertEqual(pre_submittion_audit_logs_count, 0) + self.assertEqual(AuditLogEntry.objects.all().count(), 0) + + + def test_EDIT_board_memeber_valid_POST_is_valid(self): + """ + User of the group "Editors" successfully EDIT a board member + """ + # with + member = editorial_modelfactories.EditorialMemberFactory.create() + member.board = EditorialBoard.objects.create(issue=self.issue) + member.save() + role = editorial_modelfactories.RoleTypeFactory.create() + member_data_update = { + 'role': role, + 'first_name': 'Nombre', + 'last_name': 'Apellido', + 'email': 'nombre.apellido@fing.edu.uy', + 'institution': 'Universidad de la Republica - Facultad de Ingenieria', + 'link_cv': 'http://scielo.org.uy/', + 'state': 'Montevideo', + 'country': 'Uruguay', + } + response = self.app.get(reverse("editorial.board.edit", args=[self.journal.id, member.id]), user=self.user) + pre_submittion_audit_logs_count = AuditLogEntry.objects.all().count() + + # when + form = response.forms['member-form'] + form.set('role', member_data_update['role'].pk) + form['first_name'] = member_data_update['first_name'] + form['last_name'] = member_data_update['last_name'] + form['email'] = member_data_update['email'] + form['institution'] = member_data_update['institution'] + form['link_cv'] = member_data_update['link_cv'] + form['state'] = member_data_update['state'] + form['country'] = member_data_update['country'] + + response = form.submit().follow() + # then + + # check frontend + self.assertIn('Board Member updated successfully.', response.body) + self.assertTemplateUsed(response, 'board/board_list.html') + self.assertIn(member_data_update['role'].name, response.body) + self.assertIn(member_data_update['first_name'], response.body) + self.assertIn(member_data_update['last_name'], response.body) + self.assertIn(member_data_update['email'], response.body) + self.assertIn(member_data_update['institution'], response.body) + # the link_cv is not displayed in the frontend + self.assertIn(member_data_update['state'], response.body) + self.assertIn(member_data_update['country'], response.body) + + # check data from db + member_from_db = EditorialMember.objects.get(pk=member.pk) + self.assertEqual(member_from_db.role, member_data_update['role']) + self.assertEqual(member_from_db.first_name, member_data_update['first_name']) + self.assertEqual(member_from_db.last_name, member_data_update['last_name']) + self.assertEqual(member_from_db.email, member_data_update['email']) + self.assertEqual(member_from_db.institution, member_data_update['institution']) + self.assertEqual(member_from_db.link_cv, member_data_update['link_cv']) + self.assertEqual(member_from_db.state, member_data_update['state']) + self.assertEqual(member_from_db.country, member_data_update['country']) + + # check relations + self.assertIsNotNone(self.issue.editorialboard) + self.assertEqual(len(self.issue.editorialboard.editorialmember_set.all()), 1) + self.assertEqual(member_from_db.pk, self.issue.editorialboard.editorialmember_set.all()[0].pk) + + # check audit log: + self.assertEqual(pre_submittion_audit_logs_count, 0) + audit_entries = AuditLogEntry.objects.all() + self.assertEqual(audit_entries.count(), 1) + entry = audit_entries[0] + self.assertEqual(entry.action_flag, CHANGE) # Flag correspond with CHANGE action + self.assertEqual(entry.content_type.model_class(), EditorialMember) + audited_obj = entry.get_audited_object() + self.assertEqual(audited_obj._meta.object_name, 'EditorialMember') + self.assertEqual(audited_obj.pk, member_from_db.pk) + + self.assertIn( u'Changed fields:', entry.change_message) # message starts with u'Changed fields:' + for field_name in member_data_update.keys(): + self.assertIn(field_name, entry.change_message) + + def test_EDIT_board_memeber_invalid_POST_is_invalid(self): + """ + User of the group "Editors" UNsuccessfully EDIT a board member with a invalid email + """ + # with + member = editorial_modelfactories.EditorialMemberFactory.create() + member.board = EditorialBoard.objects.create(issue=self.issue) + member.save() + role = editorial_modelfactories.RoleTypeFactory.create() + member_data_update = { + 'role': role, + 'first_name': 'Nombre', + 'last_name': 'Apellido', + 'email': 'nombre.apellido@', # invalid email + 'institution': 'Universidad de la Republica - Facultad de Ingenieria', + 'link_cv': 'http://scielo.org.uy/', + 'state': 'Montevideo', + 'country': 'Uruguay', + } + response = self.app.get(reverse("editorial.board.edit", args=[self.journal.id, member.id]), user=self.user) + pre_submittion_audit_logs_count = AuditLogEntry.objects.all().count() + + # when + form = response.forms['member-form'] + form.set('role', member_data_update['role'].pk) + form['first_name'] = member_data_update['first_name'] + form['last_name'] = member_data_update['last_name'] + form['email'] = member_data_update['email'] + form['institution'] = member_data_update['institution'] + form['link_cv'] = member_data_update['link_cv'] + form['state'] = member_data_update['state'] + form['country'] = member_data_update['country'] + + response = form.submit() + + # then + # check output + self.assertTemplateUsed(response, 'board/board_member_edit_form.html') + self.assertFalse(response.context['form'].is_valid()) + expected_errors = {'email': [u'Enter a valid e-mail address.']} + self.assertEqual(response.context['form'].errors, expected_errors) + self.assertIn('Check mandatory fields.', response.body) + + # expected extra context data + expected_post_url = reverse("editorial.board.edit", args=[self.journal.id, member.id]) + expected_board_url = reverse('editorial.board', args=[self.journal.pk, ]) + self.assertEqual(response.context['post_url'], expected_post_url) + self.assertEqual(response.context['board_url'], expected_board_url) + self.assertEqual(response.context['board_member'].pk, member.pk) + + # check relations: board remains the same, but no members were edited + self.assertIsNotNone(self.issue.editorialboard) + self.assertEqual(len(self.issue.editorialboard.editorialmember_set.all()), 1) + member_from_db = self.issue.editorialboard.editorialmember_set.all()[0] + + self.assertEqual(member_from_db.role, member.role) + self.assertEqual(member_from_db.first_name, member.first_name) + self.assertEqual(member_from_db.last_name, member.last_name) + self.assertEqual(member_from_db.email, member.email) + self.assertEqual(member_from_db.institution, member.institution) + self.assertEqual(member_from_db.link_cv, member.link_cv) + self.assertEqual(member_from_db.state, member.state) + self.assertEqual(member_from_db.country, member.country) + + # check audit logs: no logs generated + self.assertEqual(pre_submittion_audit_logs_count, 0) + self.assertEqual(AuditLogEntry.objects.all().count(), 0) + + def test_DELETE_board_memeber_valid_POST_is_valid(self): + """ + User of the group "Editors" successfully DELETE a board member + """ + # with + member = editorial_modelfactories.EditorialMemberFactory.create() + member.board = EditorialBoard.objects.create(issue=self.issue) + member.save() + response = self.app.get(reverse("editorial.board.delete", args=[self.journal.id, member.id]), user=self.user) + form = response.forms['member-form'] + pre_submittion_audit_logs_count = AuditLogEntry.objects.all().count() + + # when + response = form.submit().follow() + + # then + + # check frontend + self.assertIn('Board Member DELETED successfully.', response.body) + self.assertTemplateUsed(response, 'board/board_list.html') + + # check that member was deleted from DB + # using .filter(pk=...) to receive a [emtpy] list, and avoid DoesNotExist exception + members_from_db = EditorialMember.objects.filter(pk=member.pk) + self.assertNotIn(member, members_from_db) + + # check relations: board remains the same, but no members were edited + self.assertIsNotNone(self.issue.editorialboard) + self.assertNotIn(member, self.issue.editorialboard.editorialmember_set.all()) + self.assertEqual(len(self.issue.editorialboard.editorialmember_set.all()), 0) + + # check audit log: + self.assertEqual(pre_submittion_audit_logs_count, 0) + audit_entries = AuditLogEntry.objects.all() + self.assertEqual(audit_entries.count(), 1) + entry = audit_entries[0] + self.assertEqual(entry.action_flag, DELETION) # Flag correspond with DELETE action + self.assertEqual(entry.content_type.model_class(), EditorialMember) + audited_obj = entry.get_audited_object() + self.assertIsNone(audited_obj) # audited object (member) was deleted, so, no referece to it + expected_change_msg = u'Record DELETED (%s, pk: %s): %s' % (member.pk, member._meta.verbose_name, unicode(member)) + self.assertEqual(entry.change_message, expected_change_msg) + + +class EditorialMemberFormAsLibrarianTests(EditorialMemberFormAsEditorTests): + """ + Excecute the same tests that an Editors (EditorialMemberFormAsEditorTests), the setUp is almost the same. + Only change is that the self.user is assigned as a member of "Librarian" group instead of "Editors" group. + """ + def setUp(self): + super(EditorialMemberFormAsLibrarianTests, self).setUp() + # change user group to belong to Librarian group + self.user.groups.clear() + group = modelfactories.GroupFactory(name="Librarian") + self.user.groups.add(group) + self.user.save() + _add_required_permission_to_group(group) + def tearDown(self): + super(EditorialMemberFormAsLibrarianTests, self).tearDown() diff --git a/scielomanager/editorialmanager/tests/test_pages.py b/scielomanager/editorialmanager/tests/test_pages.py index 811ab92c..01dee8f3 100644 --- a/scielomanager/editorialmanager/tests/test_pages.py +++ b/scielomanager/editorialmanager/tests/test_pages.py @@ -1,20 +1,27 @@ -#coding: utf-8 +# coding: utf-8 from django_webtest import WebTest from django.core.urlresolvers import reverse - +from django.conf import settings from journalmanager.tests import modelfactories +from . import modelfactories as editorial_modelfactories +from .test_forms import _makePermission +from editorialmanager.models import EditorialMember, EditorialBoard -class IndexPageTests(WebTest): +def _set_permission_to_group(perm, group): + perm_add_editorialmember = _makePermission(perm=perm, model='editorialmember') + group.permissions.add(perm_add_editorialmember) - def setUp(self): - #create a group 'Editors' - group = modelfactories.GroupFactory(name="Editors") - #create a user and set group 'Editors' +class PagesAsEditorTests(WebTest): + + def setUp(self): + # create a group 'Editors' + self.group = modelfactories.GroupFactory(name="Editors") + # create a user and set group 'Editors' self.user = modelfactories.UserFactory(is_active=True) - self.user.groups.add(group) + self.user.groups.add(self.group) self.collection = modelfactories.CollectionFactory.create() self.collection.add_user(self.user, is_manager=False) @@ -23,20 +30,223 @@ def setUp(self): self.journal = modelfactories.JournalFactory.create() self.journal.join(self.collection, self.user) - #set the user as editor of the journal + # set the user as editor of the journal self.journal.editor = self.user + + # create an issue + self.issue = modelfactories.IssueFactory.create() + self.issue.journal = self.journal self.journal.save() + self.issue.save() - def test_logged_editor_access_to_index_of_editorial_manager(self): + def tearDown(self): + pass + def test_logged_user_access_to_index(self): + """ + User loggin and access 'editorial.index' page, + the result must show the journal who is editable by the logged user + """ + # when response = self.app.get(reverse('editorial.index'), user=self.user) - + # then + self.assertTrue(self.user.get_profile().is_editor or self.user.get_profile().is_librarian) self.assertTemplateUsed(response, 'journal/journal_list.html') - def test_logged_editor_access_to_index_and_see_your_journals(self): - - response = self.app.get(reverse('editorial.index'), user=self.user) - + journals_in_response = response.context['objects_journal'] + self.assertIsNotNone(journals_in_response) + journals_in_response = journals_in_response.object_list + self.assertIn(self.journal, journals_in_response) + response.mustcontain(self.journal.title) + journal_detail_link_url = reverse("editorial.journal.detail", args=[self.journal.pk]) + response.mustcontain(journal_detail_link_url) + + def test_logged_user_access_to_journal_detail(self): + # when + response = self.app.get(reverse("editorial.journal.detail", args=[self.journal.pk]), user=self.user) + # then + self.assertTemplateUsed(response, 'journal/journal_detail.html') + journal_in_response = response.context['journal'] + self.assertIsNotNone(journal_in_response) response.mustcontain(self.journal.title) - self.assertTemplateUsed(response, 'journal/journal_list.html') + def test_user_access_links_to_add_when_have_permissions(self): + # with + member = editorial_modelfactories.EditorialMemberFactory.create() + member.board = EditorialBoard.objects.create(issue=self.issue) + member.save() + add_url = reverse("editorial.board.add", args=[self.journal.id, self.issue.id]) + board_url = reverse("editorial.board", args=[self.journal.pk]) + # when + response = self.app.get(board_url, user=self.user) + + # then + + # have no link to ADD, EDIT or DELETE a board member + self.assertNotIn(add_url, response.body) + # when gain permission to ADD editorial member, the link is shown + _set_permission_to_group('add_editorialmember', self.group) + response = self.app.get(board_url, user=self.user) + self.assertEqual(response.status_code, 200) + self.assertIn(add_url, response.body) + + def test_user_access_links_to_edit_when_have_permissions(self): + # with + member = editorial_modelfactories.EditorialMemberFactory.create() + member.board = EditorialBoard.objects.create(issue=self.issue) + member.save() + edit_url = reverse("editorial.board.edit", args=[self.journal.id, member.id]) + board_url = reverse("editorial.board", args=[self.journal.pk]) + # when + response = self.app.get(board_url, user=self.user) + + # then + # have no link to ADD, EDIT or DELETE a board member + self.assertNotIn(edit_url, response.body) + # when gain permission to EDIT editorial member, the link is shown + _set_permission_to_group('change_editorialmember', self.group) + response = self.app.get(board_url, user=self.user) + self.assertEqual(response.status_code, 200) + self.assertIn(edit_url, response.body) + + def test_user_access_links_to_delete_when_have_permissions(self): + # with + member = editorial_modelfactories.EditorialMemberFactory.create() + member.board = EditorialBoard.objects.create(issue=self.issue) + member.save() + delete_url = reverse("editorial.board.delete", args=[self.journal.id, member.id]) + board_url = reverse("editorial.board", args=[self.journal.pk]) + # when + response = self.app.get(reverse("editorial.board", args=[self.journal.pk]), user=self.user) + + # then + + # have no link to ADD, EDIT or DELETE a board member + self.assertNotIn(delete_url,response.body) + + # when gain permission to DELETE editorial member, the link is shown + _set_permission_to_group('delete_editorialmember', self.group) + response = self.app.get(board_url, user=self.user) + self.assertEqual(response.status_code, 200) + self.assertIn(delete_url, response.body) + + def test_unauthorized_user_access_to_add_member_page(self): + """ + A user without permissions to EDIT board member, when try to access the *EDIT* page, + will be redirected to ``settings.AUTHZ_REDIRECT_URL`` + """ + # when + response = self.app.get( + reverse("editorial.board.add", args=[self.journal.id, self.issue.id]), + user=self.user, + expect_errors=True) + # then + self.assertEqual(response.status_code, 302) + self.assertIn(settings.AUTHZ_REDIRECT_URL, response.location) + self.assertTemplateUsed(response.follow(), 'accounts/unauthorized.html') + + def test_authorized_user_access_to_add_member_page(self): + """ + A user with permissions to ADD board member, when try to access the *ADD* page, + will see the correct page + """ + # with + _set_permission_to_group('add_editorialmember', self.group) + # when + response = self.app.get( + reverse("editorial.board.add", args=[self.journal.id, self.issue.id]), + user=self.user) + # when + self.assertEqual(response.status_code, 200) + self.assertTemplateUsed(response, 'board/board_member_edit.html') + + def test_unauthorized_user_access_to_edit_member_page(self): + """ + A user without permissions to EDIT board member, when try to access the *EDIT* page, + will be redirected to ``settings.AUTHZ_REDIRECT_URL`` + """ + # with + member = editorial_modelfactories.EditorialMemberFactory.create() + member.board = EditorialBoard.objects.create(issue=self.issue) + member.save() + # when + response = self.app.get( + reverse("editorial.board.edit", args=[self.journal.id, member.id]), + user=self.user, + expect_errors=True) + # then + self.assertEqual(response.status_code, 302) + self.assertIn(settings.AUTHZ_REDIRECT_URL, response.location) + self.assertTemplateUsed(response.follow(), 'accounts/unauthorized.html') + + def test_authorized_user_access_to_edit_member_page(self): + """ + A user with permissions to EDIT board member, when try to access the *EDIT* page, + will see the correct page + """ + # with + member = editorial_modelfactories.EditorialMemberFactory.create() + member.board = EditorialBoard.objects.create(issue=self.issue) + member.save() + _set_permission_to_group('change_editorialmember', self.group) + # when + response = self.app.get( + reverse("editorial.board.edit", args=[self.journal.id, member.id]), + user=self.user) + # when + self.assertEqual(response.status_code, 200) + self.assertTemplateUsed(response, 'board/board_member_edit.html') + + def test_unauthorized_user_access_to_delete_member_page(self): + """ + A user without permissions to DELETE board member, when try to access the *DELETE* page, + will be redirected to ``settings.AUTHZ_REDIRECT_URL`` + """ + # with + member = editorial_modelfactories.EditorialMemberFactory.create() + member.board = EditorialBoard.objects.create(issue=self.issue) + member.save() + # when + response = self.app.get( + reverse("editorial.board.delete", args=[self.journal.id, member.id]), + user=self.user, + expect_errors=True) + # then + self.assertEqual(response.status_code, 302) + self.assertIn(settings.AUTHZ_REDIRECT_URL, response.location) + self.assertTemplateUsed(response.follow(), 'accounts/unauthorized.html') + + def test_authorized_user_access_to_delete_member_page(self): + """ + A user with permissions to DELETE board member, when try to access the *DELETE* page, + will see the correct page + """ + # with + member = editorial_modelfactories.EditorialMemberFactory.create() + member.board = EditorialBoard.objects.create(issue=self.issue) + member.save() + _set_permission_to_group('delete_editorialmember', self.group) + # when + response = self.app.get( + reverse("editorial.board.delete", args=[self.journal.id, member.id]), + user=self.user) + # when + self.assertEqual(response.status_code, 200) + self.assertTemplateUsed(response, 'board/board_member_delete.html') + + +class PagesAsLibrarianTests(PagesAsEditorTests): + """ + Excecute the same tests that an Editors (PagesAsEditorTests), the setUp is almost the same. + Only change is that the self.user is assigned as a member of "Librarian" group instead of "Editors" group. + """ + def setUp(self): + super(PagesAsLibrarianTests, self).setUp() + # change user group to belong to Librarian group + self.user.groups.clear() + self.group = modelfactories.GroupFactory(name="Librarian") + self.user.groups.add(self.group) + self.user.save() + + def tearDown(self): + super(PagesAsLibrarianTests, self).tearDown() diff --git a/scielomanager/journalmanager/templates/journalmanager/includes/form_add_user_to_collection.html b/scielomanager/journalmanager/templates/journalmanager/includes/form_add_user_to_collection.html new file mode 100644 index 00000000..ab034215 --- /dev/null +++ b/scielomanager/journalmanager/templates/journalmanager/includes/form_add_user_to_collection.html @@ -0,0 +1,13 @@ +{% load i18n %} +

{% trans 'Choose a user to be part of this collection' %}:

+ + {% csrf_token %} + + + + \ No newline at end of file diff --git a/scielomanager/journalmanager/templates/journalmanager/user_list.html b/scielomanager/journalmanager/templates/journalmanager/user_list.html index b2453ec8..f0215ed1 100644 --- a/scielomanager/journalmanager/templates/journalmanager/user_list.html +++ b/scielomanager/journalmanager/templates/journalmanager/user_list.html @@ -10,10 +10,10 @@ {% block content %} {% if perms.auth.add_user %}
-
- {% trans 'New' %} +
+ {% include 'journalmanager/includes/form_add_user_to_collection.html' %}
-
+
{% simple_pagination users %}
@@ -66,6 +66,11 @@ {% endif %} {% endif %} +
  • + + {% trans 'Exclude from this collection' %} + +
  • @@ -74,3 +79,9 @@ {% pagination users %} {% endblock %} + +{% block extrafooter %} + +{% endblock %} diff --git a/scielomanager/journalmanager/tests/tests_forms.py b/scielomanager/journalmanager/tests/tests_forms.py index 0f959d75..475fb66c 100644 --- a/scielomanager/journalmanager/tests/tests_forms.py +++ b/scielomanager/journalmanager/tests/tests_forms.py @@ -2081,6 +2081,7 @@ def test_collections_field_must_only_display_collections_the_user_is_bound(self) class IssueBaseFormClassTests(unittest.TestCase): + def test_basic_structure(self): issue_form = forms.IssueBaseForm() @@ -3919,6 +3920,98 @@ def test_sections_must_not_be_trashed(self): self.assertRaises(ValueError, lambda: form.set('section', str(trashed_section.pk))) + def test_ADD_issues_with_valid_form_and_invalid_formset_raise_validation_error(self): + """ + TEST BUG #1018. + Is the form submitted, are valid, but, title formset is invalid, the submittion + must not raise any exception, only notify the wrong input message to user + """ + perm_issue_change = _makePermission(perm='add_issue', + model='issue', app_label='journalmanager') + perm_issue_list = _makePermission(perm='list_issue', + model='issue', app_label='journalmanager') + self.user.user_permissions.add(perm_issue_change) + self.user.user_permissions.add(perm_issue_list) + for t in ['regular', 'supplement', 'special']: + form = self.app.get(reverse('issue.add_%s' % t, + args=[self.journal.pk]), user=self.user).forms['issue-form'] + + if t == 'supplement': + form['number'] = '' + form['volume'] = '29' + form['suppl_type'] = 'volume' + form['suppl_text'] = 'suppl.X' + elif t == 'special': + form['number'] = '3' + form['spe_type'] = 'number' + form['spe_text'] = 'X' + else: # regular + form['number'] = '3' + form['volume'] = '29' + + form['total_documents'] = '16' + form.set('ctrl_vocabulary', 'nd') + + form['publication_start_month'] = '9' + form['publication_end_month'] = '11' + form['publication_year'] = '2012' + form['is_marked_up'] = False + form['editorial_standard'] = 'other' + # inline title formset + form.set('title-0-language', '') + form.set('title-0-id', '') + form.set('title-0-title', 'La mexicanidad y el neoindianismo hoy') + response = form.submit() + self.assertTrue(response.context['add_form'].is_valid()) + self.assertFalse(response.context['titleformset'].is_valid()) + self.assertIn('There are some errors or missing data.', response.body) + + def test_EDIT_issues_with_valid_form_and_invalid_formset_raise_validation_error(self): + """ + TEST BUG #1018. + Is the form submitted, are valid, but, title formset is invalid, the submittion + must not raise any exception, only notify the wrong input message to user + """ + perm_issue_change = _makePermission(perm='add_issue', + model='issue', app_label='journalmanager') + perm_issue_list = _makePermission(perm='list_issue', + model='issue', app_label='journalmanager') + self.user.user_permissions.add(perm_issue_change) + self.user.user_permissions.add(perm_issue_list) + for t in ['regular', 'supplement', 'special']: + issue = modelfactories.IssueFactory(journal=self.journal, suppl_text='', type=t) + form = self.app.get(reverse('issue.edit', args=[self.journal.pk, issue.pk]), user=self.user).forms['issue-form'] + + if t == 'supplement': + form['number'] = '' + form['volume'] = '29' + form['suppl_type'] = 'volume' + form['suppl_text'] = 'suppl.X' + elif t == 'special': + form['number'] = '3' + form['spe_type'] = 'number' + form['spe_text'] = 'X' + else: # regular + form['number'] = '3' + form['volume'] = '29' + + form['total_documents'] = '16' + form.set('ctrl_vocabulary', 'nd') + + form['publication_start_month'] = '9' + form['publication_end_month'] = '11' + form['publication_year'] = '2012' + form['is_marked_up'] = False + form['editorial_standard'] = 'other' + # inline title formset + form.set('title-0-language', '') + form.set('title-0-id', '') + form.set('title-0-title', 'La mexicanidad y el neoindianismo hoy') + response = form.submit() + self.assertTrue(response.context['add_form'].is_valid()) + self.assertFalse(response.context['titleformset'].is_valid()) + self.assertIn('There are some errors or missing data.', response.body) + class SearchFormTests(WebTest): diff --git a/scielomanager/journalmanager/tests/tests_pages.py b/scielomanager/journalmanager/tests/tests_pages.py index a5f76444..df7284bc 100644 --- a/scielomanager/journalmanager/tests/tests_pages.py +++ b/scielomanager/journalmanager/tests/tests_pages.py @@ -364,6 +364,21 @@ def test_logged_user_access(self): self.assertTemplateUsed(response, 'journalmanager/user_list.html') + def test_remove_user_from_collection(self): + perm = _makePermission(perm='change_user', model='user', app_label='auth') + self.user.user_permissions.add(perm) + + other_user = auth.UserF(is_active=True) + self.collection.add_user(other_user) + + response = self.app.get(reverse('user.index'), user=self.user) + + self.assertTrue(other_user.username in response.body) + + response = self.app.get(reverse('user.exclude_user_from_collection', args=[other_user.pk]), user=self.user) + + self.assertFalse(other_user.username in response.body) + def test_logged_user_access_users_not_being_manager_of_the_collection(self): user = auth.UserF(is_active=True) response = self.app.get(reverse('user.index'), user=user).follow() diff --git a/scielomanager/journalmanager/urls.py b/scielomanager/journalmanager/urls.py index c37458da..0e51fc04 100644 --- a/scielomanager/journalmanager/urls.py +++ b/scielomanager/journalmanager/urls.py @@ -49,6 +49,8 @@ # Users Tools url(r'^user/$', views.user_index, name="user.index"), url(r'^user/new/$', views.add_user, name="user.add"), + url(r'^user/add_to_collection/$', views.add_user_to_collection, name="user.add_user_to_collection"), + url(r'^user/exclude_from_collection/(?P\d+)$', views.exclude_user_from_collection, name="user.exclude_user_from_collection"), url(r'^user/(?P\d+)/edit/$', views.add_user, name="user.edit"), url(r'^user/(?P\d+)/toggle_availability/$', views.toggle_user_availability, name='user.toggle_availability'), url(r'^user/(?P\d+)/toggle_active_collection/(?P\d+)$', diff --git a/scielomanager/journalmanager/views.py b/scielomanager/journalmanager/views.py index 2c9b331c..f291cd11 100644 --- a/scielomanager/journalmanager/views.py +++ b/scielomanager/journalmanager/views.py @@ -54,6 +54,14 @@ user_request_context = usercontext.get_finder() +def _user_has_access(user): + + collection = models.Collection.userobjects.active() + + if not collection.is_managed_by_user(user): + return False + + return True def get_first_letter(objects_all): """ @@ -385,23 +393,61 @@ def user_index(request): users = get_paginated(col_users, request.GET.get('page', 1)) + elegible_users = models.User.objects.filter( + ~Q(usercollections__collection__in=[collection])).distinct('username').order_by('username') + t = loader.get_template('journalmanager/user_list.html') c = RequestContext(request, { 'users': users, + 'elegible_users': elegible_users }) return HttpResponse(t.render(c)) @permission_required('auth.change_user', login_url=settings.AUTHZ_REDIRECT_URL) +@user_passes_test(_user_has_access, login_url=settings.AUTHZ_REDIRECT_URL) +def add_user_to_collection(request): + """ + Add a existing user to the active collection + """ + + collection = models.Collection.userobjects.active() + + if request.method == 'POST': + user_id = request.POST.get('user_id', None) + if user_id: + user = get_object_or_404(User, id=user_id) + collection.add_user(user) + return HttpResponseRedirect(reverse('user.index')) + else: + return HttpResponseRedirect(reverse('user.add')) + + +@permission_required('auth.change_user', login_url=settings.AUTHZ_REDIRECT_URL) +@user_passes_test(_user_has_access, login_url=settings.AUTHZ_REDIRECT_URL) +def exclude_user_from_collection(request, user_id=None): + """ + Remove a user from the active collection + """ + + collection = models.Collection.userobjects.active() + + if user_id: + user = get_object_or_404(User, id=user_id) + + collection.remove_user(user) + + return HttpResponseRedirect(reverse('user.index')) + + +@permission_required('auth.change_user', login_url=settings.AUTHZ_REDIRECT_URL) +@user_passes_test(_user_has_access, login_url=settings.AUTHZ_REDIRECT_URL) def add_user(request, user_id=None): """ Handles new and existing users """ collection = models.Collection.userobjects.active() - if not collection.is_managed_by_user(request.user): - return HttpResponseRedirect(settings.AUTHZ_REDIRECT_URL) - if user_id is None: user = User() else: @@ -461,7 +507,7 @@ def add_user(request, user_id=None): 'mode': 'user_journal', 'user_name': request.user.pk, 'usercollectionsformset': usercollectionsformset, - 'user': user, + 'user': user }, context_instance=RequestContext(request)) @@ -783,7 +829,7 @@ def get_issue_form_by_type(issue_type, request, journal, instance): audit_old_values = helpers.collect_old_values(issue, form, [titleformset, ]) - if form.is_valid(): + if form.is_valid() and titleformset.is_valid(): saved_issue = form.save(commit=False) saved_issue.journal = issue.journal saved_issue.save() @@ -891,7 +937,7 @@ def get_issue_form_by_type(issue_type, request, journal, instance=None, initial= add_form = get_issue_form_by_type(issue_type, request, journal, instance=issue) titleformset = IssueTitleFormSet(request.POST, instance=issue, prefix='title') - if add_form.is_valid(): + if add_form.is_valid() and titleformset.is_valid(): saved_issue = add_form.save(commit=False) saved_issue.journal = journal saved_issue.type = issue_type diff --git a/scielomanager/scielomanager/static/css/style.css b/scielomanager/scielomanager/static/css/style.css index 8f5ad4cd..c1e5eee9 100644 --- a/scielomanager/scielomanager/static/css/style.css +++ b/scielomanager/scielomanager/static/css/style.css @@ -538,3 +538,7 @@ table._listings h4 { .accordion-board-members li div:hover { background-color: #f5f5f5; } + +.flat { + color: red; +} diff --git a/scielomanager/validator/templates/validator/includes/xml_annotated.html b/scielomanager/validator/templates/validator/includes/xml_annotated.html index c27f4937..a270a5df 100644 --- a/scielomanager/validator/templates/validator/includes/xml_annotated.html +++ b/scielomanager/validator/templates/validator/includes/xml_annotated.html @@ -62,6 +62,10 @@

    {% trans "XML validation output" %}:

    {% endfor %} + {% else %} +
    +

    {% trans "The XML seems to be valid." %}

    +
    {% endif %} {# XML ANNOTATED #}
    diff --git a/scielomanager/validator/tests/doubles.py b/scielomanager/validator/tests/doubles.py index 4f19e176..85e28f00 100644 --- a/scielomanager/validator/tests/doubles.py +++ b/scielomanager/validator/tests/doubles.py @@ -40,3 +40,38 @@ class DummyError(object): error_list.append(DummyError()) return False, error_list + + +#------------------ +# utils.analyze_xml +#------------------ +def make_stub_analyze_xml(type): + """Factory for utils.analyze_xml stub functions. + + `type` can be: 'valid' or 'invalid'. + """ + sample_xml = "
    foo
    " + sample_meta = {} + + if type == 'valid': + result = { + 'annotations': sample_xml, + 'validation_errors': None, + 'meta': sample_meta, + } + err = None + elif type == 'invalid': + result = { + 'annotations': sample_xml, + 'validation_errors': [], + 'meta': sample_meta, + } + err = None + else: + raise ValueError('Unknown type value') + + def stub__analyze_xml(f): + return result, err + + return stub__analyze_xml + diff --git a/scielomanager/validator/tests/tests_pages.py b/scielomanager/validator/tests/tests_pages.py index a579fbe2..9cf706e8 100644 --- a/scielomanager/validator/tests/tests_pages.py +++ b/scielomanager/validator/tests/tests_pages.py @@ -161,7 +161,13 @@ def test_submit_valid_url_then_form_is_valid(self): """ # with self._addWaffleFlag() - self._mocker_replace_stylechecker() + + stub_analyze_xml = doubles.make_stub_analyze_xml('valid') + mock_utils = self.mocker.replace('validator.utils') + mock_utils.analyze_xml + self.mocker.result(stub_analyze_xml) + self.mocker.replay() + # when page = self.app.get( reverse('validator.packtools.stylechecker',), @@ -170,20 +176,11 @@ def test_submit_valid_url_then_form_is_valid(self): form['type'] = 'url' form['url'] = 'http://example.com/foo/bar/valid.xml' response = form.submit() + # then self.assertTemplateUsed(response, 'validator/packtools.html') - self.assertEqual(response.status_code, 200) self.assertTrue(response.context['form'].is_valid()) - expected_results = { - 'results': None, - 'xml_exception': None, - 'packtools_version': PACKTOOLS_VERSION - } - self.assertEqual(response.context['results'], expected_results['results']) - self.assertEqual(response.context['xml_exception'], expected_results['xml_exception']) - self.assertEqual(response.context['packtools_version'], expected_results['packtools_version']) - def test_submit_text_file_then_form_not_valid(self): """ Submitting a text file will raise a from validation error @@ -200,7 +197,6 @@ def test_submit_text_file_then_form_not_valid(self): } ) # then - self.assertEqual(200, response.status_code) form = response.context['form'] self.assertFalse(response.context['form'].is_valid()) expected_errors = { @@ -225,7 +221,6 @@ def test_submit_image_file_then_form_not_valid(self): } ) # then - self.assertEqual(200, response.status_code) form = response.context['form'] self.assertFalse(response.context['form'].is_valid()) expected_errors = { @@ -241,9 +236,17 @@ def test_submit_valid_xml_file_then_get_annotations_form_valid(self): """ # with self._addWaffleFlag() - self._mocker_replace_stylechecker(with_annotations=True) + + stub_analyze_xml = doubles.make_stub_analyze_xml('valid') + mock_utils = self.mocker.replace('validator.utils') + mock_utils.analyze_xml + self.mocker.result(stub_analyze_xml) + + self.mocker.replay() + test_file_path = _get_test_xml_abspath('with_style_errors.xml') test_file = get_temporary_xml_file(test_file_path) + # when response = self.client.post( reverse('validator.packtools.stylechecker',), @@ -252,26 +255,16 @@ def test_submit_valid_xml_file_then_get_annotations_form_valid(self): 'file': test_file } ) + # then form = response.context['form'] xml_exception = response.context['xml_exception'] results = response.context['results'] - self.assertEqual(200, response.status_code) - self.assertTrue(response.context['form'].is_valid()) + self.assertTrue(form.is_valid()) self.assertTemplateUsed('validator/packtools.html') - self.assertIsNotNone(results) # have some results - self.assertEqual(results.keys(), ['validation_errors', 'meta', 'annotations']) - expected_meta = { - 'article_title': 'HIV/AIDS knowledge among men who have sex with men: applying the item response theory', - 'issue_year': '2014', - 'journal_title': u'Revista de Sa\xfade P\xfablica', - 'journal_pissn': '0034-8910', - 'journal_eissn': '1518-8787', - 'issue_number': '2', - 'issue_volume': '48' - } - self.assertEqual(results['meta'], expected_meta) - self.assertEqual(len(results['validation_errors']), 1) - self.assertEqual(results['annotations'], "some annotations in xml string") + # the template cares about this keys + self.assertEqual(results.keys(), + ['validation_errors', 'meta', 'annotations']) + diff --git a/scielomanager/validator/utils.py b/scielomanager/validator/utils.py index ee710109..2f3b27ae 100644 --- a/scielomanager/validator/utils.py +++ b/scielomanager/validator/utils.py @@ -51,21 +51,21 @@ def analyze_xml(file): else: status, errors = xml.validate_all() + err_xml = lxml.etree.tostring(xml.annotate_errors(), + pretty_print=True, encoding='utf-8', xml_declaration=True) - if not status: - err_xml = lxml.etree.tostring(xml.annotate_errors(), - pretty_print=True, encoding='utf-8', xml_declaration=True) - - err_list = ((error, count(error, errors, lambda x: x.message)) for error in errors) + result = { + 'annotations': err_xml, + 'validation_errors': None, + 'meta': xml.meta, + } - err_filter = make_error_filter(lambda x: x[0].message) - unique_err_list = filter(err_filter, err_list) + if not status: + err_filter = make_error_filter(lambda x: x.message) + unique_err_list = filter(err_filter, errors) + err_list = [(error, count(error, errors, lambda x: x.message)) for error in unique_err_list] - result = { - 'annotations': err_xml, - 'validation_errors': unique_err_list, - 'meta': xml.meta, - } + result['validation_errors'] = err_list return result, err