Skip to content

Commit 10a5a41

Browse files
committed
Merge branch 'release/v1.7.0'
2 parents e6369f2 + 89b523c commit 10a5a41

File tree

141 files changed

+4425
-748
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

141 files changed

+4425
-748
lines changed

.gitignore

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,13 @@ Thumbs.db
1616
fabfile.py
1717
# Users media
1818
app/media/*
19-
19+
# Topic Skeleton images exception rules
20+
!app/media/topics-skeletons/body-count.jpg
21+
!app/media/topics-skeletons/corporate-nets.jpg
22+
!app/media/topics-skeletons/family-affairs-schema.jpg
23+
!app/media/topics-skeletons/family-affairs.jpg
24+
!app/media/topics-skeletons/political-influence.jpg
25+
!app/media/topics-skeletons/supply-chain.jpg
2026
# Binary files and database
2127
*.db
2228

.travis.yml

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,27 @@
11
language: python
22
python:
3-
- "2.7"
3+
- '2.7'
44
notifications:
55
irc:
66
channels:
7-
- "anchor.foonetic.net#jplusplus"
7+
- anchor.foonetic.net#jplusplus
88
on_success: always
99
on_failure: always
10+
slack:
11+
secure: lc2NcRnOWDXmxoQt8wJh0947g9+cIpDaSe2SAJlXigdPL8qzq04GriV0KmRlcVBvjqB4bViWfEsn5ldtGBBiu5wyQ/nIckESfV/8zpLmUM5uEqhx1U3f9YJLDLjH1THNAJBIL9EI9DKo9IHF2uiZF0/sGj4ejorjiZC8J+K3MWk=
1012
before_install:
11-
- export COVERALLS_REPO_TOKEN=6opRu71NU2ODTYXULbiEUS1EPTcDNcZqI
12-
- export COVERALLS_SERVICE_NAME=travis-ci
13-
- export DATABASE_URL=sqlite:///test.db
14-
- export DJANGO_SETTINGS_MODULE=app.settings_tests
15-
- export NEO4J_PORT=7474
16-
- export NEO4J_VERSION=1.9.1
13+
- export COVERALLS_REPO_TOKEN=UDtLCy1He2SylMvJMjq1Uu9f1zVFbTim8
14+
- export COVERALLS_SERVICE_NAME=travis-ci
15+
- export DATABASE_URL=sqlite:///test.db
16+
- export DJANGO_SETTINGS_MODULE=app.settings_tests
17+
- export NEO4J_PORT=7474
18+
- export NEO4J_VERSION=1.9.1
1719
install:
18-
- ./install_local_neo4j.bash $NEO4J_VERSION
19-
- ./lib/neo4j/bin/neo4j start
20-
- pip install -r test_requirements.txt
21-
- pip install coveralls
20+
- ./install_local_neo4j.bash $NEO4J_VERSION
21+
- ./lib/neo4j/bin/neo4j start
22+
- pip install -r test_requirements.txt
23+
- pip install coveralls
2224
script:
23-
- coverage run --source=app.detective ./manage.py test detective --pythonpath=. --traceback
25+
- coverage run --source=app.detective ./manage.py test detective --pythonpath=. --traceback
2426
after_success:
25-
- coveralls
27+
- coveralls

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ test:
106106
make startdb
107107
./manage.py syncdb -v 0 --noinput --traceback --pythonpath=. --settings=app.settings_tests
108108
# Launch test with coverage
109-
python -W ignore::DeprecationWarning $(COVERAGE) run --source=app.detective ./manage.py test detective --pythonpath=. --settings=app.settings_tests --traceback
109+
-python -W ignore::DeprecationWarning $(COVERAGE) run --source=app.detective ./manage.py test detective --pythonpath=. --settings=app.settings_tests --traceback
110110
# Send report to coveralls
111111
coveralls
112112
# Stop database in order to restore it

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
[License](https://github.com/jplusplus/detective.io/blob/master/LICENSE)
66
[Test coverage](https://coveralls.io/r/jplusplus/detective.io)
77
[Documentation](http://docs.detective.io/en/latest/)
8-
*Version 1.6.5 Seestern*
8+
*Version 1.7.0 Seepferd*
99

1010
## Installation
1111

app/detective/admin.py

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,17 @@
11
from app.detective import utils
2-
from app.detective.models import QuoteRequest, Topic, TopicToken, SearchTerm, Article, DetectiveProfileUser
2+
from app.detective.models import QuoteRequest
3+
from app.detective.models import Topic
4+
from app.detective.models import TopicSkeleton
5+
from app.detective.models import TopicToken
6+
from app.detective.models import SearchTerm
7+
from app.detective.models import Article
8+
from app.detective.models import DetectiveProfileUser
9+
from app.detective.models import Subscription
10+
from app.detective.models import PLANS_CHOICES
311
from django.conf import settings
412
from django.contrib import admin
13+
from django import forms
14+
from django.db import models
515
from django.db.models import CharField
616
from django.contrib.auth.admin import UserAdmin
717
from django.contrib.auth.models import User
@@ -130,6 +140,17 @@ def get_form(self, request, obj=None, **kwargs):
130140

131141
admin.site.register(Topic, TopicAdmin)
132142

143+
144+
class TopicSkeletonForm(forms.ModelForm):
145+
target_plans = forms.MultipleChoiceField(choices=PLANS_CHOICES)
146+
147+
148+
class TopicSkeletonAdmin(admin.ModelAdmin):
149+
form = TopicSkeletonForm
150+
list_display = ("title","picture", "picture_credits","ontology", "target_plans")
151+
152+
admin.site.register(TopicSkeleton, TopicSkeletonAdmin)
153+
133154
class ArticleAdmin(admin.ModelAdmin):
134155
save_on_top = True
135156
prepopulated_fields = {'slug': ('title',)}
@@ -142,6 +163,12 @@ class DetectiveProfileUserInline(admin.StackedInline):
142163
can_delete = False
143164
verbose_name_plural = 'detective settings'
144165

166+
class SubscriptionAdmin(admin.ModelAdmin):
167+
list_display = ("user", "email", "plan", "type", "name", "status")
168+
list_filter = ("status", )
169+
170+
admin.site.register(Subscription, SubscriptionAdmin)
171+
145172
# Define a new User admin
146173
class UserAdmin(UserAdmin):
147174
inlines = (DetectiveProfileUserInline, )

app/detective/exceptions.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
class UnavailableImage(Exception): pass
2+
class NotAnImage(Exception): pass

app/detective/fixtures/default_skeletons.json

Lines changed: 67 additions & 0 deletions
Large diffs are not rendered by default.

app/detective/fixtures/default_topics.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
"first_name": "detective",
77
"last_name": "detective",
88
"username": "detective",
9-
"password": ""
9+
"password": "",
10+
"is_superuser" : true
1011
}
1112
},
1213
{

app/detective/fixtures/tests_topics.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"ontology_as_owl": null,
1111
"ontology_as_mod": "testtopic",
1212
"about": "",
13-
"background": "",
13+
"background": "topics-skeletons/supply-chain.jpg",
1414
"public": true,
1515
"featured": true
1616
}

app/detective/individual.py

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,11 @@ def read_list(self, object_list, bundle):
6363
def create_detail(self, object_list, bundle):
6464
if not self.check_contribution_permission(object_list, bundle, 'add'):
6565
raise Unauthorized("Sorry, only staff or contributors can create resource.")
66+
# check if user can add regarding to his plan
67+
topic = get_topic_from_request(bundle.request)
68+
owner_profile = topic.author.detectiveprofileuser
69+
if owner_profile.nodes_max() > -1 and owner_profile.nodes_count()[topic.slug] >= owner_profile.nodes_max():
70+
raise Unauthorized("Sorry, you have to upgrade your plan.")
6671
return True
6772

6873
def update_detail(self, object_list, bundle):
@@ -489,7 +494,6 @@ def arr_no_dict_dup(in_arr):
489494

490495
def get_patch(self, request, **kwargs):
491496
self.method_check(request, allowed=['post'])
492-
#self.is_authenticated(request)
493497
self.throttle_check(request)
494498
self.is_authenticated(request)
495499
bundle = self.build_bundle(request=request)
@@ -498,7 +502,7 @@ def get_patch(self, request, **kwargs):
498502
try:
499503
node = model.objects.get(id=kwargs["pk"])
500504
except ObjectDoesNotExist:
501-
raise Http404("Sorry, unkown node.")
505+
raise Http404("Sorry, unknown node.")
502506
# Parse only body string
503507
body = json.loads(request.body) if type(request.body) is str else request.body
504508
# Copy data to allow dictionary resizing
@@ -515,29 +519,48 @@ def get_patch(self, request, **kwargs):
515519
attr = getattr(node, field)
516520
# It's a relationship
517521
if hasattr(attr, "_rel"):
522+
existing_rels_id = [r.id for r in attr.all()]
523+
rules = request.current_topic.get_rules()
524+
# Model that manages properties
525+
though = rules.model( self.get_model() ).field(field).get("through")
518526
related_model = attr._rel.relationship.target_model
519-
# Clean the field to avoid duplicates
520-
attr.clear()
521527
# Load the json-formated relationships
522528
data[field] = rels = value
523-
# For each relation...
529+
# For each relationship...
524530
for idx, rel in enumerate(rels):
525-
if type(rel) in [str, int]: rel = dict(id=rel)
531+
if type(rel) in [str, int]:
532+
rel = dict(id=rel)
526533
# We receied an object with an id
527534
if rel.has_key("id"):
535+
# skip for existing relationships
536+
if rel["id"] in existing_rels_id:
537+
continue
528538
# Get the related object
529539
try:
530540
related = related_model.objects.get(id=rel["id"])
531-
# Creates the relationship between the two objects
532-
attr.add(related)
533541
except ObjectDoesNotExist:
534542
del data[field][idx]
535543
# Too bad! Go to the next related object
536544
continue
545+
else:
546+
attr.add(related)
547+
# removing unused relationship
548+
rel_type = self.get_model_field(field).rel_type
549+
for relationship in node.node.relationships.all(types=[rel_type]):
550+
if relationship.end.id not in [rel["id"] for rel in rels]:
551+
relation_id = relationship.id
552+
relationship.delete()
553+
try:
554+
property = though.objects.get(_relationship=relation_id)
555+
except ObjectDoesNotExist:
556+
pass
557+
else:
558+
property.delete()
559+
537560
# It's a literal value and not the ID
538-
elif field != 'id' and value is not None:
561+
elif field != 'id':
539562
field_prop = self.get_model_field(field)._property
540-
if isinstance(field_prop, DateProperty):
563+
if isinstance(field_prop, DateProperty) and value != None:
541564
try:
542565
# It's a date and therefor `value` should be converted as it
543566
value = datetime.strptime(value, RFC_DATETIME_FORMAT)
@@ -570,15 +593,14 @@ def get_patch(self, request, **kwargs):
570593

571594
def get_relationships(self, request, **kwargs):
572595
# Extract node id from given node uri
573-
node_id = lambda uri: re.search(r'(\d+)$', uri).group(1)
596+
def node_id(uri) : return re.search(r'(\d+)$', uri).group(1)
574597
# Get the end of the given relationship
575-
rel_from = lambda rel, side: node_id(rel.__dict__["_dic"][side])
576-
connected = lambda rel, idx: rel_from(rel, "end") == idx or rel_from(rel, "start") == idx
598+
def rel_from(rel, side): return node_id(rel.__dict__["_dic"][side])
599+
def connected(rel, idx): return rel_from(rel, "end") == idx or rel_from(rel, "start") == idx
577600

578601
self.method_check(request, allowed=['get'])
579602
self.throttle_check(request)
580603
pk = kwargs['pk']
581-
582604
node = connection.nodes.get(pk)
583605
# Only the relationships for a given field
584606
if "field" in kwargs:

0 commit comments

Comments
 (0)