11import datetime
22import email
33
4- from django . utils . safestring import mark_safe
4+
55from django import forms
6+ from django .core .validators import RegexValidator
7+ from django .utils .safestring import mark_safe
8+
9+ import debug # pyflakes:ignore
610
711from ietf .group .models import Group
812from ietf .doc .fields import SearchableDocAliasField
1317 IprLicenseTypeName , IprDisclosureStateName )
1418from ietf .message .models import Message
1519from ietf .utils .fields import DatepickerDateField
20+ from ietf .utils .text import dict_to_text
1621
1722# ----------------------------------------------------------------
1823# Globals
@@ -101,6 +106,31 @@ class Meta:
101106 }
102107 help_texts = { 'sections' : 'Sections' }
103108
109+ validate_patent_number = RegexValidator (
110+ regex = "^(([A-Z][A-Z]\d{6,12}|[A-Z][A-Z]\d{4}(\w{1,2}\d{5,7})?)[, ]*)+$" ,
111+ message = "Please enter one or more patent publication or application numbers as country code and serial number, e.g.: WO2017123456." )
112+
113+ def validate_string (s , letter_min , digit_min , space_min , message ):
114+ letter_count = 0
115+ space_count = 0
116+ digit_count = 0
117+ s = s .strip ()
118+ for c in s :
119+ if c .isalpha ():
120+ letter_count += 1
121+ if c .isspace ():
122+ space_count += 1
123+ if not (letter_count >= letter_min and digit_count >= digit_min and space_count >= space_min ):
124+ raise forms .ValidationError (message )
125+
126+ def validate_name (name ):
127+ return validate_string (name , letter_min = 3 , space_min = 1 , digit_min = 0 ,
128+ message = "This doesn't look like a name. Please enter the actual inventor name." )
129+
130+ def validate_title (title ):
131+ return validate_string (title , letter_min = 15 , space_min = 2 , digit_min = 0 ,
132+ message = "This doesn't look like a patent title. Please enter the actual patent title." )
133+
104134class GenericDisclosureForm (forms .Form ):
105135 """Custom ModelForm-like form to use for new Generic or NonDocSpecific Iprs.
106136 If patent_info is submitted create a NonDocSpecificIprDisclosure object
@@ -114,7 +144,14 @@ class GenericDisclosureForm(forms.Form):
114144 holder_contact_info = forms .CharField (label = "Other Info (address, phone, etc.)" , max_length = 255 ,widget = forms .Textarea ,required = False , strip = False )
115145 submitter_name = forms .CharField (max_length = 255 ,required = False )
116146 submitter_email = forms .EmailField (required = False )
117- 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 )
147+ #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)
148+ patent_number = forms .CharField (max_length = 127 , required = False , validators = [ validate_patent_number ],
149+ help_text = "Patent publication or application number (2-letter country code followed by serial number)" )
150+ patent_inventor = forms .CharField (max_length = 63 , required = False , validators = [ validate_name ], help_text = "Inventor name" )
151+ patent_title = forms .CharField (max_length = 63 , required = False , validators = [ validate_title ], help_text = "Title of invention" )
152+ patent_date = forms .DateField (required = False , help_text = "Date granted or applied for" )
153+ patent_notes = forms .CharField (max_length = 127 , required = False , widget = forms .Textarea )
154+
118155 has_patent_pending = forms .BooleanField (required = False )
119156 statement = forms .CharField (max_length = 255 ,widget = forms .Textarea ,required = False , strip = False )
120157 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,14 +169,31 @@ def clean(self):
132169 if not self .cleaned_data .get ('same_as_ii_above' ):
133170 if not ( self .cleaned_data .get ('submitter_name' ) and self .cleaned_data .get ('submitter_email' ) ):
134171 raise forms .ValidationError ('Submitter information must be provided in section VII' )
135-
172+
173+ patent_fields = [ 'patent_' + k for k in ['number' , 'inventor' , 'title' , 'date' , ] ]
174+ patent_values = [ cleaned_data .get (k ) for k in patent_fields ]
175+ if any (patent_values ) and not all (patent_values ):
176+ for k in patent_fields :
177+ if not cleaned_data .get (k ):
178+ self .add_error (k , "This field is required if you are filing a patent-specific disclosure." )
179+ raise forms .ValidationError ("A generic IPR disclosure cannot have any patent-specific information, "
180+ "but a patent-specific disclosure must provide full patent information." )
181+
182+ patent_fields += ['patent_notes' ]
183+ patent_info = dict ([ (k .replace ('patent_' ,'' ).capitalize (), cleaned_data .get (k )) for k in patent_fields if cleaned_data .get (k ) ] )
184+ cleaned_data ['patent_info' ] = dict_to_text (patent_info ).strip ()
185+ cleaned_data ['patent_fields' ] = patent_fields
186+
136187 return cleaned_data
137188
138189 def save (self , * args , ** kwargs ):
139190 nargs = self .cleaned_data .copy ()
140191 same_as_ii_above = nargs .get ('same_as_ii_above' )
141192 del nargs ['same_as_ii_above' ]
142193
194+ for k in self .cleaned_data ['patent_fields' ] + ['patent_fields' ,]:
195+ del nargs [k ]
196+
143197 if self .cleaned_data .get ('patent_info' ):
144198 obj = NonDocSpecificIprDisclosure (** nargs )
145199 else :
@@ -160,13 +214,20 @@ class IprDisclosureFormBase(forms.ModelForm):
160214 """Base form for Holder and ThirdParty disclosures"""
161215 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." ))
162216 same_as_ii_above = forms .BooleanField (required = False )
217+ patent_number = forms .CharField (max_length = 127 , required = True , validators = [ validate_patent_number ],
218+ help_text = "Patent publication or application number (2-letter country code followed by serial number)" )
219+ patent_inventor = forms .CharField (max_length = 63 , required = True , validators = [ validate_name ], help_text = "Inventor name" )
220+ patent_title = forms .CharField (max_length = 63 , required = True , validators = [ validate_title ], help_text = "Title of invention" )
221+ patent_date = forms .DateField (required = True , help_text = "Date granted or applied for" )
222+ patent_notes = forms .CharField (max_length = 127 , required = False , widget = forms .Textarea )
163223
164224 def __init__ (self ,* args ,** kwargs ):
165225 super (IprDisclosureFormBase , self ).__init__ (* args ,** kwargs )
166226 self .fields ['submitter_name' ].required = False
167227 self .fields ['submitter_email' ].required = False
168228 self .fields ['compliant' ].initial = True
169229 self .fields ['compliant' ].label = "This disclosure complies with RFC 3979"
230+ patent_fields = [ 'patent_' + k for k in ['number' , 'inventor' , 'title' , 'date' , ] ]
170231 if "ietfer_name" in self .fields :
171232 self .fields ["ietfer_name" ].label = "Name"
172233 if "ietfer_contact_email" in self .fields :
@@ -175,7 +236,10 @@ def __init__(self,*args,**kwargs):
175236 self .fields ["ietfer_contact_info" ].label = "Other info"
176237 self .fields ["ietfer_contact_info" ].help_text = "Address, phone, etc."
177238 if "patent_info" in self .fields :
178- 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"
239+ self .fields ['patent_info' ].required = False
240+ else :
241+ for f in patent_fields :
242+ del self .fields [f ]
179243 if "licensing" in self .fields :
180244 self .fields ["licensing_comments" ].label = "Licensing information, comments, notes, or URL for further information"
181245 if "submitter_claims_all_terms_disclosed" in self .fields :
@@ -187,7 +251,7 @@ class Meta:
187251 """This will be overridden"""
188252 model = IprDisclosureBase
189253 fields = '__all__'
190-
254+
191255 def clean (self ):
192256 super (IprDisclosureFormBase , self ).clean ()
193257 cleaned_data = self .cleaned_data
@@ -198,6 +262,12 @@ def clean(self):
198262 if not ( self .cleaned_data .get ('submitter_name' ) and self .cleaned_data .get ('submitter_email' ) ):
199263 raise forms .ValidationError ('Submitter information must be provided in section VII' )
200264
265+ patent_fields = [ 'patent_' + k for k in ['number' , 'inventor' , 'title' , 'date' , 'notes' ] ]
266+
267+ patent_info = dict ([ (k .replace ('patent_' ,'' ).capitalize (), cleaned_data .get (k )) for k in patent_fields if cleaned_data .get (k ) ] )
268+ cleaned_data ['patent_info' ] = dict_to_text (patent_info ).strip ()
269+ cleaned_data ['patent_fields' ] = patent_fields
270+
201271 return cleaned_data
202272
203273class HolderIprDisclosureForm (IprDisclosureFormBase ):
@@ -220,8 +290,7 @@ def __init__(self, *args, **kwargs):
220290 self .fields ['licensing' ].queryset = IprLicenseTypeName .objects .exclude (slug = 'none-selected' )
221291
222292 def clean (self ):
223- super (HolderIprDisclosureForm , self ).clean ()
224- cleaned_data = self .cleaned_data
293+ cleaned_data = super (HolderIprDisclosureForm , self ).clean ()
225294 if not self .data .get ('iprdocrel_set-0-document' ) and not cleaned_data .get ('other_designations' ):
226295 raise forms .ValidationError ('You need to specify a contribution in Section IV' )
227296 return cleaned_data
@@ -270,8 +339,7 @@ class Meta:
270339 exclude = [ 'by' ,'docs' ,'state' ,'rel' ]
271340
272341 def clean (self ):
273- super (ThirdPartyIprDisclosureForm , self ).clean ()
274- cleaned_data = self .cleaned_data
342+ cleaned_data = super (ThirdPartyIprDisclosureForm , self ).clean ()
275343 if not self .data .get ('iprdocrel_set-0-document' ) and not cleaned_data .get ('other_designations' ):
276344 raise forms .ValidationError ('You need to specify a contribution in Section III' )
277345 return cleaned_data
0 commit comments