Changed the patent information text fields to individual fields for patent number, inventor, title, date and notes, with validation. Fixes issue #2411.

- Legacy-Id: 14499
This commit is contained in:
Henrik Levkowetz 2018-01-10 14:22:10 +00:00
parent 5b178aa26f
commit 5a1f3eaf36
3 changed files with 111 additions and 17 deletions

View file

@ -1,8 +1,12 @@
import datetime
import email
from django.utils.safestring import mark_safe
from django import forms
from django.core.validators import RegexValidator
from django.utils.safestring import mark_safe
import debug # pyflakes:ignore
from ietf.group.models import Group
from ietf.doc.fields import SearchableDocAliasField
@ -101,6 +105,31 @@ class DraftForm(forms.ModelForm):
}
help_texts = { 'sections': 'Sections' }
validate_patent_number = RegexValidator(
regex="^(([A-Z][A-Z]\d{6,12}|[A-Z][A-Z]\d{4}(\w{1,2}\d{5,7})?)[, ]*)+$",
message="Please enter one or more patent publication or application numbers as country code and serial number, e.g.: WO2017123456." )
def validate_string(s, letter_min, digit_min, space_min, message):
letter_count = 0
space_count = 0
digit_count = 0
s = s.strip()
for c in s:
if c.isalpha():
letter_count += 1
if c.isspace():
space_count += 1
if not (letter_count >= letter_min and digit_count >= digit_min and space_count >= space_min):
raise forms.ValidationError(message)
def validate_name(name):
return validate_string(name, letter_min=3, space_min=1, digit_min=0,
message="This doesn't look like a name. Please enter the actual inventor name.")
def validate_title(title):
return validate_string(title, letter_min=15, space_min=2, digit_min=0,
message="This doesn't look like a patent title. Please enter the actual patent title.")
class GenericDisclosureForm(forms.Form):
"""Custom ModelForm-like form to use for new Generic or NonDocSpecific Iprs.
If patent_info is submitted create a NonDocSpecificIprDisclosure object
@ -114,7 +143,14 @@ class GenericDisclosureForm(forms.Form):
holder_contact_info = forms.CharField(label="Other Info (address, phone, etc.)", max_length=255,widget=forms.Textarea,required=False, strip=False)
submitter_name = forms.CharField(max_length=255,required=False)
submitter_email = forms.EmailField(required=False)
patent_info = forms.CharField(max_length=255,widget=forms.Textarea, required=False, help_text="Patent, Serial, Publication, Registration, or Application/File number(s), Date(s) granted or applied for, Country, and any additional notes.", strip=False)
#patent_info = forms.CharField(max_length=255,widget=forms.Textarea, required=False, help_text="Patent, Serial, Publication, Registration, or Application/File number(s), Date(s) granted or applied for, Country, and any additional notes.", strip=False)
patent_number = forms.CharField(max_length=127, required=False, validators=[ validate_patent_number ],
help_text = "Patent publication or application number (2-letter country code followed by serial number)")
patent_inventor = forms.CharField(max_length=63, required=False, validators=[ validate_name ], help_text="Inventor name")
patent_title = forms.CharField(max_length=63, required=False, validators=[ validate_title ], help_text="Title of invention")
patent_date = forms.DateField(required=False, help_text="Date granted or applied for")
patent_notes = forms.CharField(max_length=127, required=False, widget=forms.Textarea)
has_patent_pending = forms.BooleanField(required=False)
statement = forms.CharField(max_length=255,widget=forms.Textarea,required=False, strip=False)
updates = SearchableIprDisclosuresField(required=False, help_text="If this disclosure <strong>updates</strong> other disclosures identify here which ones. Leave this field blank if this disclosure does not update any prior disclosures. <strong>Note</strong>: Updates to IPR disclosures must only be made by authorized representatives of the original submitters. Updates will automatically be forwarded to the current Patent Holder's Contact and to the Submitter of the original IPR disclosure.")
@ -132,7 +168,20 @@ class GenericDisclosureForm(forms.Form):
if not self.cleaned_data.get('same_as_ii_above'):
if not ( self.cleaned_data.get('submitter_name') and self.cleaned_data.get('submitter_email') ):
raise forms.ValidationError('Submitter information must be provided in section VII')
patent_fields = [ 'patent_'+k for k in ['number', 'inventor', 'title', 'date', ] ]
patent_values = [ cleaned_data.get(k) for k in patent_fields ]
if any(patent_values) and not all(patent_values):
for k in patent_fields:
if not cleaned_data.get(k):
self.add_error(k, "This field is required if you are filing a patent-specific disclosure.")
raise forms.ValidationError("A generic IPR disclosure cannot have any patent-specific information, "
"but a patent-specific disclosure must provide full patent information.")
patent_values = [str(v) for v in patent_values if v ] + [ cleaned_data['patent_notes'] ]
cleaned_data['patent_info'] = ('\n'.join(patent_values)).strip()
cleaned_data['patent_fields'] = patent_fields
return cleaned_data
def save(self, *args, **kwargs):
@ -140,6 +189,9 @@ class GenericDisclosureForm(forms.Form):
same_as_ii_above = nargs.get('same_as_ii_above')
del nargs['same_as_ii_above']
for k in self.cleaned_data['patent_fields'] + ['patent_fields', 'patent_notes']:
del nargs[k]
if self.cleaned_data.get('patent_info'):
obj = NonDocSpecificIprDisclosure(**nargs)
else:
@ -160,6 +212,12 @@ class IprDisclosureFormBase(forms.ModelForm):
"""Base form for Holder and ThirdParty disclosures"""
updates = SearchableIprDisclosuresField(required=False, help_text=mark_safe("If this disclosure <strong>updates</strong> other disclosures identify here which ones. Leave this field blank if this disclosure does not update any prior disclosures. Note: Updates to IPR disclosures must only be made by authorized representatives of the original submitters. Updates will automatically be forwarded to the current Patent Holder's Contact and to the Submitter of the original IPR disclosure."))
same_as_ii_above = forms.BooleanField(required=False)
patent_number = forms.CharField(max_length=127, required=True, validators=[ validate_patent_number ],
help_text = "Patent publication or application number (2-letter country code followed by serial number)")
patent_inventor = forms.CharField(max_length=63, required=True, validators=[ validate_name ], help_text="Inventor name")
patent_title = forms.CharField(max_length=63, required=True, validators=[ validate_title ], help_text="Title of invention")
patent_date = forms.DateField(required=True, help_text="Date granted or applied for")
patent_notes = forms.CharField(max_length=127, required=False, widget=forms.Textarea)
def __init__(self,*args,**kwargs):
super(IprDisclosureFormBase, self).__init__(*args,**kwargs)
@ -167,6 +225,7 @@ class IprDisclosureFormBase(forms.ModelForm):
self.fields['submitter_email'].required = False
self.fields['compliant'].initial = True
self.fields['compliant'].label = "This disclosure complies with RFC 3979"
patent_fields = [ 'patent_'+k for k in ['number', 'inventor', 'title', 'date', ] ]
if "ietfer_name" in self.fields:
self.fields["ietfer_name"].label = "Name"
if "ietfer_contact_email" in self.fields:
@ -175,7 +234,10 @@ class IprDisclosureFormBase(forms.ModelForm):
self.fields["ietfer_contact_info"].label = "Other info"
self.fields["ietfer_contact_info"].help_text = "Address, phone, etc."
if "patent_info" in self.fields:
self.fields["patent_info"].help_text = "Patent, Serial, Publication, Registration, or Application/File number(s), Date(s) granted or applied for, Country, and any additional notes"
self.fields['patent_info'].required = False
else:
for f in patent_fields:
del self.fields[f]
if "licensing" in self.fields:
self.fields["licensing_comments"].label = "Licensing information, comments, notes, or URL for further information"
if "submitter_claims_all_terms_disclosed" in self.fields:
@ -187,7 +249,7 @@ class IprDisclosureFormBase(forms.ModelForm):
"""This will be overridden"""
model = IprDisclosureBase
fields = '__all__'
def clean(self):
super(IprDisclosureFormBase, self).clean()
cleaned_data = self.cleaned_data
@ -198,6 +260,12 @@ class IprDisclosureFormBase(forms.ModelForm):
if not ( self.cleaned_data.get('submitter_name') and self.cleaned_data.get('submitter_email') ):
raise forms.ValidationError('Submitter information must be provided in section VII')
patent_fields = [ 'patent_'+k for k in ['number', 'inventor', 'title', 'date', 'notes'] ]
patent_values = [ cleaned_data.get(k) for k in patent_fields ]
patent_values = [ str(v) for v in patent_values if v ]
cleaned_data['patent_info'] = ('\n'.join(patent_values)).strip()
cleaned_data['patent_fields'] = patent_fields
return cleaned_data
class HolderIprDisclosureForm(IprDisclosureFormBase):
@ -220,8 +288,7 @@ class HolderIprDisclosureForm(IprDisclosureFormBase):
self.fields['licensing'].queryset = IprLicenseTypeName.objects.exclude(slug='none-selected')
def clean(self):
super(HolderIprDisclosureForm, self).clean()
cleaned_data = self.cleaned_data
cleaned_data = super(HolderIprDisclosureForm, self).clean()
if not self.data.get('iprdocrel_set-0-document') and not cleaned_data.get('other_designations'):
raise forms.ValidationError('You need to specify a contribution in Section IV')
return cleaned_data
@ -270,8 +337,7 @@ class ThirdPartyIprDisclosureForm(IprDisclosureFormBase):
exclude = [ 'by','docs','state','rel' ]
def clean(self):
super(ThirdPartyIprDisclosureForm, self).clean()
cleaned_data = self.cleaned_data
cleaned_data = super(ThirdPartyIprDisclosureForm, self).clean()
if not self.data.get('iprdocrel_set-0-document') and not cleaned_data.get('other_designations'):
raise forms.ValidationError('You need to specify a contribution in Section III')
return cleaned_data

View file

@ -292,7 +292,7 @@ class IprTests(TestCase):
ipr = iprs[0]
self.assertEqual(ipr.holder_legal_name, "Test Legal")
self.assertEqual(ipr.state.slug, 'pending')
self.assertTrue(isinstance(ipr.get_child(),GenericIprDisclosure))
self.assertTrue(isinstance(ipr.get_child(), GenericIprDisclosure))
def test_new_specific(self):
"""Add a new specific disclosure. Note: submitter does not need to be logged in.
@ -314,14 +314,16 @@ class IprTests(TestCase):
"iprdocrel_set-0-document": "%s" % draft.docalias_set.first().pk,
"iprdocrel_set-0-revisions": '00',
"iprdocrel_set-1-document": DocAlias.objects.filter(name__startswith="rfc").first().pk,
"patent_info": "none",
"patent_number": "SE12345678901",
"patent_inventor": "A. Nonymous",
"patent_title": "A method of transfering bits",
"patent_date": "2000-01-01",
"has_patent_pending": False,
"licensing": "royalty-free",
"submitter_name": "Test Holder",
"submitter_email": "test@holder.com",
})
self.assertEqual(r.status_code, 200)
# print r.content
self.assertTrue("Your IPR disclosure has been submitted" in unicontent(r))
iprs = IprDisclosureBase.objects.filter(title__icontains=draft.name)
@ -329,6 +331,8 @@ class IprTests(TestCase):
ipr = iprs[0]
self.assertEqual(ipr.holder_legal_name, "Test Legal")
self.assertEqual(ipr.state.slug, 'pending')
for item in [u'SE12345678901','A method of transfering bits','2000-01-01']:
self.assertIn(item, ipr.get_child().patent_info)
self.assertTrue(isinstance(ipr.get_child(),HolderIprDisclosure))
self.assertEqual(len(outbox),1)
self.assertTrue('New IPR Submission' in outbox[0]['Subject'])
@ -352,7 +356,10 @@ class IprTests(TestCase):
"iprdocrel_set-0-document": "%s" % draft.docalias_set.first().pk,
"iprdocrel_set-0-revisions": '00',
"iprdocrel_set-1-document": DocAlias.objects.filter(name__startswith="rfc").first().pk,
"patent_info": "none",
"patent_number": "SE12345678901",
"patent_inventor": "A. Nonymous",
"patent_title": "A method of transfering bits",
"patent_date": "2000-01-01",
"has_patent_pending": False,
"licensing": "royalty-free",
"submitter_name": "Test Holder",
@ -366,6 +373,8 @@ class IprTests(TestCase):
ipr = iprs[0]
self.assertEqual(ipr.holder_legal_name, "Test Legal")
self.assertEqual(ipr.state.slug, "pending")
for item in [u'SE12345678901','A method of transfering bits','2000-01-01' ]:
self.assertIn(item, ipr.get_child().patent_info)
self.assertTrue(isinstance(ipr.get_child(),ThirdPartyIprDisclosure))
self.assertEqual(len(outbox),1)
self.assertTrue('New IPR Submission' in outbox[0]['Subject'])
@ -391,7 +400,10 @@ class IprTests(TestCase):
"iprdocrel_set-0-document": "%s" % draft.docalias_set.first().pk,
"iprdocrel_set-0-revisions": '00',
"iprdocrel_set-1-document": DocAlias.objects.filter(name__startswith="rfc").first().pk,
"patent_info": "none",
"patent_number": "SE12345678901",
"patent_inventor": "A. Nonymous",
"patent_title": "A method of transfering bits",
"patent_date": "2000-01-01",
"has_patent_pending": False,
"licensing": "royalty-free",
"submitter_name": "Test Holder",
@ -415,7 +427,6 @@ class IprTests(TestCase):
draft = make_test_data()
url = urlreverse("ietf.ipr.views.new", kwargs={ "type": "specific" })
# successful post
empty_outbox()
r = self.client.post(url, {
"updates": "this is supposed to be an integer",
@ -426,7 +437,10 @@ class IprTests(TestCase):
"iprdocrel_set-INITIAL_FORMS": 0,
"iprdocrel_set-0-document": "%s" % draft.docalias_set.first().pk,
"iprdocrel_set-0-revisions": '00',
"patent_info": "none",
"patent_number": "SE12345678901",
"patent_inventor": "A. Nonymous",
"patent_title": "A method of transfering bits",
"patent_date": "2000-01-01",
"has_patent_pending": False,
"licensing": "royalty-free",
"submitter_name": "Test Holder",

View file

@ -162,7 +162,21 @@
<h2>{% cycle section %}. Disclosure of Patent Information{% if form.instance|to_class_name == "ThirdPartyIprDicslosure" %}, if known{% endif %}
<small>i.e., patents or patent applications required to be disclosed by Section 5 of RFC8179</small></h2>
{% if form.patent_info %}
{% if form.patent_number %}
<p>
A. For granted patents or published pending patent applications,
please provide the following information:
</p>
{% bootstrap_field form.patent_number layout='horizontal' %}
{% bootstrap_field form.patent_inventor layout='horizontal' %}
{% bootstrap_field form.patent_title layout='horizontal' %}
{% bootstrap_field form.patent_date layout='horizontal' %}
{% bootstrap_field form.patent_notes layout='horizontal' %}
<p>B. Does your disclosure relate to an unpublished pending patent application?</p>
{% bootstrap_field form.has_patent_pending layout='horizontal' %}
{% elif form.patent_info %}
<p>
A. For granted patents or published pending patent applications,
please provide the following information: